ScaleObject and TransformObject behaviour

Hello,

I am encountering problems with scaling an object at different factors in x, y, z. Here is what I am trying to do:


# FuselageOMLSurf is the result of a complex, parametric script. I am trying to scale it by 5 in the y direction and 10 in the z direction here (though in my actual script Scaling is the argument of the function) 

Scaling = [1, 5, 10] 

FuselageOMLSurf = rs.ScaleObject(FuselageOMLSurf, (0,0,0), Scaling)

When I first run it, this produces the expected result: FuselageOMLSurf stretches by a factor of 5 in the y direction and 10 in the z direction. However, after a few runs it starts mixing up the axes and will stretch it in the z direction by a factor of 5 and y by a factor of 10, etc.

My suspicion is that this has something to do with the line “Scaling is based on the active construction plane.” is the ScaleObject header, but I don’t know how to ensure that it all happens in world coordinates.

I don’t use construction planes - at least, not knowingly! I have also tried:


Scaling = [1, 5, 10] 

xform = rs.XformScale(Scaling)

FuselageOMLSurf = rs.TransformObjects(FuselageOMLSurf, xform)
   

This seems to have the same (inconsistent) result.

In either case, if I open a new document, the first time I run the script the result is as expected. But if I delete the resulting objects and re-run the script, it starts getting hit and miss.

Any help would be much appreciated.

1 Like

Yes, ScaleObject(s) looks like it only uses the active construction plane, which seems odd, as I think it definitely should have an option to use World coordinate system. (@stevebaer, @dale please add this as a request for both Python and vb Rhinoscript) Depending on the view that is active when you pick the objects, you will get different results. OTOH, XformScale should scale in World coordinates as far as I can tell. If you do not input a “scale around” point in XformScale, it will use the world origin. It should therefore be consistent. I will try to check that.

As a workaround, you have two choices - either set the active view (and thus its CPlane) with rs.CurrentView() before proceeding, or, as you are using Python, go into RhinoCommon and use the Rhino.Geometry.Transform.Scale() method which allows you to specify a plane.

Following is a definiton ScaleObjectsEx() which allows you to input a plane for the transform.
–Mitch

import rhinoscriptsyntax as rs
import scriptcontext as sc
import Rhino, System

def ScaleObjectsEx(object_ids, plane, scale, copy=False):
    """Scales one or more objects. Can be used to perform a uniform or non-
    uniform scale transformation. Scaling is based on supplied plane argument.
    Parameters:
      object_ids: Identifiers of objects to scale
      plane: the plane for the transformation
      scale: three numbers that identify the X, Y, and Z axis scale factors to apply
      copy[opt] = copy the objects
    Returns:
      List of identifiers of the scaled objects if successful
      None on error
    """
    scale = rs.coerce3dpoint(scale, True)
    if scale:
        xform = Rhino.Geometry.Transform.Scale(plane, scale.X, scale.Y, scale.Z)
        rc = []
        for object_id in object_ids:
            object_id = rs.coerceguid(object_id, True)
            id = sc.doc.Objects.Transform(object_id, xform, not copy)
            if id!=System.Guid.Empty: rc.append(id)
        if rc: sc.doc.Views.Redraw()
    return rc
    
def TestScaleObjectsEx():    
    objs=rs.GetObjects("Select objects to scale")
    plane=rs.WorldXYPlane()
    sf=[1,5,10]
    ScaleObjectsEx(objs,plane,sf,False)
TestScaleObjectsEx()
import rhinoscriptsyntax as rs
objs=rs.GetObjects("Select objects to scale")
Scaling = [1, 5, 10]
xform = rs.XformScale(Scaling)
tObjs = rs.TransformObjects(objs,xform,False)

In checking I find that this consistently uses the World XY plane/origin, no matter what viewport is active when I pick the objects. Can you post an example that is inconsistent with a recipe on how to make it misbehave? --Mitch

1 Like

Thank you very much @Helvetosaur, that was very useful.

First, reading your comments on XformScale made me go back to the code and have a much more thorough and careful test - you must be right, it must indeed use World coordinates, as I have not managed to get it to fail again - it probably never did, more to do with me working at 1am, rather than anything else, I must have entered the parameters incorrectly.

As for the Transform.Scale method, I implemented your suggestion like this:


P = rs.PlaneFromFrame((0,0,0),(1,0,0),(0,1,0))
TransfMatrix = Rhino.Geometry.Transform.Scale(P, Scaling[0], Scaling[1], Scaling[2])

This works too! I’m not sure I understand what exactly is happening here (how does a plane define a 3d coordinate system? How does Rhino know which axis should be which?), but that is purely academic, the main thing is that it is working.

Thanks again.

A plane in Rhino is defined by 4 things - a 3d point that is the origin, plus 3 axis vectors. These are always in the order origin, x, y, z, so there is no confusion as to which is which. --Mitch

Ah OK, got it. So basically it assumes a right-handed system and then once you’ve specified O, X and Y (the only things needed by, e.g., rs.PlaneFromFrame), then Z results from that and thus the system is unequivocally defined. Thanks Mitch.

I’ve created an issue for this.

http://mcneel.myjetbrains.com/youtrack/issue/RH-28801