Macro Center Align Objects with Faces or Edges

Description
This macro covers the following constraints:

- Concentric constraint among non cylindrical parts;

- Constraint on center Faces and/or Edges.







Aligning tool video tutorial

Use
Faces or Edges constraint among non cylindrical parts: Just open a FC document, launch the Macro and select two or more Faces/Edges to be aligned. Click on Align button and that's it!

Script
The icon for your toolbar

Macro Center Faces of Parts.FCMacro

After downloading the file here

GitHub page

https://github.com/easyw/FreeCAD_Macros/blob/master/Align%20Objects/CenterAlignObjectswFacesEdges.py

code:

https://github.com/easyw/FreeCAD_Macros/raw/master/Align%20Objects/CenterAlignObjectswFacesEdges.py

you need to copy the file to your macro directory.

How to install macros

QtCore.Qt.WindowStaysOnTopHint); CenterAlignObjectsFacesEdges.show
 * 1) CenterAlignObjectsFacesEdges.setWindowFlags(CenterAlignObjectsFacesEdges.windowFlags & QtCore.Qt.CustomizeWindowHint)
 * 2) CenterAlignObjectsFacesEdges.setWindowModality(QtCore.Qt.ApplicationModal)
 * 3) self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
 * 4) CenterAlignObjectsFacesEdges.setWindowFlags(CenterAlignObjectsFacesEdges.windowFlags & QtCore.Qt.WindowStaysOnTopHint)


 * 1) def center(self):
 * 2)    frameGm = self.frameGeometry
 * 3)     screen = QtGui.QApplication.desktop.screenNumber(QtGui.QApplication.desktop.cursor.pos)
 * 4)     #say(screen)
 * 5)     centerPoint = QtGui.QApplication.desktop.screenGeometry(screen).center
 * 6)     #say(centerPoint)
 * 7)     frameGm.moveCenter(centerPoint)
 * 8)     self.move(frameGm.center)

CenterAlignObjectsFacesEdges.move(100,100)
 * 1) center(CenterAlignObjectsFacesEdges)
 * 1) CenterAlignObjectsFacesEdges.move(500,100)
 * 2) to do:
 * 3) ok single instance
 * 4) - always on top
 * 5) - no maximize
 * 6) -code to be inserted and remove from new generation- ###
 * 1) -code to be inserted and remove from new generation- ###

def Undo: say('Undo') global initial_placement, last_selection global moving, rotating global objs, objs_plc if len(last_selection) == 1: obj = last_selection[0].Object say ('last selection: ' + obj.Name) obj.Placement.Base =initial_placement FreeCAD.ActiveDocument.recompute objs = [] last_selection = [] elif len (objs) > 1: say ('Moving: ' + str(moving)) say ('Rotating: ' + str(rotating)) i=0 for o in objs: say ('obj: ' + o.Name) if i != 0: #o.Placement.Base = objs_plc [i] if rotating[i][0]!=0: sayw('restoring rotation') o.Placement.move(App.Vector(-moving[i][0],-moving[i][1],-moving[i][2])) Draft.rotate(o,rotating[i][0],rotating[i][1],rotating[i][2]) else: sayw('restoring position') o.Placement.Base = objs_plc [i] #o.Placement=rtg.multiply(o.Placement) #incremental Placement #o.Placement=moving.multiply(o.Placement) #incremental Placement #else: #   o.Placement.Base = objs_plc [i] say ('Placement: ' + str(objs_plc [i])) i=i+1 objs = [] last_selection = [] FreeCAD.ActiveDocument.recompute def Move: global initial_placement, last_selection global objs, objs_plc say('Move') selection = [s for s in FreeCADGui.Selection.getSelectionEx if s.Document == FreeCAD.ActiveDocument ] if len(selection) == 1: objs = [] last_selection = selection say('Move2') PartMover( FreeCADGui.activeDocument.activeView, selection[0].Object ) say('starting '+str(initial_placement)) else: PartMoverSelectionObserver

