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()