#*************************************************************************** #* * #* Copyright (c) 2015 - Victor Titov (DeepSOIC) * #* * #* * #* This program is free software; you can redistribute it and/or modify * #* it under the terms of the GNU Lesser General Public License (LGPL) * #* as published by the Free Software Foundation; either version 2 of * #* the License, or (at your option) any later version. * #* for detail see the LICENCE text file. * #* * #* This program is distributed in the hope that it will be useful, * #* but WITHOUT ANY WARRANTY; without even the implied warranty of * #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * #* GNU Library General Public License for more details. * #* * #* You should have received a copy of the GNU Library General Public * #* License along with this program; if not, write to the Free Software * #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * #* USA * #* * #*************************************************************************** from lattice2Common import * import lattice2Markers as markers import FreeCAD as App __title__= "Lattice SubLink feature for FreeCAD" __author__ = "DeepSOIC" __url__ = "Lattice SubLink is like Draft Facebinder, but for edges and vertices too." try: from lattice2BaseFeature import isObjectLattice except Exception: # I want to keep the module easy to strip off Lattice2 wb, so: def isObjectLattice(obj): return False # -------------------------- feature -------------------------------------------------- def makeSubLink(name): '''makeSubLink(name): makes a SubLink object.''' obj = App.ActiveDocument.addObject("Part::FeaturePython",name) LatticeSubLink(obj) ViewProviderSubLink(obj.ViewObject) return obj class LatticeSubLink: "The Lattice SubLink object" def __init__(self,obj): self.Type = "SubLink" obj.addProperty("App::PropertyLink","Object","Lattice SubLink","Object to extract an element from") obj.addProperty("App::PropertyStringList","SubNames","Lattice SubLink", "List of elements to extract. Example: Edge5,Edge8") obj.Proxy = self def execute(self,selfobj): #validity check if isObjectLattice(selfobj.Object): import lattice2Executer lattice2Executer.warning(selfobj,"A generic shape is expected, but a placement/array was supplied. It will be treated as a generic shape.") rst = [] #variable to receive the final list of shapes lnkobj = selfobj.Object for subname in selfobj.SubNames: subname = subname.strip() if len(subname)==0: raise ValueError("Empty subname! Not allowed.") if 'Face' in subname: index = int(subname.replace('Face',''))-1 rst.append(lnkobj.Shape.Faces[index]) elif 'Edge' in subname: index = int(subname.replace('Edge',''))-1 rst.append(lnkobj.Shape.Edges[index]) elif 'Vertex' in subname: index = int(subname.replace('Vertex',''))-1 rst.append(lnkobj.Shape.Vertexes[index]) else: lattice2Executer.warning(selfobj,"Unexpected subelement name: "+subname+". Trying to extract it with .Shape.getElement()...") rst.append(linkobj.Shape.getElement(subname)) if len(rst) == 0: scale = 1.0 try: if selfobj.Object: scale = selfobj.Object[0].Shape.BoundBox.DiagonalLength/math.sqrt(3) except Exception as err: App.Console.PrintError(selfobj.Name+": Failed to estimate size of marker shape") if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError('Nothing is linked, apparently!') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing. if len(rst) > 1: selfobj.Shape = Part.makeCompound(rst) else: # don't make compound of one shape, output it directly sh = rst[0] sh.transformShape(sh.Placement.toMatrix(),True) #True = make copy sh.Placement = selfobj.Placement selfobj.Shape = sh try: self.analyzeShape(selfobj) except Exception as err: App.Console.PrintError(selfobj.Name+": analyzeShape: "+err.message+"\n") def analyzeShape(self,selfobj): self.updatedProperties = set() try: sh = selfobj.Shape self.assignProp(selfobj,"App::PropertyString","ShapeType",sh.ShapeType) if sh.ShapeType == "Compound": self.assignProp(selfobj,"App::PropertyInteger","NumChildren",len(sh.childShapes(False,False))) elif sh.ShapeType == "Face": self.assignProp(selfobj,"App::PropertyFloat","Area",sh.Area) typelist = ["BSplineSurface", "BezierSurface", "Cone", "Cylinder", "OffsetSurface", "Plane", "PlateSurface", "RectangularTrimmedSurface", "Sphere", "SurfaceOfExtrusion", "SurfaceOfRevolution", "Toroid", ] surf = sh.Surface for typename in typelist: if type(surf) is getattr(Part, typename): break typename = None self.assignProp(selfobj,"App::PropertyString","FaceType",typename) self.transplant_all_attributes(selfobj,surf,"Face") elif sh.ShapeType == "Edge": self.assignProp(selfobj,"App::PropertyFloat","Length",sh.Length) typelist = ["Arc", "ArcOfCircle", "ArcOfEllipse", "ArcOfHyperbola", "ArcOfParabola", "BSplineCurve", "BezierCurve", "Circle", "Ellipse", "Hyperbola", "Line", "OffsetCurve", "Parabola", ] crv = sh.Curve for typename in typelist: if type(crv) is getattr(Part, typename): break typename = None self.assignProp(selfobj,"App::PropertyString","EdgeType",typename) self.transplant_all_attributes(selfobj,crv,"Edge") elif sh.ShapeType == "Vertex": self.assignProp(selfobj,"App::PropertyVector","Position",sh.Point) finally: #remove properties that haven't been updated for propname in selfobj.PropertiesList: if selfobj.getGroupOfProperty(propname) == "info": if not (propname in self.updatedProperties): selfobj.removeProperty(propname) def assignProp(self, selfobj, proptype, propname, propvalue): if not hasattr(selfobj,propname): selfobj.addProperty(proptype, propname,"info") selfobj.setEditorMode(propname,1) #set read-only setattr(selfobj,propname,propvalue) self.updatedProperties.add(propname) def transplant_all_attributes(self, selfobj, source, prefix): for attrname in dir(source): if attrname[0]=="_": continue try: attr = getattr(source,attrname) except Exception: continue if callable(attr): continue propname = prefix+attrname[0].upper()+attrname[1:] if type(attr) is int: self.assignProp(selfobj,"App::PropertyInteger",propname,attr) if type(attr) is float: self.assignProp(selfobj,"App::PropertyFloat",propname,attr) if type(attr) is str: self.assignProp(selfobj,"App::PropertyString",propname,attr) if type(attr) is App.Vector: self.assignProp(selfobj,"App::PropertyVector",propname,attr) if type(attr) is list: self.assignProp(selfobj,"App::PropertyInteger",propname+"Count",len(attr)) class ViewProviderSubLink: "A View Provider for the SubLink object" def __init__(self,vobj): vobj.Proxy = self def getIcon(self): if len(self.Object.SubNames) == 1: subname = self.Object.SubNames[0] if 'Face' in subname: return getIconPath("Lattice2_SubLink_Face.svg") elif 'Edge' in subname: return getIconPath("Lattice2_SubLink_Edge.svg") elif 'Vertex' in subname: return getIconPath("Lattice2_SubLink_Vertex.svg") return getIconPath("Lattice2_SubLink.svg") def attach(self, vobj): self.ViewObject = vobj self.Object = vobj.Object def setEdit(self,vobj,mode): return False def unsetEdit(self,vobj,mode): return def __getstate__(self): return None def __setstate__(self,state): return None def claimChildren(self): return [] def CreateSubLink(object, subnames): #stabilize links subnames = list(subnames) #'tuple' object does not support item assignment; SubElementNames of SelectionObject is a tuple try: cnt_faces = 0 cnt_edges = 0 cnt_vertexes = 0 cnt_somethingelse = 0 n_faces = None #vars to receive counts of respective subelements in the shape of object. Not prefilling them, for speed - filled only as needed n_edges = None n_vertexes = None for i in range(len(subnames)): subname = subnames[i].strip() if 'Face' in subname: index = int(subname.replace('Face','')) if n_faces is None: n_faces = len(object.Shape.Faces) if (index-1)*2 > n_faces: index = index - n_faces subname = "Face"+str(index) cnt_faces += 1 elif 'Edge' in subname: index = int(subname.replace('Edge','')) if n_edges is None: n_edges = len(object.Shape.Edges) if (index-1)*2 > n_edges: index = index - n_edges subname = "Edge"+str(index) cnt_edges += 1 elif 'Vertex' in subname: index = int(subname.replace('Vertex','')) if n_vertexes is None: n_vertexes = len(object.Shape.Vertexes) if (index-1)*2 > n_vertexes: index = index - n_vertexes subname = "Vertex"+str(index) cnt_vertexes += 1 else: cnt_somethingelse += 1 pass #something unexpected, pass through unchanged subnames[i] = subname except Exception: raise#fixme: remove!! pass FreeCADGui.addModule("lattice2SubLink") FreeCADGui.addModule("lattice2Executer") name = object.Name+"_"+subnames[0] if len(subnames)==1 else "SubLink" FreeCADGui.doCommand("f = lattice2SubLink.makeSubLink(name = "+repr(name)+")") label = unicode(subnames[0] if len(subnames)==1 else "subelements") + u" of " + object.Label FreeCADGui.doCommand("f.Label = "+repr(label)) FreeCADGui.doCommand("f.Object = App.ActiveDocument."+object.Name) FreeCADGui.doCommand("f.SubNames = "+repr(subnames)) FreeCADGui.doCommand("lattice2Executer.executeFeature(f)") if cnt_vertexes > 0 and cnt_faces+cnt_edges+cnt_somethingelse == 0: #only vertices selected - make them bigger to make them visible FreeCADGui.doCommand("f.ViewObject.PointSize = 10") FreeCADGui.doCommand("f.Object.ViewObject.hide()") FreeCADGui.doCommand("Gui.Selection.addSelection(f)") FreeCADGui.doCommand("f = None") def cmdSubLink(): sel = FreeCADGui.Selection.getSelectionEx() if len(sel) == 0: raise SelectionError("Bad selection", "Please select some subelements from one object, first.") if len(sel) > 1: raise SelectionError("Bad selection", "You have selected subelements from more than one object. Not allowed. You can only select subelements of one object.") if len(sel[0].SubElementNames)==0: raise SelectionError("Bad selection", "Please select some subelements, not the whole object.") App.ActiveDocument.openTransaction("Create SubLink") CreateSubLink(sel[0].Object,sel[0].SubElementNames) deselect(sel) App.ActiveDocument.commitTransaction() # -------------------------- /common stuff -------------------------------------------------- # -------------------------- Gui command -------------------------------------------------- class _CommandSubLink: "Command to create SubLink feature" def GetResources(self): return {'Pixmap' : getIconPath("Lattice2_SubLink.svg"), 'MenuText': QtCore.QT_TRANSLATE_NOOP("Lattice2_SubLink","Compound Filter"), 'Accel': "", 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Lattice2_SubLink","Compound Filter: remove some childs from a compound")} def Activated(self): try: if len(FreeCADGui.Selection.getSelection())==0: infoMessage("SubLink", "'SubLink' command. Extracts selected faces, edges or vertices from the object.\n\n"+ "Please select subelements of one object, then invoke the command.") return cmdSubLink() except Exception as err: msgError(err) def IsActive(self): if App.ActiveDocument: return True else: return False FreeCADGui.addCommand('Lattice2_SubLink', _CommandSubLink()) exportedCommands = ['Lattice2_SubLink'] # -------------------------- /Gui command --------------------------------------------------