class PartMover: global initial_placement def __init__(self, view, obj): global initial_placement self.obj = obj self.initialPosition = self.obj.Placement.Base initial_placement = self.initialPosition #sayw('init '+str(initial_placement)) self.copiedObject = False self.view = view self.callbackMove = self.view.addEventCallback("SoLocation2Event",self.moveMouse) self.callbackClick = self.view.addEventCallback("SoMouseButtonEvent",self.clickMouse) self.callbackKey = self.view.addEventCallback("SoKeyboardEvent",self.KeyboardEvent) def moveMouse(self, info): newPos = self.view.getPoint( *info['Position'] ) # debugPrint(5, 'new position %s' % str(newPos)) self.obj.Placement.Base = newPos def removeCallbacks(self): self.view.removeEventCallback("SoLocation2Event",self.callbackMove) self.view.removeEventCallback("SoMouseButtonEvent",self.callbackClick) self.view.removeEventCallback("SoKeyboardEvent",self.callbackKey) def clickMouse(self, info): global initial_placement # debugPrint(4, 'clickMouse info %s' % str(info)) if info['Button'] == 'BUTTON1' and info['State'] == 'DOWN': if not info['ShiftDown'] and not info['CtrlDown']: say('releasing obj') FreeCAD.ActiveDocument.recompute #sayw('releasing\ninitial p: '+ str( initial_placement )) #sayw('final p: '+str(self.obj.Placement.Base)) self.removeCallbacks elif info['ShiftDown']: #copy object self.obj = duplicateImportedPart( self.obj ) self.copiedObject = True elif info['CtrlDown']: azi  =  ( numpy.random.rand - 0.5 )*numpy.pi*2 ela  =  ( numpy.random.rand - 0.5 )*numpy.pi                theta =  ( numpy.random.rand - 0.5 )*numpy.pi                axis = azimuth_and_elevation_angles_to_axis( azi, ela ) self.obj.Placement.Rotation.Q = quaternion( theta, *axis )

def KeyboardEvent(self, info): # debugPrint(4, 'KeyboardEvent info %s' % str(info)) if info['State'] == 'UP' and info['Key'] == 'ESCAPE': if not self.copiedObject: self.obj.Placement.Base = self.initialPosition else: FreeCAD.ActiveDocument.removeObject(self.obj.Name) self.removeCallbacks

class PartMoverSelectionObserver: def __init__(self): FreeCADGui.Selection.addObserver(self) FreeCADGui.Selection.removeSelectionGate def addSelection( self, docName, objName, sub, pnt ): # debugPrint(4,'addSelection: docName,objName,sub = %s,%s,%s' % (docName, objName, sub)) FreeCADGui.Selection.removeObserver(self) obj = FreeCAD.ActiveDocument.getObject(objName) view = FreeCADGui.activeDocument.activeView PartMover( view, obj )


 * 1) class MovePartCommand:
 * 2)     say('Move')
 * 3)     def Activated(self):
 * 4)         selection = [s for s in FreeCADGui.Selection.getSelectionEx if s.Document == FreeCAD.ActiveDocument ]
 * 5)         if len(selection) == 1:
 * 6)             say('Move2')
 * 7)             PartMover( FreeCADGui.activeDocument.activeView, selection[0].Object )
 * 8)         else:
 * 9)             PartMoverSelectionObserver

def duplicateImportedPart( part ): nameBase = part.Label while nameBase[-1] in '0123456789' and len(nameBase) > 0: nameBase = nameBase[:-1] try: newObj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", findUnusedObjectName(nameBase)) except UnicodeEncodeError: safeName = findUnusedObjectName('import_') newObj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython", safeName) newObj.Label = findUnusedLabel( nameBase ) newObj.addProperty("App::PropertyFile",   "sourceFile",    "importPart").sourceFile = part.sourceFile newObj.addProperty("App::PropertyFloat", "timeLastImport","importPart").timeLastImport = part.timeLastImport newObj.setEditorMode("timeLastImport",1) newObj.addProperty("App::PropertyBool","fixedPosition","importPart").fixedPosition = False# part.fixedPosition newObj.addProperty("App::PropertyBool","updateColors","importPart").updateColors = getattr(part,'updateColors',True) newObj.Shape = part.Shape.copy for p in part.ViewObject.PropertiesList: #assuming that the user may change the appearance of parts differently depending on their role in the assembly. if hasattr(newObj.ViewObject, p) and p not in ['DiffuseColor','Proxy']: setattr(newObj.ViewObject, p, getattr( part.ViewObject, p)) newObj.ViewObject.DiffuseColor = copy.copy( part.ViewObject.DiffuseColor ) newObj.Proxy = Proxy_importPart newObj.ViewObject.Proxy = ImportedPartViewProviderProxy newObj.Placement.Base = part.Placement.Base newObj.Placement.Rotation = part.Placement.Rotation return newObj
 * 1) FreeCADGui.addCommand('assembly2_movePart', MovePartCommand)

