2 and 3 point orient

A while back in '23, I was talking with Pascal about an issue I had with orienting point cloud objects that were grouped together and were both visible and hidden in that group. I would try to orient the visible to something else using Orient objects: 3 points to do this. While the tools works great, but was not helpful in some ways. What it would do is after you select a visible object in that group and apply the transform, after you selected the first 3 points (2 points do the same thing BTW) and you start to select the next 3 points, those hidden grouped objects all of a sudden get displayed and don’t turn off until you select that 3rd point. That’s why in some ways the transform isn’t very helpful because the hidden objects obscure where you might want to orient that original visible object’s new location. So Pascal wrote me a Python script. It works great! I made a macro of it and now it’s on my toolbar. I’ll provide the script below but I was wonder if you could just make the transform work that way without having to use a script each time to execute the transform? The current way it works if you have primitives or solids, those hidden objects show up as wire frame which is great but not helpful for point clouds. Also, with the current transform, it would be helpful to be able to just display or not display those wireframes when using the transform, maybe in the Tools/Options/Advance panel. Anyway, here is the script that Pascal gave me. It would be nice to just add this to Rhino 8.

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

def test():
    color = Rhino.ApplicationSettings.AppearanceSettings.FeedbackColor
    lColor = Rhino.ApplicationSettings.AppearanceSettings.LockedObjectColor
    
    layers = sc.doc.Layers
    
    ids = rs.GetObjects("Select objects to orient", preselect=True, group = True)
    if not ids:
        return
        
    geos = [rs.coercegeometry(id) for id in ids]
    objs = [rs.coercerhinoobject(id) for id in ids]

    #print("pass")
    
    def GetPointDynamicDrawFunc1( sender, args ):
        """
        if there are points in PtsA draw a dot at each
        if two points draw a line between
        draw a line from the last point to the current point
        """
        
        if len(ptsA) > 0:
            if len(ptsA) == 2:
                args.Display.DrawLine(ptsA[0], ptsA[1],color)
            for n in range(len(ptsA)):
                args.Display.DrawDot(ptsA[n] , str(n+1))

            args.Display.DrawLine(ptsA[0], args.CurrentPoint, color)
            args.Display.DrawDot(args.CurrentPoint , str(len(ptsA)+1))
            
    def GetPointDynamicDrawFunc2( sender, args ):
        """
        if there are points in PtsB draw a dot at each
        if two points draw a line between
        draw a line from the last point to the current point
        """
        args.Display.DrawDot(args.CurrentPoint, str(len(ptsB)+1))
        #Draw all ptsA
        for n in range(len(ptsA)):
            args.Display.DrawDot(ptsA[n] , str(n+1))
            
        if len(ptsB) > 0:
            for q in range(len(ptsB)):
                args.Display.DrawDot(ptsB[q] ,str(q+1))
            if len(ptsB) == 2:
                args.Display.DrawLine(ptsB[0], ptsB[1],color)
            args.Display.DrawLine(ptsB[0], args.CurrentPoint, color)
            
        """
        if pt count == 2:
            make a plane with the current point
            xform = PlaneToPane(basePlane, tempPlane)
         preview visible objects as xformed
         
        """
        
        scale = None

        if len(ptsB) ==0:
            tempPlane = Rhino.Geometry.Plane(basePlane)
            tempPlane.Origin = args.CurrentPoint
            
        elif len(ptsB) ==1:
            vecDir = ptsB[0]-args.CurrentPoint
            
            """
            cook up some kind of plane that makes sense given the first
            point and the current one
                         
            perp plane to vector first point-current point  
            with origin at first point
            perp plane X projected to cplane 
            
            plane = origin, direction vector, projected X vector
            """
            tempCPlane = Rhino.Geometry.Plane(rs.ViewCPlane())
            tempCPlane.Origin = ptsB[0]
            
            perpPlane = Rhino.Geometry.Plane(ptsB[0], vecDir)
            tempPt = perpPlane.Origin + perpPlane.XAxis
            
            projPt = tempCPlane.ClosestPoint(tempPt)
            tempPlane = Rhino.Geometry.Plane(ptsB[0],args.CurrentPoint, projPt)

            scale = (ptsB[0].DistanceTo(args.CurrentPoint))/(ptsA[0].DistanceTo(ptsA[1]))

        elif len(ptsB) ==2:
            scale = (ptsB[0].DistanceTo(ptsB[1]))/(ptsA[0].DistanceTo(ptsA[1]))
            tempPlane = Rhino.Geometry.Plane(ptsB[0], ptsB[1], args.CurrentPoint)

        if tempPlane.IsValid:
            xform = Rhino.Geometry.Transform.PlaneToPlane(basePlane, tempPlane)
            if len(ptsB)  != 0:
                if oScale:
                    
                    xformScale = Rhino.Geometry.Transform.Scale(tempPlane, scale, scale, scale)
                    xform = xformScale *xform
                    
        
        for obj in objs:
            if not obj.IsHidden:
                idx = obj.Attributes.LayerIndex
                if layers[idx].IsVisible:
                    args.Display.DrawObject(obj, xform)
                   
                    
    ptsA = []
    while len(ptsA) < 3:
        
        oScale = False
        if "ORIENT_SCALE" in sc.sticky:
            oScale = sc.sticky["ORIENT_SCALE"]
        oCopy = False
        if "ORIENT_COPY" in sc.sticky:
            oCopy = sc.sticky["ORIENT_COPY"]
            
        gp = Rhino.Input.Custom.GetPoint()
        
        opCopy = Rhino.Input.Custom.OptionToggle(oCopy, "No", "Yes")
        gp.AddOptionToggle("Copy", opCopy)
        
        opScale = Rhino.Input.Custom.OptionToggle(oScale, "No", "Yes")
        gp.AddOptionToggle("Scale", opScale)
        
        gp.DynamicDraw += GetPointDynamicDrawFunc1
        
        ptRC = gp.Get()
        if( gp.CommandResult() != Rhino.Commands.Result.Success ):
            return
        if ptRC == Rhino.Input.GetResult.Point:
            ptsA.append(gp.Point())
            continue
        elif ptRC == Rhino.Input.GetResult.Option:
            oCopy = opCopy.CurrentValue
            oScale = opScale.CurrentValue
            sc.sticky["ORIENT_COPY"] = oCopy
            sc.sticky["ORIENT_SCALE"] = oScale
            continue
    if len(ptsA) != 3:
        return
    basePlane = Rhino.Geometry.Plane(ptsA[0], ptsA[1], ptsA[2])
    if not basePlane.IsValid:
        return

    ptsB = []
    
    
    while len(ptsB) < 3:

        oScale = False
        if "ORIENT_SCALE" in sc.sticky:
            oScale = sc.sticky["ORIENT_SCALE"]
        oCopy = False
        if "ORIENT_COPY" in sc.sticky:
            oCopy = sc.sticky["ORIENT_COPY"]
            
        gp = Rhino.Input.Custom.GetPoint()
        
        opCopy = Rhino.Input.Custom.OptionToggle(oCopy, "No", "Yes")
        gp.AddOptionToggle("Copy", opCopy)
        
        opScale = Rhino.Input.Custom.OptionToggle(oScale, "No", "Yes")
        gp.AddOptionToggle("Scale", opScale)
        
        gp.DynamicDraw += GetPointDynamicDrawFunc2
        
        ptRC = gp.Get()
        if( gp.CommandResult() != Rhino.Commands.Result.Success ):
            return
        if ptRC == Rhino.Input.GetResult.Point:
            ptsB.append(gp.Point())
            continue
        elif ptRC == Rhino.Input.GetResult.Option:
            oCopy = opCopy.CurrentValue
            oScale = opScale.CurrentValue
            sc.sticky["ORIENT_COPY"] = oCopy
            sc.sticky["ORIENT_SCALE"] = oScale
            continue
    if len(ptsB) != 3:
        return
        
    targPlane = Rhino.Geometry.Plane(ptsB[0], ptsB[1], ptsB[2])

    if not targPlane.IsValid:
        return
    xform = Rhino.Geometry.Transform.PlaneToPlane(basePlane, targPlane)
    if oScale:
        factor = (ptsB[1].DistanceTo(ptsB[2]))/(ptsA[1].DistanceTo(ptsA[2]))
        scaleXform = Rhino.Geometry.Transform.Scale(targPlane, factor, factor, factor)
        xform = scaleXform * xform
        
    """
    need to handle grouping
    
    makle a dict of groups and all relevant ids in each
    grp:[ids]
    for each group make a new group
    keep a list of (old grp,newgrp)
    duplicate each object 
    check the dict for its presence in each group
    if found, find that grp in the list and assign to the new associated group
    
    """

    if oCopy:
        groups = {}
        for id in ids:
            temp = rs.ObjectGroups(id)
            for item in temp:
                if item in groups:
                    tempList = groups[item]
                    tempList.append(id)
                    groups[item] = tempList
                else:
                    groups.Add(item,[id])
        grpList = groups.keys()
        pairList = {}
        for grp in grpList:
            pairList.Add(grp,rs.AddGroup())
        
        for id in ids:
            copyId = sc.doc.Objects.Duplicate(id)
            temp = rs.ObjectGroups(id)
            if temp:
                for item in temp:
                    if item in pairList:
                        rs.AddObjectToGroup(copyId, pairList[item])
                        rs.RemoveObjectFromGroup(copyId, item)
            
            sc.doc.Objects.Transform(copyId, xform, True)
       
    else:
        for id in ids:
            sc.doc.Objects.Transform(id, xform, True)


    sc.doc.Views.Redraw()
test()

Assigned to Scripting category

I was hoping it would be on the wish list instead of scripting, just saying. I included the script for reference only.

@pascal - can you review?

Hi, has anyone looked into this any further? Thanks

Hi @CreativeType,

This wish has been asked for before.

https://mcneel.myjetbrains.com/youtrack/issue/RH-74346

I’ll add your comment to the lssue.

– Dale

Thanks Dale,

I see you commented on my move objects turning on when grouping is invoked and those objects are turned off. looks like this was asked more than a year ago. Someday it will happen :slight_smile: