POSSIBLE BUG: Line Points Incorrect After Rotating

Python: This might be a possible bug. If I create a line object, rotate it, now the To point does not change according to the rotation. Why? Is this a bug? Try this code…

l = rs.AddLine((0,0,0), (0, 100, 0))
objl = rs.coerceline(l)

print objl.To

rs.RotateObject(l, objl.From, -90)

print objl.To

If this is a bug, is there a band-aid for it to get the new point? The line would not necessarily be on any of the axis.

This works:

l = rs.AddLine((0,0,0), (0, 100, 0))
objl = rs.coerceline(l)

print objl.To

l2 = rs.RotateObject(l, objl.From, -90)

objl2 = rs.coerceline(l2)

print objl2.To
1 Like

@siemen - Ok great thanks! I didn’t realize if you move, rotate, or scale an object it actually returns a new GUID for the new object.

Hi @Mike24

Are you aware you can transform the object without copying:

RotateObject(object_id, center_point, rotation_angle, axis=None, copy=False) 

@Willem - Yes I’m aware. That’s the default value copy=False. That is what I was doing but what happens is if you use the same object, it gets rotated but the point on the line that is opposite of the center of rotation does not change. In my case it was the To point. The To point location still was the same as before the rotation. Odd behavior in my opinion. So what you have to do is grab the object that is passed back from the RotateObject method and use that. That object will have the correct (new) point (From and To) locations. For me this is a bit of a pain having to do it this way because I will utilize the From and To points in a line quite often. I’m a noob so this is the first time I have ran into this issue.

Hi @Mike24
I’ll try t break this down to help you wrap you head around this:

l = rs.AddLine((0,0,0), (0, 100, 0))
# l is now an object-id representing a rhino object
objl = rs.coerceline(l)
# objl is a copy of the geometry of l ( a Rhino line)
# there is no reference back to the rhino object l

print objl.To  # clear: print the To point

# here you rotate the rhino object l, but this does not affect objl in any way
rs.RotateObject(l, objl.From, -90, copy=False)

print objl.To # clear: print the same To point

# What you would do to get the expected result is:
objl_2 = rs.coerceline(l)
print objl_2.To

Does this make sense?

With your background in programming I think you will benefit a lot from checking out the rhinoscriptsyntax module.

eg rs.RotateObject():

def RotateObject(object_id, center_point, rotation_angle, axis=None, copy=False):
    """Rotates a single object
    Parameters:
      object_id (guid): The identifier of an object to rotate
      center_point (point): the center of rotation
      rotation_angle (number): in degrees
      axis (plane, optional): axis of rotation, If omitted, the Z axis of the active
        construction plane is used as the rotation axis
      copy (bool, optional): copy the object
    Returns:
      guid: Identifier of the rotated object if successful
      None: on error
    Example:
      import rhinoscriptsyntax as rs
      obj = rs.GetObject("Select object to rotate")
      if obj:
          point = rs.GetPoint("Center point of rotation")
          if point: rs.RotateObject(obj, point, 45.0, None, copy=True)
    See Also:
      RotateObjects
    """
    rc = RotateObjects(object_id, center_point, rotation_angle, axis, copy)
    if rc: return rc[0]
    return scriptcontext.errorhandler()

RotateObjects():

def RotateObjects( object_ids, center_point, rotation_angle, axis=None, copy=False):
    """Rotates multiple objects
    Parameters:
      object_ids ([guid, ...]): Identifiers of objects to rotate
      center_point (point): the center of rotation
      rotation_angle (number): in degrees
      axis (plane, optional): axis of rotation, If omitted, the Z axis of the active
        construction plane is used as the rotation axis
      copy (bool, optional): copy the object
    Returns:
      list(guid, ...): identifiers of the rotated objects if successful
    Example:
      import rhinoscriptsyntax as rs
      objs = rs.GetObjects("Select objects to rotate")
      if objs:
          point = rs.GetPoint("Center point of rotation")
          if point:
              rs.RotateObjects( objs, point, 45.0, None, True )
    See Also:
      RotateObject
    """
    center_point = rhutil.coerce3dpoint(center_point, True)
    if not axis:
        axis = scriptcontext.doc.Views.ActiveView.ActiveViewport.ConstructionPlane().Normal
    axis = rhutil.coerce3dvector(axis, True)
    rotation_angle = Rhino.RhinoMath.ToRadians(rotation_angle)
    xf = Rhino.Geometry.Transform.Rotation(rotation_angle, axis, center_point)
    rc = TransformObjects(object_ids, xf, copy)
    return rc