def Align(normal,type,mode,cx,cy,cz): global initial_placement, last_selection global objs, objs_plc global moving, rotating objs = [] ; objs_plc = [] #cx = 1 # center x -> 1 #cy = 1 # center y -> 1 #cz = 1 # center z -> 1 say(str(cx)+str(cy)+str(cz)) create_points=False use_bb = True #align center based on bounding boxes or center of mass if type==1: use_bb = False #align center based on bounding boxes or center of mass

sel = FreeCADGui.Selection.getSelection selEx = FreeCADGui.Selection.getSelectionEx if len(selEx) < 2: return last_selection = [] say("number of objects: "+ str(len(selEx))) objs = [selobj.Object for selobj in selEx] #k=0 for o in objs: say ('obj: ' + o.Name) objs_plc.append(o.Placement.Base) say ('Placement: ' + str(o.Placement.Base)) moving.append([App.Vector(0,0,0)]) rotating.append([0, App.Vector(0,0,0), App.Vector(0,0,0)]) #k=k+1 #objs_plc = [selobj.Object.Placement.Base for selobj in selEx] #say(objs) #sayw(objs_plc) #stop #def say(msg): #   FreeCAD.Console.PrintMessage(msg) #   FreeCAD.Console.PrintMessage('\n') #   #def sayw(msg): #   FreeCAD.Console.PrintWarning(msg) #   FreeCAD.Console.PrintWarning('\n') def edgeToVector(edge): """ Return a vector from an edge or a Part.line.       """ if isinstance(edge,Part.Shape): return edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point) elif isinstance(edge,Part.Line): return edge.EndPoint.sub(edge.StartPoint) else: sayw("Error in edgeToVector(edge) : not a good type of input" + str(type(edge))) return None

