... and regarding the "any geometry", it does happen one do want to Move just a point or line or curve and so on but like to have a the original left in its orignal place.
For all Moves we want the Moved stuff to be copies with new coordinates AND rememberd original position AND visualized with its own Move-icon in the tree.
This is what differs from todays setup.
Discussion on placement
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
Be nice to others! Read the FreeCAD code of conduct!
- Pauvres_honteux
- Posts: 728
- Joined: Sun Feb 16, 2014 12:05 am
- Location: Far side of the moon
Re: Discussion on placement
edwilliams16 wrote: ↑Tue Jun 15, 2021 8:06 pmThe macro now handles ShapeTypes: Solid, Shell, Compound and CompSolid, computing the CG of composite objects.Code: Select all
debug = True '''Usage: This macro will adjust the placement of a shape to place a selected feature at the origin. One vertex: - to origin One line segment - midpoint to origin One circle - center to origin One arc of circle - center to origin One face - CG of face to origin Three vertices - center of circle through vertices to origin Shape - CG of shape to origin If the shape is a BaseFeature inside a body, the base feature's placement in the body is adjusted. ''' def circumcenter(A, B, C): '''Return the circumcenter of three 3D vertices''' a2 = (B - C).Length**2 b2 = (C - A).Length**2 c2 = (A - B).Length**2 if a2 * b2 * c2 == 0.0: print('Three vertices must be distinct') return alpha = a2 * (b2 + c2 - a2) beta = b2 * (c2 + a2 - b2) gamma = c2 * (a2 + b2 - c2) return (alpha*A + beta * B + gamma * C)/(alpha + beta + gamma) def centerofmass(subshapes): mass =0.0 moment = App.Vector(0, 0, 0) for sh in subshapes: mass += sh.Volume moment += sh.Volume * sh.CenterOfMass return moment/mass selt = Gui.Selection.getSelectionEx() if len(selt) != 1: print('Select one vertex, edge or object\nor three vertices for center of circle') shift = App.Vector(0, 0, 0) # bail and do nothing else: sel = selt[0] if sel.HasSubObjects: vv = sel.SubObjects if len(vv) ==3 and vv[0].ShapeType == 'Vertex' and vv[1].ShapeType == 'Vertex' and vv[2].ShapeType == 'Vertex': shift = circumcenter(vv[0].Point, vv[1].Point, vv[2].Point) if debug: print('Three Vertices') elif len(vv) == 1: selected = sel.SubObjects[0] if selected.ShapeType == 'Vertex': shift = selected.Point if debug: print('One vertex') elif selected.ShapeType == 'Edge': if selected.Curve.TypeId == 'Part::GeomCircle': #circles and arcs shift = selected.Curve.Center if debug: print('One arc') elif selected.Curve.TypeId == 'Part::GeomLine': shift = (selected.valueAt(selected.FirstParameter) + selected.valueAt(selected.LastParameter))/2 # mid-point if debug: print('One line') else: print(f'edge is {selected.Curve.TypeId} not handled') shift = App.Vector(0, 0, 0) # bail and do nothing elif selected.ShapeType == 'Face': shift = selected.CenterOfMass #center of face if debug: print('One face') else: print(f'ShapeType {selected.ShapeType} not handled') shift = App.Vector(0, 0, 0) # bail and do nothing else: print('Wrong number of Selections') shift = App.Vector(0, 0, 0) # bail and do nothing else: if debug: print(f'ObjectName is {sel.ObjectName}') if hasattr(sel.Object, 'Shape'): if debug: print('One solid') shape = sel.Object.Shape if shape.ShapeType == 'Solid' or shape.ShapeType == 'Shell': shift = shape.CenterOfMass elif shape.ShapeType == 'Compound' or shape.ShapeType == 'CompSolid': shift = centerofmass(shape.SubShapes) #more cases? else: print(f'Object ShapeType {sel.Object.Shape.ShapeType} not handled') shift = App.Vector(0, 0, 0) # bail and do nothing else: print(f'{sel.ObjectName} is not a shape') shift = App.Vector(0, 0, 0) # bail and do nothing if debug: print(f'Shift = {shift}') base = App.ActiveDocument.getObject(sel.ObjectName).Placement base.move(-shift) App.ActiveDocument.recompute()
It will use a single vertex, the mid-point of a line, the center of a circle or arc or the CG of a face as the reference point to move to the origin.
It will do nothing with B-splines and Wires.
There's no Undo - but the restoring shift is printed to the report view. Undo would be better if I figured how to do it.
Maybe approaching prime-time with user testing? Should I link to new thread in Python Scripting and Macros?
I made a few additions to the above code, with it you can select a second item and have the first item move to it. Good if you want to line up a vertex on one object with a vertex on another...
Code: Select all
'''Usage:
This macro will adjust the placement of a shape to place a selected feature at the origin.
One vertex: - to origin
One line segment - midpoint to origin
One circle - center to origin
One arc of circle - center to origin
One face - CG of face to origin
Three vertices - center of circle through vertices to origin
Shape - CG of shape to origin
If the shape is a BaseFeature inside a body, the base feature's placement in the body is adjusted.
'''
from PySide import QtCore, QtGui
def err(msg):
# Create a simple dialog QMessageBox
# The first argument indicates the icon used: one of QtGui.QMessageBox.{NoIcon, Information, Warning, Critical, Question}
print('Error: '+ msg)
diag = QtGui.QMessageBox(QtGui.QMessageBox.Warning, 'Error', msg)
diag.setWindowModality(QtCore.Qt.ApplicationModal)
diag.exec_()
def debug(msg):
print(msg)
def calcVectorFromOrigin(A, B, C):
'''Return the calcVectorFromOrigin of three 3D vertices'''
a2 = (B - C).Length**2
b2 = (C - A).Length**2
c2 = (A - B).Length**2
if a2 * b2 * c2 == 0.0:
err('Three vertices must be distinct')
return
alpha = a2 * (b2 + c2 - a2)
beta = b2 * (c2 + a2 - b2)
gamma = c2 * (a2 + b2 - c2)
return (alpha*A + beta * B + gamma * C)/(alpha + beta + gamma)
def calcCenterOfMass(subshapes):
mass =0.0
moment = App.Vector(0, 0, 0)
for sh in subshapes:
mass += sh.Volume
moment += sh.Volume * sh.CenterOfMass
return moment/mass
def getShapeType(sel):
if hasattr(sel, 'ShapeType'):
return sel.ShapeType
elif hasattr(sel, 'Object') and hasattr(sel.Object, 'Shape') and hasattr(sel.Object.Shape, 'ShapeType'):
return sel.Object.Shape.ShapeType
else:
return 'unknown'
def getVectorFromOriginToSelection(sel):
msg=f"Object '{sel.ObjectName}'"
if sel.HasSubObjects:
vv = sel.SubObjects
msg=f"{msg} contained {vv} subobject(s)"
if len(vv) ==3 and getShapeType(vv[0]) == 'Vertex' and getShapeType(vv[1]) == 'Vertex' and getShapeType(vv[2]) == 'Vertex':
debug(f"{msg} all with .ShapeType='Vertex', using their .Point properties")
return calcVectorFromOrigin(vv[0].Point, vv[1].Point, vv[2].Point)
elif len(vv) == 1:
selected = sel.SubObjects[0]
shapeType=getShapeType(selected)
msg=f"{msg} had .ShapeType='{shapeType}'"
if shapeType == 'Vertex':
debug(f"{msg}, using the .Point property")
return selected.Point
elif shapeType == 'Edge':
msg=f"{msg} and .Curve.TypeId='{selected.Curve.TypeId}'"
if selected.Curve.TypeId == 'Part::GeomCircle': #circles and arcs
debug(f'{msg}, using the .Center property')
return selected.Curve.Center
elif selected.Curve.TypeId == 'Part::GeomLine':
debug(f'{msg}, using the average of the .FirstParameter and .LastParameter properties')
return (selected.valueAt(selected.FirstParameter) + selected.valueAt(selected.LastParameter))/2 # mid-point
else:
err(f"{msg}, however currently only handling edges of types 'Part::GeomCircle' or 'Part::GeomLine'")
return App.Vector(0, 0, 0) # bail and do nothing
elif shapeType == 'Face':
debug(f"{msg}, using the .CenterOfMass property")
return selected.CenterOfMass #center of face
else:
err(f"{msg}. These types are not currently supported")
return App.Vector(0, 0, 0) # bail and do nothing
else:
err(f"{msg} which is not currently supported")
return App.Vector(0, 0, 0) # bail and do nothing
else:
# debug(f'ObjectName is {sel.ObjectName}')
if hasattr(sel.Object, 'Shape'):
shape = sel.Object.Shape
msg=f"{msg} had .Object.Shape.ShapeType='{shape.ShapeType}'"
if shape.ShapeType == 'Solid' or shape.ShapeType == 'Shell':
debug(f"{msg}, using .Center property")
return shape.CenterOfMass
elif shape.ShapeType == 'Compound' or shape.ShapeType == 'CompSolid':
debug(f"{msg}, calculating center or mass of its subshapes")
return calcCenterOfMass(shape.SubShapes)
elif shape.ShapeType == 'Vertex':
debug(f"{msg}, using the .Point property")
return shape.Point
else:
err(f"{msg}. These types are not currently supported")
return App.Vector(0, 0, 0) # bail and do nothing
else:
err(f"{msg} doesn't have the .Shape property, ie. not supported")
return App.Vector(0, 0, 0) # bail and do nothing
debug('...')
selections = Gui.Selection.getSelectionEx()
s=len(selections)
if s==0 or s>2:
err('Select one vertex, edge or object\nor three vertices for center of circle')
else:
objName=selections[0].ObjectName
obj=App.ActiveDocument.getObject(objName)
if(s==1):
debug(f"Will try to move {objName} to origin")
vector=getVectorFromOriginToSelection(selections[0]);
# Remember, the vector starts in the origin and runs to the current position,
# but we want the move to happen in reverse so...
toorigin = -vector;
debug(f'Vector to origin: {toorigin}')
obj.Placement.move(toorigin)
else:
debug(f"Will try to move {objName} to second selection...")
# techincally we're moving it first to the origin, then out to the second point
vector = getVectorFromOriginToSelection(selections[0]);
toorigin = -vector
debug(f'Move to origin: {toorigin}')
obj.Placement.move(toorigin)
vector = getVectorFromOriginToSelection(selections[1]);
fromorigin = vector
debug(f'Move from origin to second selection: {fromorigin}')
obj.Placement.move(fromorigin)
App.ActiveDocument.recompute()
-
- Veteran
- Posts: 3180
- Joined: Thu Sep 24, 2020 10:31 pm
- Location: Hawaii
- Contact:
Re: Discussion on placement
Sounds useful. Thanks, I'll take a look. A later version of my code, with undo and other features is in https://wiki.freecadweb.org/Macro_MoveToOrigin
I don't think it handles nested linked objects, because getGlobalPlacement() needs to be dealt with in that case. - though for its original purpose of dealing with STEP imports, this problem doesn't arise.
I don't think it handles nested linked objects, because getGlobalPlacement() needs to be dealt with in that case. - though for its original purpose of dealing with STEP imports, this problem doesn't arise.