#*************************************************************************** #* * #* Copyright (c) 2011 * #* Yorik van Havre * #* * #* 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 * #* * #*************************************************************************** import FreeCAD,FreeCADGui,Draft,ArchComponent from draftlibs import fcvec from FreeCAD import Vector from PyQt4 import QtCore __title__="FreeCAD Arch Commands" __author__ = "Yorik van Havre" __url__ = "http://free-cad.sourceforge.net" # module functions ############################################### def addComponents(objectsList,host): '''addComponents(objectsList,hostObject): adds the given object or the objects from the given list as components to the given host Object. Use this for example to add windows to a wall, or to add walls to a cell or floor.''' if not isinstance(objectsList,list): objectsList = [objectsList] tp = Draft.getType(host) if tp in ["Cell","Floor","Building","Site"]: c = host.Components for o in objectsList: if not o in c: c.append(o) host.Components = c elif tp in ["Wall","Structure"]: a = host.Additions for o in objectsList: if not o in a: if hasattr(o,"Shape"): a.append(o) host.Additions = a elif tp in ["SectionPlane"]: a = host.Objects for o in objectsList: if not o in a: if hasattr(o,"Shape"): a.append(o) host.Objects = a def removeComponents(objectsList,host=None): '''removeComponents(objectsList,[hostObject]): removes the given component or the components from the given list from their parents. If a host object is specified, this function will try adding the components as holes to the host object instead.''' if not isinstance(objectsList,list): objectsList = [objectsList] if host: if Draft.getType(host) in ["Wall","Structure"]: s = host.Subtractions for o in objectsList: if not o in s: s.append(o) host.Subtractions = s else: for o in objectsList: if o.InList: h = o.InList[0] tp = Draft.getType(h) if tp in ["Cell","Floor","Building","Site"]: c = h.Components if o in c: c.remove(o) h.Components = c o.ViewObject.show() elif tp in ["Wall","Structure"]: a = h.Additions s = h.Subtractions if o in a: a.remove(o) h.Additions = a o.ViewObject.show() elif o in s: s.remove(o) h.Subtractions = s o.ViewObject.show() elif tp in ["SectionPlane"]: a = h.Objects if o in a: a.remove(o) h.Objects = a def copyProperties(obj1,obj2): '''copyProperties(obj1,obj2): Copies properties values from obj1 to obj2, when that property exists in both objects''' for prop in obj1.PropertiesList: if prop in obj2.PropertiesList: if not prop in ["Proxy","Shape"]: setattr(obj2,prop,getattr(obj1,prop)) if obj1.ViewObject and obj2.ViewObject: for prop in obj1.ViewObject.PropertiesList: if prop in obj2.ViewObject.PropertiesList: if not prop in ["Proxy","Shape"]: setattr(obj2.ViewObject,prop,getattr(obj1.ViewObject,prop)) def splitMesh(obj,mark=True): '''splitMesh(object,[mark]): splits the given mesh object into separated components. If mark is False, nothing else is done. If True (default), non-manifold components will be painted in red.''' if not obj.isDerivedFrom("Mesh::Feature"): return [] basemesh = obj.Mesh comps = basemesh.getSeparateComponents() nlist = [] if comps: basename = obj.Name FreeCAD.ActiveDocument.removeObject(basename) for c in comps: newobj = FreeCAD.ActiveDocument.addObject("Mesh::Feature",basename) newobj.Mesh = c if mark and (not(c.isSolid()) or c.hasNonManifolds()): newobj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) nlist.append(newobj) return nlist return [obj] def meshToShape(obj,mark=True): '''meshToShape(object,[mark]): turns a mesh into a shape, joining coplanar facets. If mark is True (default), non-solid objects will be marked in red''' name = obj.Name import Part,MeshPart from draftlibs import fcgeo if "Mesh" in obj.PropertiesList: faces = [] mesh = obj.Mesh plac = obj.Placement segments = mesh.getPlanes(0.001) # use rather strict tolerance here print len(segments)," segments ",segments for i in segments: print "treating",segments.index(i),i if len(i) > 0: wires = MeshPart.wireFromSegment(mesh, i) print "wire done" print wires if len(wires) > 1: # a segment can have inner holes print "inner wires found" ext = None max_length = 0 # cleaning up rubbish in wires for i in range(len(wires)): wires[i] = fcgeo.removeInterVertices(wires[i]) for w in wires: # we assume that the exterior boundary is that one with # the biggest bounding box if w.BoundBox.DiagonalLength > max_length: max_length = w.BoundBox.DiagonalLength ext = w print "exterior wire",ext wires.remove(ext) # all interior wires mark a hole and must reverse # their orientation, otherwise Part.Face fails for w in wires: print "reversing",w #w.reverse() print "reversed" # make sure that the exterior wires comes as first in the list wires.insert(0, ext) print "done sorting", wires if wires: faces.append(Part.Face(wires)) print "done facing" print "faces",faces try: se = Part.makeShell(faces) solid = Part.Solid(se) except: pass else: if solid.isClosed(): FreeCAD.ActiveDocument.removeObject(name) else: if mark: newobj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) newobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) newobj.Shape = solid newobj.Placement = plac return newobj return None def removeShape(objs,mark=True): '''takes an arch object (wall or structure) built on a cubic shape, and removes the inner shape, keeping its length, width and height as parameters.''' from draftlibs import fcgeo if not isinstance(objs,list): objs = [objs] for obj in objs: if fcgeo.isCubic(obj.Shape): dims = fcgeo.getCubicDimensions(obj.Shape) if dims: name = obj.Name tp = Draft.getType(obj) print tp if tp == "Structure": FreeCAD.ActiveDocument.removeObject(name) import ArchStructure str = ArchStructure.makeStructure(length=dims[1],width=dims[2],height=dims[3],name=name) str.Placement = dims[0] elif tp == "Wall": FreeCAD.ActiveDocument.removeObject(name) import ArchWall length = dims[1] width = dims[2] v1 = Vector(length/2,0,0) v2 = fcvec.neg(v1) v1 = dims[0].multVec(v1) v2 = dims[0].multVec(v2) line = Draft.makeLine(v1,v2) wal = ArchWall.makeWall(line,width=width,height=dims[3],name=name) else: if mark: obj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) def mergeCells(objectslist): '''mergeCells(objectslist): merges the objects in the given list into one. All objects must be of the same type and based on the Cell object (cells, floors, buildings, or sites).''' if not objectslist: return None if not isinstance(objectslist,list): return None if len(objectslist) < 2: return None typ = Draft.getType(objectslist[0]) if not(typ in ["Cell","Floor","Building","Site"]): return None for o in objectslist: if Draft.getType(o) != typ: return None base = objectslist.pop(0) for o in objectslist: l = base.Components for c in o.Components: if not c in l: l.append(c) base.Components = l FreeCAD.ActiveDocument.removeObject(o.Name) FreeCAD.ActiveDocument.recompute() return base # command definitions ############################################### class _CommandAdd: "the Arch Add command definition" def GetResources(self): return {'Pixmap' : 'Arch_Add', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Add","Add component"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Add","Adds the selected components to the active object")} def IsActive(self): if len(FreeCADGui.Selection.getSelection()) > 1: return True else: return False def Activated(self): sel = FreeCADGui.Selection.getSelection() FreeCAD.ActiveDocument.openTransaction("Grouping") if not mergeCells(sel): host = sel.pop() addComponents(sel,host) FreeCAD.ActiveDocument.commitTransaction() class _CommandRemove: "the Arch Add command definition" def GetResources(self): return {'Pixmap' : 'Arch_Remove', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_Remove","Remove component"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_Remove","Remove the selected components from their parents, or create a hole in a component")} def IsActive(self): if FreeCADGui.Selection.getSelection(): return True else: return False def Activated(self): sel = FreeCADGui.Selection.getSelection() FreeCAD.ActiveDocument.openTransaction("Ungrouping") if Draft.getType(sel[-1]) in ["Wall","Structure"]: host = sel.pop() removeComponents(sel,host) else: removeComponents(sel) FreeCAD.ActiveDocument.commitTransaction() class _CommandSplitMesh: "the Arch SplitMesh command definition" def GetResources(self): return {'Pixmap' : 'Arch_SplitMesh', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_SplitMesh","Split Mesh"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_SplitMesh","Splits selected meshes into independent components")} def IsActive(self): if len(FreeCADGui.Selection.getSelection()): return True else: return False def Activated(self): if FreeCADGui.Selection.getSelection(): sel = FreeCADGui.Selection.getSelection() FreeCAD.ActiveDocument.openTransaction("Split Mesh") for obj in sel: n = obj.Name nobjs = splitMesh(obj) if len(nobjs) > 1: g = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup",n) for o in nobjs: g.addObject(o) FreeCAD.ActiveDocument.commitTransaction() class _CommandMeshToShape: "the Arch MeshToShape command definition" def GetResources(self): return {'Pixmap' : 'Arch_MeshToShape', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_MeshToShape","Mesh to Shape"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_MeshToPart","Turns selected meshes into Part Shape objects")} def IsActive(self): if FreeCADGui.Selection.getSelection(): return True else: return False def Activated(self): if FreeCADGui.Selection.getSelection(): f = FreeCADGui.Selection.getSelection()[0] g = None if f.isDerivedFrom("App::DocumentObjectGroup"): g = f FreeCADGui.Selection.clearSelection() for o in f.OutList: FreeCADGui.Selection.addSelection(o) else: if f.InList: if f.InList[0].isDerivedFrom("App::DocumentObjectGroup"): g = f.InList[0] FreeCAD.ActiveDocument.openTransaction("Mesh to Shape") for obj in FreeCADGui.Selection.getSelection(): newobj = meshToShape(obj) if g and newobj: g.addObject(newobj) FreeCAD.ActiveDocument.commitTransaction() class _CommandSelectNonSolidMeshes: "the Arch SelectNonSolidMeshes command definition" def GetResources(self): return {'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_SelectNonSolidMeshes","Select non-manifold meshes"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_SelectNonSolidMeshes","Selects all non-manifold meshes from the document or from the selected groups")} def Activated(self): msel = [] if FreeCADGui.Selection.getSelection(): for o in FreeCADGui.Selection.getSelection(): if o.isDerivedFrom("App::DocumentObjectGroup"): msel.extend(o.OutList) if not msel: msel = FreeCAD.ActiveDocument.Objects sel = [] for o in msel: if o.isDerivedFrom("Mesh::Feature"): if (not o.Mesh.isSolid()) or o.Mesh.hasNonManifolds(): sel.append(o) if sel: FreeCADGui.Selection.clearSelection() for o in sel: FreeCADGui.Selection.addSelection(o) class _CommandRemoveShape: "the Arch RemoveShape command definition" def GetResources(self): return {'Pixmap' : 'Arch_RemoveShape', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Arch_RemoveShape","Remove Shape from Arch"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Arch_RemoveShape","Removes cubic shapes from Arch components")} def IsActive(self): if FreeCADGui.Selection.getSelection(): return True else: return False def Activated(self): sel = FreeCADGui.Selection.getSelection() removeShape(sel) FreeCADGui.addCommand('Arch_Add',_CommandAdd()) FreeCADGui.addCommand('Arch_Remove',_CommandRemove()) FreeCADGui.addCommand('Arch_SplitMesh',_CommandSplitMesh()) FreeCADGui.addCommand('Arch_MeshToShape',_CommandMeshToShape()) FreeCADGui.addCommand('Arch_SelectNonSolidMeshes',_CommandSelectNonSolidMeshes()) FreeCADGui.addCommand('Arch_RemoveShape',_CommandRemoveShape())