def centerLinePoint(edge,info=0): """ Return the center point of the Line.       """ center = None #VVector_A=edge.valueAt( 0.0 ) Vector_A = edge.Vertexes[0].Point if info != 0: say("Origin of line selected is : "+str(Vector_A)) #Vector_B=edge.valueAt( edge.Length ) Vector_B = edge.Vertexes[-1].Point if info != 0: say("End of line selected is : "+str(Vector_B)) Vector_MidPoint = Vector_B + Vector_A center = Vector_MidPoint.multiply(0.5) if info != 0: say("Center of line selected is : "+str(center)) return center def object_alignEdges: """        Align the Edge(s) from selected object(s) to the last Edge selected.        - Click first to select an Edge of an object or several Edges from several objects.         - Click second to select an Edge to align to.        NB:        The center of rotation is the center of the bounbing box if possible or         the center of the Edge.        if the Edge of the object selected is already aligned to the last one,        a rotation of 180 deg is applied to the object.        In this case the Axis of rotation is Z vector : Base.Vector(0, 0, 1)        Two clicks will rotate by 180 deg.        """ msg=verbose error_msg =\ "INCORRECT Object(s) Selection :\n" +\ "You Must Select at least two(2) Edges (from two objects) !\n" +\ "All Edges will be aligned to the last one !" Selection = get_SelectedObjectsWithParent(info=msg, printError=False) m_actDoc=get_ActiveDocument(info=1) Selection2 = Gui.Selection.getSelectionEx(m_actDoc.Name) try: SelectedObjects = Selection Number_of_Edges = SelectedObjects[1] if msg!=0: print_msg("Number_of_Edges=" + str(Number_of_Edges)) if Number_of_Edges >= 2 : Edge_List = SelectedObjects[4] if msg != 0: print_msg(" Edge_List=" + str(Edge_List)) # Get the Reference Edge : last of the selected Ref_Edge_dict = Edge_List[-1] for Selected_Edge, Parent_Edge in Ref_Edge_dict.iteritems: Edge_ref = Selected_Edge del Edge_List[-1] for Selected_Edge_dict in Edge_List: if msg != 0: print_msg("Selected_Edge_dict = " + str(Selected_Edge_dict)) for Selected_Edge, Parent_Edge in Selected_Edge_dict.iteritems: if msg != 0: print_msg("Selected_Edge = " + str(Selected_Edge)) print_msg("Parent = " + str(Parent_Edge)) try: Edge_Point = Parent_Edge.Shape.BoundBox.Center except: Edge_Point = centerLinePoint(Selected_Edge,info=0) if msg != 0: print_point(Edge_Point, msg="Edge_Point = ") Edge = Selected_Edge if colinearEdges(Edge, Edge_ref, info=msg, tolerance=1e-12): rot_axis = Base.Vector(0, 0, 1).cross(edgeToVector(Edge)) rot_center = Edge_Point rot_angle = 180. + m_angleAlignEdges Draft.rotate(Parent_Edge,rot_angle,rot_center,rot_axis) else: m_angle, m_angle_rad = angleBetween(Edge,Edge_ref) print_msg("m_angle = " + str(m_angle)) rot_axis = edgeToVector(Edge).cross(edgeToVector(Edge_ref)) print_msg("rot_axis = " + str(rot_axis)) rot_center = Edge_Point rot_angle = m_angle + m_angleAlignEdges Draft.rotate(Parent_Edge,rot_angle,rot_center,rot_axis) # Reset the selection changed by Draft.rotate reset_SelectedObjects(Selection2, info=0) else: sayerr(error_msg) except: sayerr(error_msg) def angleBetween(e1, e2): """ Return the angle (in degrees) between 2 edges.       """ if isinstance(e1,Part.Edge) and isinstance(e2,Part.Edge): # Create the Vector for first edge v1 = e1.Vertexes[-1].Point v2 = e1.Vertexes[0].Point ve1 = v1.sub(v2) # Create the Vector for second edge v3 = e2.Vertexes[-1].Point v4 = e2.Vertexes[0].Point ve2 = v3.sub(v4) elif isinstance(e1,Base.Vector) and isinstance(e2,Base.Vector): ve1 = e1           ve2 = e2        elif isinstance(e1,Part.Edge) and isinstance(e2,Base.Vector): v1 = e1.Vertexes[-1].Point v2 = e1.Vertexes[0].Point ve1 = v1.sub(v2) ve2 = e2       elif isinstance(e1,Base.Vector) and  isinstance(e2,Part.Edge): ve1 = e1           v3 = e2.Vertexes[-1].Point v4 = e2.Vertexes[0].Point ve2 = v3.sub(v4) else: return angle = ve1.getAngle(ve2) import math return math.degrees(angle), angle def colinearVectors(A, B, C, info=0, tolerance=1e-12): """ Return true if the 3 points are aligned.       """ Vector_1 = B - A       Vector_2 = C - B        #if info != 0: #   print_point(Vector_1, msg="Vector_1 : ") #   print_point(Vector_2, msg="Vector_2 : ") Vector_3 = Vector_1.cross(Vector_2) #if info != 0: #   print_point(Vector_3, msg="Vector_1.cross(Vector_2) : ") if abs(Vector_3.x) <= tolerance and abs(Vector_3.y) <= tolerance and abs(Vector_3.z) <= tolerance: if info != 0: sayw("Colinear Vectors !") return True else: if info != 0: sayw("NOT Colinear Vectors !") return False return coords = [] normals = [] coordPs = [] sEdge = [] j = 0 # .BoundBox.Center #align faces if (len(selEx) > 1) and (len(selEx)==len(sel)): #s = obj.Shape last_selection = [] #removing old Move object for fc in selEx: say ("j= "+str(j)) say("len selEx "+str(len(selEx))) s=fc #selectedEdge = FreeCADGui.Selection.getSelectionEx[j].SubObjects[0] # select one element SubObjects selectedEdge = selEx[j].SubObjects[0] # select one element SubObjects sEdge.append(selectedEdge) pad=0 if str(fc.SubObjects[0])[1:5] != "Face": #edge # try: #    Edge_Point = centerLinePoint(selectedEdge,info=1) # except: #    stop try: wire = Part.Wire(selectedEdge) fw = Part.Face(wire) except: sayerr('edge non closed to be managed') Edge_Point = centerLinePoint(selectedEdge,info=0) reply = QtGui.QMessageBox.information(None,"info", "edge(s) non closed are not managed atm\n") stop #Part.show(fw) f=FreeCAD.ActiveDocument.addObject("Part::Feature","Facebinder") f.Shape=fw pad=1 #FreeCAD.ActiveDocument.recompute say("Label : "+ make_string(sel[j].Label))    # extract the Label say("Name : "+ str(sel[j].Name))     # extract the Name say( "Center Face Binder "+str(0)+" "+str(FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass)) # Vector center mass to face say( "Center Face Binder bb "+str(0)+" "+str(FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center)) # Vector center mass to face else: #face pad=0 f=Draft.makeFacebinder(s) say("Label : "+ make_string(sel[j].Label))    # extract the Label say("Name : "+ str(sel[j].Name))     # extract the Name say( "Center Face Binder "+str(0)+" "+str(f.Shape.Faces[0].CenterOfMass)) # Vector center mass to face say( "Center Face Binder bb "+str(0)+" "+str(f.Shape.Faces[0].BoundBox.Center)) # Vector center mass to face # LineColor red  = 1.0  # 1 = 255 green = 0.0 # blue = 0.0  # if create_points: if pad==0: if use_bb: Draft.makePoint(f.Shape.Faces[0].BoundBox.Center.x,f.Shape.Faces[0].BoundBox.Center.y,f.Shape.Faces[0].BoundBox.Center.z) # create a point else: Draft.makePoint(f.Shape.Faces[0].CenterOfMass.x,f.Shape.Faces[0].CenterOfMass.y,f.Shape.Faces[0].CenterOfMass.z) # create a point FreeCADGui.activeDocument.activeObject.PointColor = (red, green, blue) else: if use_bb: Draft.makePoint(FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.x,FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.y,FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.z) # create a point else: Draft.makePoint(FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.x,FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.y,FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.z) # create a point FreeCADGui.activeDocument.activeObject.PointColor = (red, green, blue) if pad==0: if use_bb: coordNx = f.Shape.Faces[0].BoundBox.Center.x                   coordNy = f.Shape.Faces[0].BoundBox.Center.y                    coordNz = f.Shape.Faces[0].BoundBox.Center.z                    coordP  = f.Shape.Faces[0].BoundBox.Center else: coordNx = f.Shape.Faces[0].CenterOfMass.x                   coordNy = f.Shape.Faces[0].CenterOfMass.y                    coordNz = f.Shape.Faces[0].CenterOfMass.z                    coordP  = f.Shape.Faces[0].CenterOfMass else: if use_bb: coordNx = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.x                   coordNy = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.y                    coordNz = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.z                    coordP  = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center else: coordNx = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.x                   coordNy = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.y                    coordNz = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.z                    coordP  = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass coords.append ([coordNx,coordNy,coordNz]) coordPs.append (coordP) #norm = f.Shape.Faces[0].normalAt(0,0) if j==0: if normal==1: norm = f.Shape.Faces[0].normalAt(0,0)*-1 else: norm = f.Shape.Faces[0].normalAt(0,0) else: norm = f.Shape.Faces[0].normalAt(0,0) #else: #   norm = f.Shape.Faces[0].normalAt(0,0) say (norm) normals.append (norm) FreeCAD.ActiveDocument.removeObject(f.Name) if j>0: pos=App.Vector(-coords[j][0]+coords[0][0],-coords[j][1]+coords[0][1],-coords[j][2]+coords[0][2]) ## objs[j].Placement.move(pos) m_angle, m_angle_rad = angleBetween(normals[0],normals[j]) say (m_angle) Origin = Base.Vector(0, 0, 0) copl=0 if colinearVectors(normals[0], Origin, normals[j], info=1, tolerance=1e-12): rot_axis = Base.Vector(0, 0, 1).cross(normals[0]) if rot_axis==FreeCAD.Vector (0.0, 0.0, 0.0): rot_axis=Base.Vector(0, 1, 0).cross(normals[0]) rot_center = coordPs[j] if normal==1: rot_angle = 180. # + m_angleAlignFaces else: rot_angle=0. copl=1 #Draft.rotate(Parent_Plane,rot_angle,rot_center,rot_axis) else: #m_angle, m_angle_rad = angleBetween(Plane_Normal,Plane_Normal_ref) rot_axis = normals[0].cross(normals[j]) rot_center = coordPs[j] rot_angle = m_angle # + m_angleAlignFaces #Draft.rotate(Parent_Plane,rot_angle,rot_center,rot_axis) #rot_axis = normals[0].cross(normals[j]) #rot_center = coordPs[j] #rot_angle = m_angle # + m_angleAlignFaces sayw("axis,center,angle") say(rot_axis) say(rot_center) say(rot_angle) if rot_angle!=0: # and rot_axis!=FreeCAD.Vector (0.0, 0.0, 0.0): if mode==0 or mode==2: if rot_axis!=FreeCAD.Vector (0.0, 0.0, 0.0): Draft.rotate(objs[j],-rot_angle,rot_center,rot_axis) rotating[j] = [rot_angle,rot_center,rot_axis] say("Rotated    : angle "+str(-rot_angle)+" center "+str(rot_center)+" axis "+str(rot_axis)) else: rotating[j] = [0, App.Vector(0,0,0), App.Vector(0,0,0)] else: rotating[j] = [0, App.Vector(0,0,0), App.Vector(0,0,0)] j=j+1 coords = [] normals = [] coordPs = [] j = 0 #align centers if (len(selEx) >= 1) and (len(selEx)==len(sel)): #s = obj.Shape for fc in selEx: s=fc print j           #selectedEdge = FreeCADGui.Selection.getSelectionEx[j].SubObjects[0] # select one element SubObjects selectedEdge = sEdge[j] # select one element SubObjects pad=0 if str(fc.SubObjects[0])[1:5] != "Face": wire = Part.Wire(selectedEdge) fw = Part.Face(wire) if testing: Part.show(fw) f=FreeCAD.ActiveDocument.addObject("Part::Feature","Facebinder") f.Shape=fw pad=1 #FreeCAD.ActiveDocument.recompute else: pad=0 f=Draft.makeFacebinder(s) say("Label : "+ make_string(sel[j].Label))    # extract the Label say("Name : "+ str(sel[j].Name))     # extract the Name say( "Center Face Binder "+str(0)+" "+str(f.Shape.Faces[0].CenterOfMass)) # Vector center mass to face say( "Center Face Binder bb "+str(0)+" "+str(f.Shape.Faces[0].BoundBox.Center)) # Vector center mass to face # LineColor red  = 1.0  # 1 = 255 green = 0.0 # blue = 0.0  # if create_points: if pad==0: Draft.makePoint(f.Shape.Faces[0].CenterOfMass.x,f.Shape.Faces[0].CenterOfMass.y,f.Shape.Faces[0].CenterOfMass.z) # create a point FreeCADGui.activeDocument.activeObject.PointColor = (red, green, blue) else: Draft.makePoint(FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.x,FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.y,FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.z) # create a point FreeCADGui.activeDocument.activeObject.PointColor = (red, green, blue) if pad==0: if use_bb: coordNx = f.Shape.Faces[0].BoundBox.Center.x                   coordNy = f.Shape.Faces[0].BoundBox.Center.y                    coordNz = f.Shape.Faces[0].BoundBox.Center.z                    coordP  = f.Shape.Faces[0].BoundBox.Center else: coordNx = f.Shape.Faces[0].CenterOfMass.x                   coordNy = f.Shape.Faces[0].CenterOfMass.y                    coordNz = f.Shape.Faces[0].CenterOfMass.z                    coordP  = f.Shape.Faces[0].CenterOfMass norm = f.Shape.Faces[0].normalAt(0,0) else: if use_bb: coordNx = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.x                   coordNy = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.y                    coordNz = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center.z                    coordP  = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].BoundBox.Center else: coordNx = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.x                   coordNy = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.y                    coordNz = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass.z                    coordP  = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].CenterOfMass norm   = FreeCAD.ActiveDocument.getObject(f.Name).Shape.Faces[0].normalAt(0,0) coords.append ([coordNx,coordNy,coordNz]) coordPs.append (coordP) say (norm) normals.append (norm) FreeCAD.ActiveDocument.removeObject(f.Name) if j>0: pos=App.Vector((-coords[j][0]+coords[0][0])*cx,(-coords[j][1]+coords[0][1])*cy,(-coords[j][2]+coords[0][2])*cz) if mode==0 or mode==1: objs[j].Placement.move(pos) moving[j] = pos say("Moved    : "+str(coordNx-coords[0][0])+" "+str(coordNy-coords[0][1])+" "+str(coordNz-coords[0][2])) else: moving[j] = App.Vector(0,0,0) j=j+1 FreeCAD.ActiveDocument.recompute for obj in objs: FreeCADGui.Selection.removeSelection(obj) # except: #    App.Console.PrintError( "select a face"+"\n")

Link
Forum : Faces or Edges constraint among non cylindrical parts Macro