From 1c9fe432f7e81916f247cd114679558130ec208e Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Sun, 17 Feb 2013 11:24:12 -0300 Subject: [PATCH] Draft: Upgrade tool is now available to python scripting --- src/Mod/Draft/Draft.py | 363 +++++++++++++++++++++++++++++++++++- src/Mod/Draft/DraftTools.py | 274 ++------------------------- 2 files changed, 369 insertions(+), 268 deletions(-) diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index f5aee76be..2f29b9ee2 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -1200,10 +1200,11 @@ def offset(obj,delta,copy=False,bind=False,sym=False,occ=False): select(obj) return newobj -def draftify(objectslist,makeblock=False): - '''draftify(objectslist,[makeblock]): turns each object of the given list +def draftify(objectslist,makeblock=False,delete=True): + '''draftify(objectslist,[makeblock],[delete]): turns each object of the given list (objectslist can also be a single object) into a Draft parametric - wire. If makeblock is True, multiple objects will be grouped in a block''' + wire. If makeblock is True, multiple objects will be grouped in a block. + If delete = False, old objects are not deleted''' import DraftGeomUtils, Part if not isinstance(objectslist,list): @@ -1226,7 +1227,8 @@ def draftify(objectslist,makeblock=False): nobj.ViewObject.DisplayMode = "Wireframe" newobjlist.append(nobj) formatObject(nobj,obj) - FreeCAD.ActiveDocument.removeObject(obj.Name) + if delete: + FreeCAD.ActiveDocument.removeObject(obj.Name) FreeCAD.ActiveDocument.recompute() if makeblock: return makeBlock(newobjlist) @@ -1731,8 +1733,359 @@ def heal(objlist=None,delete=True,reparent=True): if dellist and delete: for n in dellist: FreeCAD.ActiveDocument.removeObject(n) + +def upgrade(objects,deelte=False,force=None): + """upgrade(objects,delete=False,force=None): Upgrades the given object(s) (can be + an object or a list of objects). If delete is True, old objects are deleted. + The force attribute can be used to + force a certain way of upgrading. It can be: makeCompound, closeGroupWires, + makeSolid, closeWire, turnToParts, makeFusion, makeShell, makeFaces, turnToDraft, + joinFaces, makeSketchFace, makeWires + Returns a dictionnary containing two lists, a list of new objects and a list + of objects to be deleted""" + + import Part, DraftGeomUtils + from DraftTools import msg,translate + + if not isinstance(objects,list): + objects = [objects] + + global deleteList, newList + deleteList = [] + addList = [] + + # definitions of actions to perform + + def makeCompound(objectslist): + """returns a compound object made from the given objects""" + newobj = makeBlock(objectslist) + addList.append(newobj) + return newobj + + def closeGroupWires(groupslist): + """closes every open wire in the given groups""" + result = False + for grp in groupslist: + for obj in grp.Group: + newobj = closeWire(obj) + # add new objects to their respective groups + if newobj: + result = True + grp.addObject(newobj) + return result + + def makeSolid(obj): + """turns an object into a solid, if possible""" + if obj.Shape.Solids: + return None + sol = None + try: + sol = Part.makeSolid(obj.Shape) + except: + return None + else: + if sol: + if sol.isClosed(): + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Solid") + newobj.Shape = sol + addList.append(newobj) + deleteList.append(obj) + return newob + + def closeWire(obj): + """closes a wire object, if possible""" + if obj.Shape.Faces: + return None + if len(obj.Shape.Wires) != 1: + return None + if len(obj.Shape.Edges) == 1: + return None + if getType(obj) == "Wire": + obj.Closed = True + return True + else: + w = obj.Shape.Wires[0] + if not w.isClosed(): + edges = w.Edges + p0 = w.Vertexes[0].Point + p1 = w.Vertexes[-1].Point + if p0 == p1: + # sometimes an open wire can have its start and end points identical (OCC bug) + # in that case, although it is not closed, face works... + f = Part.Face(w) + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Face") + newobj.Shape = f + else: + edges.append(Part.Line(p1,p0).toShape()) + w = Part.Wire(DraftGeomUtils.sortEdges(edges)) + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Wire") + newobj.Shape = w + addList.append(newobj) + deleteList.append(obj) + return newobj + else: + return None + + def turnToParts(meshes): + """turn given meshes to parts""" + result = False + import Arch + for mesh in meshes: + sh = Arch.getShapeFromMesh(mesh.Mesh) + if sh: + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Shell") + newobj.Shape = sh + addList.append(newobj) + deleteList.append(mesh) + result = True + return result + + def makeFusion(obj1,obj2): + """makes a Draft or Part fusion between 2 given objects""" + newobj = fuse(obj1,obj2) + if newobj: + addList.append(newobj) + return newobj + return None + + def makeShell(objectslist): + """makes a shell with the given objects""" + faces = [] + for obj in objectslist: + faces.append(obj.Shape.Faces) + sh = Part.makeShell(faces) + if sh: + if sh.Faces: + newob = FreeCAD.ActiveDocument.addObject("Part::Feature","Shell") + newob.Shape = sh + addList.append(newobj) + deleteList.extend(objectslist) + return newobj + return None + + def joinFaces(objectslist): + """makes one big face from selected objects, if possible""" + faces = [] + for obj in objectslist: + faces.append(obj.Shape.Faces) + u = faces.pop(0) + for f in faces: + u = u.fuse(f) + if DraftGeomUtils.isCoplanar(faces): + u = DraftGeomUtils.concatenate(u) + if not DraftGeomUtils.hasCurves(u): + # several coplanar and non-curved faces: they can becoem a Draft wire + newobj = makeWire(u.Wires[0],closed=True,face=True) + else: + # if not possible, we do a non-parametric union + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Union") + newobj.Shape = u + addList.append(newobj) + deleteList.extend(objectslist) + return newobj + return None + + def turnToDraft(objectslist): + """turns each of the objects into a Draft object, if possible""" + newobj = draftify(objects,delete=False) + if newobj: + if not isinstance(newobj,list): + newobj = [newobj] + addList.extend(newobj) + deleteList.extend(objectslist) + return newobj + return None + + def makeSketchFace(obj): + """Makes a Draft face out of a sketch""" + newobj = makeWire(obj.Shape,closed=True) + if newobj: + newobj.Base = obj + obj.ViewObject.Visibility = False + addList.append(newobj) + return newobj + return None + + def makeFaces(objectslist): + """make a face from every closed wire in the list""" + result = False + for o in objectslist: + for w in o.Shape.Wires: + if w.isClosed() and DraftGeomUtils.isPlanar(w): + f = Part.Face(w) + if f: + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Face") + newobj.Shape = f + addList.append(newobj) + result = True + return result + + def makeWires(objectslist): + """joins edges in the given objects list into wires""" + edges = [] + for o in objectslist: + for e in o.Shape.Edges: + edges.append(e) + try: + nedges = DraftGeomUtils.sortEdges(edges[:]) + # for e in nedges: print "debug: ",e.Curve,e.Vertexes[0].Point,e.Vertexes[-1].Point + w = Part.Wire(nedges) + except: + return None + else: + if len(w.Edges) == len(edges): + newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Wire") + newobj.Shape = w + addList.append(newobj) + deleteList.extend(objectslist) + return True + return None + + # analyzing what we have in our selection + + edges = [] + wires = [] + openwires = [] + faces = [] + groups = [] + parts = [] + curves = [] + facewires = [] + loneedges = [] + meshes = [] + for ob in objects: + if ob.Type == "App::DocumentObjectGroup": + groups.append(ob) + elif ob.isDerivedFrom("Part::Feature"): + parts.append(ob) + faces.extend(ob.Shape.Faces) + wires.extend(ob.Shape.Wires) + edges.extend(ob.Shape.Edges) + for f in ob.Shape.Faces: + facewires.extend(f.Wires) + wirededges = [] + for w in ob.Shape.Wires: + if len(w.Edges) > 1: + for e in w.Edges: + wirededges.append(e.hashCode()) + if not w.isClosed(): + openwires.append(w) + for e in ob.Shape.Edges: + if not isinstance(e.Curve,Part.Line): + curves.append(e) + if not e.hashCode() in wirededges: + loneedges.append(e) + elif ob.isDerivedFrom("Mesh::Feature"): + meshes.append(ob) + objects = parts + + print "objects:",objects," edges:",edges," wires:",wires," openwires:",openwires," faces:",faces + print "groups:",groups," curves:",curves," facewires:",facewires, "loneedges:", loneedges + + if force: + if force in ["makeCompound","closeGroupWires","makeSolid","closeWire","turnToParts","makeFusion", + "makeShell","makeFaces","turnToDraft","joinFaces","makeSketchFace","makeWires"]: + result = eval(force)(objects) + else: + msg(translate("Upgrade: Unknow force method:")+" "+force) + result = None + + else: + + # applying transformations automatically + + result = None + + # if we have a group: turn each closed wire inside into a face + if groups: + result = closeGroupWires(groups) + if result: msg(translate("draft", "Found groups: closing each open object inside\n")) + + # if we have meshes, we try to turn them into shapes + elif meshes: + result = turnToParts(meshes) + if result: msg(translate("draft", "Found mesh(es): turning into Part shapes\n")) + + # we have only faces here, no lone edges + elif faces and (len(wires) + len(openwires) == len(facewires)): + + # we have one shell: we try to make a solid + if (len(objects) == 1) and (len(faces) > 3): + result = makeSolid(objects[0]) + if result: msg(translate("draft", "Found 1 solidificable object: solidifying it\n")) + + # we have exactly 2 objects: we fuse them + elif (len(objects) == 2) and (not curves): + result = makeFusion(objects[0],objects[1]) + if result: msg(translate("draft", "Found 2 objects: fusing them\n")) + + # we have many separate faces: we try to make a shell + elif (len(objects) > 2) and (len(faces) > 1) and (not loneedges): + result = makeShell(objects) + if result: msg(translate("draft", "Found several objects: making a shell\n")) + + # we have faces: we try to join them if they are coplanar + elif len(faces) > 1: + result = joinFaces(objects) + if result: msg(translate("draft", "Found several coplanar objects or faces: making one face\n")) + + # only one object: if not parametric, we "draftify" it + elif len(objects) == 1: + result = turnToDraft(objects) + if result: msg(translate("draft", "Found 1 non-parametric objects: draftifying it\n")) + + # we have only closed wires, no faces + elif wires and (not faces) and (not openwires): + + # we have a sketch: Extract a face + if (len(objects) == 1) and objects[0].isDerivedFrom("Sketcher::SketchObject") and (not curves): + result = makeSketchFace(objects[0]) + if result: msg(translate("draft", "Found 1 closed sketch object: making a face from it\n")) + + # only closed wires + else: + result = makeFaces(objects) + if result: msg(translate("draft", "Found closed wires: making faces\n")) + + # special case, we have only one open wire. We close it, unless it has only 1 edge!" + elif (len(openwires) == 1) and (not faces) and (not loneedges): + result = closeWire(objects[0]) + if result: msg(translate("draft", "Found 1 open wire: closing it\n")) + + # only open wires and edges: we try to join their edges + elif openwires and (not wires) and (not faces): + result = makeWires(objects) + if result: msg(translate("draft", "Found several open wires: joining them\n")) + + # only loneedges: we try to join them + elif loneedges and (not facewires): + result = makeWires(objects) + if result: msg(translate("draft", "Found several edges: wiring them\n")) + + # only one selected object, do not make a compound + elif (len(objects) == 1): + result = None + + # all other cases + else: + result = makeCompound(objects) + if result: msg(translate("draft", "Found several non-treatable objects: making compound\n")) + + # no result has been obtained + if not result: + msg(translate("draft", "Unable to upgrade these objects\n")) + + if delete: + names = [] + for o in deleteList: + names.append(o.Name) + deleteList = [] + for n in names: + FreeCAD.ActiveDocument.removeObject(n) + + return [addList,deleteList] + - #--------------------------------------------------------------------------- # Python Features definitions #--------------------------------------------------------------------------- diff --git a/src/Mod/Draft/DraftTools.py b/src/Mod/Draft/DraftTools.py index 7e1571b89..2f379e23e 100644 --- a/src/Mod/Draft/DraftTools.py +++ b/src/Mod/Draft/DraftTools.py @@ -2127,17 +2127,9 @@ class Offset(Modifier): 'Draft.offset(FreeCAD.ActiveDocument.'+self.sel.Name+','+d+',copy='+str(copymode)+',occ='+str(occmode)+')']) self.finish() - + class Upgrade(Modifier): - '''The Draft_Upgrade FreeCAD command definition. - This class upgrades selected objects in different ways, - following this list (in order): - - if there are more than one faces, the faces are merged (union) - - if there is only one face, nothing is done - - if there are closed wires, they are transformed in a face - - otherwise join all edges into a wire (closed if applicable) - - if nothing of the above is possible, a Compound is created - ''' + '''The Draft_Upgrade FreeCAD command definition.''' def GetResources(self): return {'Pixmap' : 'Draft_Upgrade', @@ -2154,261 +2146,17 @@ class Upgrade(Modifier): self.call = self.view.addEventCallback("SoEvent",selectObject) else: self.proceed() - - def compound(self): - # shapeslist = [] - # for ob in self.sel: shapeslist.append(ob.Shape) - # newob = self.doc.addObject("Part::Feature","Compound") - # newob.Shape = Part.makeCompound(shapeslist) - newob = Draft.makeBlock(self.sel) - self.nodelete = True - return newob - + def proceed(self): - if self.call: self.view.removeEventCallback("SoEvent",self.call) - self.sel = Draft.getSelection() - newob = None - self.nodelete = False - edges = [] - wires = [] - openwires = [] - faces = [] - groups = [] - curves = [] - facewires = [] - loneedges = 0 - - # determining what we have in our selection - for ob in self.sel: - if ob.Type == "App::DocumentObjectGroup": - groups.append(ob) - else: - if ob.Shape.ShapeType == 'Edge': openwires.append(ob.Shape) - for f in ob.Shape.Faces: - faces.append(f) - facewires.extend(f.Wires) - wedges = 0 - for w in ob.Shape.Wires: - wedges += len(w.Edges) - if w.isClosed(): - wires.append(w) - else: - openwires.append(w) - if wedges < len(ob.Shape.Edges): - loneedges += (len(ob.Shape.Edges)-wedges) - for e in ob.Shape.Edges: - if not isinstance(e.Curve,Part.Line): - curves.append(e) - lastob = ob - - # print "objects:",self.sel," edges:",edges," wires:",wires," openwires:",openwires," faces:",faces - # print "groups:",groups," curves:",curves," facewires:",facewires - - # applying transformation - self.doc.openTransaction("Upgrade") - - if groups: - # if we have a group: turn each closed wire inside into a face - msg(translate("draft", "Found groups: closing each open object inside\n")) - for grp in groups: - for ob in grp.Group: - if not ob.Shape.Faces: - for w in ob.Shape.Wires: - newob = Draft.makeWire(w,closed=w.isClosed()) - self.sel.append(ob) - grp.addObject(newob) - - elif faces and (len(wires)+len(openwires)==len(facewires)): - # we have only faces here, no lone edges - - if (len(self.sel) == 1) and (len(faces) > 1): - # we have a shell: we try to make a solid - sol = Part.makeSolid(self.sel[0].Shape) - if sol.isClosed(): - msg(translate("draft", "Found 1 solidificable object: solidifying it\n")) - newob = self.doc.addObject("Part::Feature","Solid") - newob.Shape = sol - Draft.formatObject(newob,lastob) - - elif (len(self.sel) == 2) and (not curves): - # we have exactly 2 objects: we fuse them - msg(translate("draft", "Found 2 objects: fusing them\n")) - newob = Draft.fuse(self.sel[0],self.sel[1]) - self.nodelete = True - - elif (len(self.sel) > 2) and (len(faces) > 6): - # we have many separate faces: we try to make a shell - sh = Part.makeShell(faces) - newob = self.doc.addObject("Part::Feature","Shell") - newob.Shape = sh - Draft.formatObject(newob,lastob) - - elif (len(self.sel) > 2) or (len(faces) > 1): - # more than 2 objects or faces: we try the draft way: make one face out of them - u = faces.pop(0) - for f in faces: - u = u.fuse(f) - if DraftGeomUtils.isCoplanar(faces): - if self.sel[0].ViewObject.DisplayMode == "Wireframe": - f = False - else: - f = True - u = DraftGeomUtils.concatenate(u) - if not curves: - # several coplanar and non-curved faces: they can becoem a Draft wire - msg(translate("draft", "Found several objects or faces: making a parametric face\n")) - newob = Draft.makeWire(u.Wires[0],closed=True,face=f) - Draft.formatObject(newob,lastob) - else: - # if not possible, we do a non-parametric union - msg(translate("draft", "Found objects containing curves: fusing them\n")) - newob = self.doc.addObject("Part::Feature","Union") - newob.Shape = u - Draft.formatObject(newob,lastob) - else: - # if not possible, we do a non-parametric union - msg(translate("draft", "Found several objects: fusing them\n")) - # if we have a solid, make sure we really return a solid - if (len(u.Faces) > 1) and u.isClosed(): - u = Part.makeSolid(u) - newob = self.doc.addObject("Part::Feature","Union") - newob.Shape = u - Draft.formatObject(newob,lastob) - elif len(self.sel) == 1: - # only one object: if not parametric, we "draftify" it - self.nodelete = True - if (not curves) and (Draft.getType(self.sel[0]) == "Part"): - msg(translate("draft", "Found 1 non-parametric objects: draftifying it\n")) - Draft.draftify(self.sel[0]) - else: - msg(translate("draft", "No upgrade available for this object\n")) - self.doc.abortTransaction() - return - else: - msg(translate("draft", "Couldn't upgrade these objects\n")) - self.doc.abortTransaction() - return - - elif wires and (not faces) and (not openwires): - # we have only wires, no faces - - if (len(self.sel) == 1) and self.sel[0].isDerivedFrom("Sketcher::SketchObject") and (not curves): - # we have a sketch - msg(translate("draft", "Found 1 closed sketch object: making a face from it\n")) - newob = Draft.makeWire(self.sel[0].Shape,closed=True) - newob.Base = self.sel[0] - self.sel[0].ViewObject.Visibility = False - self.nodelete = True - - else: - # only closed wires - for w in wires: - if DraftGeomUtils.isPlanar(w): - f = Part.Face(w) - faces.append(f) - else: - msg(translate("draft", "One wire is not planar, upgrade not done\n")) - self.nodelete = True - for f in faces: - # if there are curved segments, we do a non-parametric face - msg(translate("draft", "Found a closed wire: making a face\n")) - newob = self.doc.addObject("Part::Feature","Face") - newob.Shape = f - Draft.formatObject(newob,lastob) - newob.ViewObject.DisplayMode = "Flat Lines" - - elif (len(openwires) == 1) and (not faces) and (not wires) and (not loneedges): - # special case, we have only one open wire. We close it, unless it has only 1 edge!" - p0 = openwires[0].Vertexes[0].Point - p1 = openwires[0].Vertexes[-1].Point - if p0 == p1: - # sometimes an open wire can have its start and end points identical (OCC bug) - # in that case, although it is not closed, face works... - f = Part.Face(openwires[0]) - msg(translate("draft", "Found a closed wire: making a face\n")) - newob = self.doc.addObject("Part::Feature","Face") - newob.Shape = f - Draft.formatObject(newob,lastob) - newob.ViewObject.DisplayMode = "Flat Lines" - else: - edges = openwires[0].Edges - if len(edges) > 1: - msg(translate("draft", "Found 1 open wire: closing it\n")) - edges.append(Part.Line(p1,p0).toShape()) - w = Part.Wire(DraftGeomUtils.sortEdges(edges)) - if not curves: - # only straight edges, we do a draft wire - newob = Draft.makeWire(w,closed=True) - else: - # if not possible, we do a non-parametric union - newob = self.doc.addObject("Part::Feature","Wire") - newob.Shape = w - Draft.formatObject(newob,lastob) - else: - if len(w.Vertexes) == 2: - msg(translate("draft", "Found 1 open edge: making a line\n")) - newob = Draft.makeWire(w,closed=False) - elif len(w.Vertexes) == 1: - msg(translate("draft", "Found 1 circular edge: making a circle\n")) - c = w.Edges[0].Curve.Center - r = w.Edges[0].Curve.Radius - p = FreeCAD.Placement() - p.move(c) - newob = Draft.makeCircle(r,p) - else: - msg(translate("draft", "Found 1 unknown edge. Aborting.\n")) - self.nodelete = True - elif openwires and (not wires) and (not faces): - # only open wires and edges: we try to join their edges - for ob in self.sel: - for e in ob.Shape.Edges: - edges.append(e) - newob = None - nedges = DraftGeomUtils.sortEdges(edges[:]) - #for e in nedges: print "debug: ",e.Curve,e.Vertexes[0].Point,e.Vertexes[-1].Point - try: - w = Part.Wire(nedges) - except: - msg(translate("draft", "Error: unable to join edges\n")) - else: - if len(w.Edges) == len(edges): - msg(translate("draft", "Found several edges: wiring them\n")) - newob = self.doc.addObject("Part::Feature","Wire") - newob.Shape = w - Draft.formatObject(newob,lastob) - if not newob: - if (len(self.sel) == 1) and (lastob.Shape.ShapeType == "Compound"): - # the selected object is already a compound - msg(translate("draft", "Unable to upgrade more\n")) - self.nodelete = True - else: - # all other cases - #print "no new object found" - msg(translate("draft", "Found several non-connected edges: making compound\n")) - newob = self.compound() - Draft.formatObject(newob,lastob) - else: - if (len(self.sel) == 1) and (lastob.Shape.ShapeType == "Compound"): - # the selected object is already a compound - msg(translate("draft", "Unable to upgrade more\n")) - self.nodelete = True - else: - # all other cases - msg(translate("draft", "Found several non-treatable objects: making compound\n")) - newob = self.compound() - Draft.formatObject(newob,lastob) - - if not self.nodelete: - # deleting original objects, if needed - for ob in self.sel: - if not ob.Type == "App::DocumentObjectGroup": - self.doc.removeObject(ob.Name) - - self.doc.commitTransaction() - if newob: Draft.select(newob) - Modifier.finish(self) + if self.call: + self.view.removeEventCallback("SoEvent",self.call) + if Draft.getSelection(): + self.commit(translate("draft","Upgrade"), + ['import Draft', + 'Draft.upgrade(FreeCADGui.Selection.getSelection(),delete=True)']) + self.finish() + - class Downgrade(Modifier): ''' The Draft_Downgrade FreeCAD command definition.