TransformObjects():

def TransformObjects(object_ids, matrix, copy=False):
    """Moves, scales, or rotates a list of objects given a 4x4 transformation
    matrix. The matrix acts on the left.
    Parameters:
      object_ids [(guid, ...}): List of object identifiers.
      matrix (transform): The transformation matrix (4x4 array of numbers).
      copy (bool, optional): Copy the objects
    Returns:
      list(guid, ...): ids identifying the newly transformed objects
    Example:
      import rhinoscriptsyntax as rs
      # Translate (move) objects by (10,10,0)
      xform = rs.XformTranslation([10,10,0])
      objs = rs.GetObjects("Select objects to translate")
      if objs: rs.TransformObjects(objs, xform)
    See Also:
      TransformObject
    """
    xform = rhutil.coercexform(matrix, True)
    id = rhutil.coerceguid(object_ids, False)
    if id: object_ids = [id]
    elif isinstance(object_ids, Rhino.Geometry.GeometryBase): object_ids = [object_ids]
    elif type(object_ids) in __allowed_transform_types: object_ids = [object_ids]
    rc = []
    for object_id in object_ids:
        id = System.Guid.Empty
        old_id = rhutil.coerceguid(object_id, False)
        if old_id:
            id = scriptcontext.doc.Objects.Transform(old_id, xform, not copy)
        elif isinstance(object_id, Rhino.Geometry.GeometryBase):
            if copy: object_id = object_id.Duplicate()
            if not object_id.Transform(xform): raise Exception("Cannot apply transform to geometry.")
            id = scriptcontext.doc.Objects.Add(object_id)
        else:
            type_of_id = type(object_id)
            if type_of_id in __allowed_transform_types:
                if copy: object_id = System.ICloneable.Clone(object_id)
                if object_id.Transform(xform) == False: #some of the Transform methods return bools, others have no return
                    raise Exception("Cannot apply transform to geometry.")
                ot = scriptcontext.doc.Objects
                fs = [ot.AddPoint,ot.AddLine,ot.AddRectangle,ot.AddCircle,ot.AddEllipse,ot.AddArc,ot.AddPolyline,ot.AddBox,ot.AddSphere]
                t_index = __allowed_transform_types.index(type_of_id)
                id = fs[t_index](object_id)
            else:
                raise Exception("The {0} cannot be tranformed. A Guid or geometry types are expected.".format(type_of_id))
        if id!=System.Guid.Empty: rc.append(id)
    if rc: scriptcontext.doc.Views.Redraw()
    return rc

As you can see these are wrapper functions with quite some coercion and input sanitation.
However the core functionality is all Rhino. and the scriptcontext module.
That module ‘taps into’ the current document to read and write all objects, layers, properties etc etc.

Does this all make sense?
HTH
-Willem

@Willem - Yes, I already understood everything you said. My point is when you rotate an object, the To property within that object does NOT change when it should change.

If I run the following code…

l = rs.AddLine((0,0,0), (0, 100, 0))
objl = rs.coerceline(l)
print objl.To  # clear: print the To point

…the output is:

0,100,0

…which is correct.

Then if I run the following code…

rs.RotateObject(l, objl.From, -90, copy=False)
print objl.To # clear: print the same To point

…the output is…

0,100,0

…which is incorrect. The To point within the l object has not changed after rotation of the l object. The location of the To point after rotation should be…

100,0,0

After seeing siemen’s post I realized that you MUST get the new object that is created from the RotateObject method and use that as the new object after you, coerce it of course. This new object contains the correct To point.

With this understood, if I run the following code…

l = rs.AddLine((0,0,0), (0, 100, 0))
objl = rs.coerceline(l)
print objl.To
l2 = rs.RotateObject(l, objl.From, -90, copy=False)
objl2 = rs.coerceline(l2)
print objl2.To

…the result I get back is…

0,100,0
100,0,0

…which is correct. I further discovered that if you move or scale an object, the same thing happens.
So if you use the From and To point, make sure you grab the new GUID and use that object.

Hi @Mike24

We might be miscommunicating here but I would rephrase the above:

If you move or scale a RhinoObject, with a rhinoscriptsyntax method, a copy of the geometry of said RhinoObject is transformed. That transformed geometry copy is then replacing the current geometry of the RhinoObject.

So if you use the From and To point, make sure you coerce the geometry of the new or copied GUID object.

Does that make sense and clarify what I tried to communicate before?

-Willem

Yes…I think we are on the same page now. Thanks for clarifying.

1 Like