#*************************************************************************** #* * #* 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 General Public License (GPL) * #* 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,Part,Draft,MeshPart,Component from draftlibs import fcgeo,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 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''' 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 faces.append(Part.Face(wires)) print "done facing" print "faces",faces shell=Part.Compound(faces) solid = Part.Solid(Part.Shell(faces)) name = obj.Name if solid.isClosed(): FreeCAD.ActiveDocument.removeObject(name) newobj = FreeCAD.ActiveDocument.addObject("Part::Feature",name) newobj.Shape = solid newobj.Placement = plac if not solid.isClosed(): newobj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) 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.''' 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 Structure str = Structure.makeStructure(length=dims[1],width=dims[2],height=dims[3],name=name) str.Placement = dims[0] elif tp == "Wall": FreeCAD.ActiveDocument.removeObject(name) import Wall 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 = Wall.makeWall(line,width=width,height=dims[3],name=name) else: if mark: obj.ViewObject.ShapeColor = (1.0,0.0,0.0,1.0) # 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() host = sel.pop() FreeCAD.ActiveDocument.openTransaction("Grouping") 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())