diff --git a/src/Mod/Arch/ArchFloor.py b/src/Mod/Arch/ArchFloor.py index 153a7a7a9..ee4200842 100644 --- a/src/Mod/Arch/ArchFloor.py +++ b/src/Mod/Arch/ArchFloor.py @@ -97,8 +97,10 @@ Floor creation aborted.\n" ) class _Floor: "The Floor object" def __init__(self,obj): - obj.addProperty("App::PropertyLength","Height","Arch","The height of this floor") - obj.addProperty("App::PropertyPlacement","Placement","Arch","The placement of this group") + obj.addProperty("App::PropertyLength","Height","Arch","The height of this object") + if not hasattr(obj,"Placement"): + # obj can be a Part Feature and already has a placement + obj.addProperty("App::PropertyPlacement","Placement","Arch","The placement of this object") self.Type = "Floor" obj.Proxy = self self.Object = obj diff --git a/src/Mod/Arch/ArchSite.py b/src/Mod/Arch/ArchSite.py index 1c1312a48..52565625c 100644 --- a/src/Mod/Arch/ArchSite.py +++ b/src/Mod/Arch/ArchSite.py @@ -36,10 +36,13 @@ __title__="FreeCAD Site" __author__ = "Yorik van Havre" __url__ = "http://www.freecadweb.org" + + def makeSite(objectslist=None,baseobj=None,name="Site"): '''makeBuilding(objectslist): creates a site including the objects from the given list.''' - obj = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroupPython",name) + import Part + obj = FreeCAD.ActiveDocument.addObject("Part::FeaturePython",name) obj.Label = translate("Arch",name) _Site(obj) if FreeCAD.GuiUp: @@ -50,6 +53,8 @@ def makeSite(objectslist=None,baseobj=None,name="Site"): obj.Terrain = baseobj return obj + + class _CommandSite: "the Arch Site command definition" def GetResources(self): @@ -96,11 +101,16 @@ Site creation aborted." ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() + + class _Site(ArchFloor._Floor): + "The Site object" + def __init__(self,obj): + ArchFloor._Floor.__init__(self,obj) - obj.addProperty("App::PropertyLink","Terrain","Arch","The terrain of this site") + obj.addProperty("App::PropertyLink","Terrain","Arch","The base terrain of this site") obj.addProperty("App::PropertyString","Address","Arch","The street and housenumber of this site") obj.addProperty("App::PropertyString","PostalCode","Arch","The postal or zip code of this site") obj.addProperty("App::PropertyString","City","Arch","The city of this site") @@ -108,11 +118,141 @@ class _Site(ArchFloor._Floor): obj.addProperty("App::PropertyFloat","Latitude","Arch","The latitude of this site") obj.addProperty("App::PropertyFloat","Longitude","Arch","The latitude of this site") obj.addProperty("App::PropertyString","Url","Arch","An url that shows this site in a mapping website") + obj.addProperty("App::PropertyLinkList","Group","Arch","The objects that are part of this site") + obj.addProperty("App::PropertyLinkList","Additions","Arch","Other shapes that are appended to this object") + obj.addProperty("App::PropertyLinkList","Subtractions","Arch","Other shapes that are subtracted from this object") + obj.addProperty("App::PropertyArea","ProjectedArea","Arch","The area of the projection of this object onto the XY plane") + obj.addProperty("App::PropertyLength","Perimeter","Arch","The perimeter length of this terrain") + obj.addProperty("App::PropertyVolume","AdditionVolume","Arch","The volume of earth to be added to this terrain") + obj.addProperty("App::PropertyVolume","SubtractionVolume","Arch","The volume of earth to be removed from this terrain") + obj.addProperty("App::PropertyVector","ExtrusionVector","Arch","An extrusion vector to use when performing boolean operations") + obj.addProperty("App::PropertyBool","RemoveSplitter","Arch","Remove splitters from the resulting shape") self.Type = "Site" obj.setEditorMode('Height',2) + obj.ExtrusionVector = FreeCAD.Vector(0,0,-100000) + + def execute(self,obj): + + ArchFloor._Floor.execute(self,obj) + + if not obj.isDerivedFrom("Part::Feature"): # old-style Site + return + + pl = obj.Placement + shape = None + if obj.Terrain: + if obj.Terrain.isDerivedFrom("Part::Feature"): + if obj.Terrain.Shape: + if not obj.Terrain.Shape.isNull(): + shape = obj.Terrain.Shape.copy() + if shape: + shells = [] + for sub in obj.Subtractions: + if sub.isDerivedFrom("Part::Feature"): + if sub.Shape: + if sub.Shape.Solids: + for sol in sub.Shape.Solids: + rest = shape.cut(sol) + shells.append(sol.Shells[0].common(shape.extrude(obj.ExtrusionVector))) + shape = rest + for sub in obj.Additions: + if sub.isDerivedFrom("Part::Feature"): + if sub.Shape: + if sub.Shape.Solids: + for sol in sub.Shape.Solids: + rest = shape.cut(sol) + shells.append(sol.Shells[0].cut(shape.extrude(obj.ExtrusionVector))) + shape = rest + if not shape.isNull(): + if shape.isValid(): + for shell in shells: + shape = shape.fuse(shell) + if obj.RemoveSplitter: + shape = shape.removeSplitter() + obj.Shape = shape + if not pl.isNull(): + obj.Placement = pl + self.computeAreas(obj) + + def onChanged(self,obj,prop): + + ArchFloor._Floor.onChanged(self,obj,prop) + if prop == "Terrain": + if obj.Terrain: + if FreeCAD.GuiUp: + obj.Terrain.ViewObject.hide() + self.execute(obj) + + def computeAreas(self,obj): + + if not obj.Shape: + return + if obj.Shape.isNull(): + return + if not obj.Shape.isValid(): + return + if not obj.Shape.Faces: + return + if not hasattr(obj,"Perimeter"): # check we have a latest version site + return + if not obj.Terrain: + return + # compute area + fset = [] + for f in obj.Shape.Faces: + if f.normalAt(0,0).getAngle(FreeCAD.Vector(0,0,1)) < 1.5707: + fset.append(f) + if fset: + import Drawing,Part + pset = [] + for f in fset: + try: + pf = Part.Face(Part.Wire(Drawing.project(f,FreeCAD.Vector(0,0,1))[0].Edges)) + except Part.OCCError: + # error in computing the area. Better set it to zero than show a wrong value + if obj.ProjectedArea.Value != 0: + print "Error computing areas for ",obj.Label + obj.ProjectedArea = 0 + else: + pset.append(pf) + if pset: + self.flatarea = pset.pop() + for f in pset: + self.flatarea = self.flatarea.fuse(f) + self.flatarea = self.flatarea.removeSplitter() + if obj.ProjectedArea.Value != self.flatarea.Area: + obj.ProjectedArea = self.flatarea.Area + # compute perimeter + lut = {} + for e in obj.Shape.Edges: + lut.setdefault(e.hashCode(),[]).append(e) + l = 0 + for e in lut.values(): + if len(e) == 1: # keep only border edges + l += e[0].Length + if l: + if obj.Perimeter.Value != l: + obj.Perimeter = l + # compute volumes + shapesolid = obj.Terrain.Shape.extrude(obj.ExtrusionVector) + addvol = 0 + subvol = 0 + for sub in obj.Subtractions: + subvol += sub.Shape.common(shapesolid).Volume + for sub in obj.Additions: + addvol += sub.Shape.cut(shapesolid).Volume + if obj.SubtractionVolume.Value != subvol: + obj.SubtractionVolume = subvol + if obj.AdditionVolume.Value != addvol: + obj.AdditionVolume = addvol + + + class _ViewProviderSite(ArchFloor._ViewProviderFloor): + "A View Provider for the Site object" + def __init__(self,vobj): ArchFloor._ViewProviderFloor.__init__(self,vobj) @@ -121,7 +261,28 @@ class _ViewProviderSite(ArchFloor._ViewProviderFloor): return ":/icons/Arch_Site_Tree.svg" def claimChildren(self): - return self.Object.Group+[self.Object.Terrain] + objs = self.Object.Group+[self.Object.Terrain] + prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch") + if hasattr(self.Object,"Additions") and prefs.GetBool("swallowAdditions",True): + objs.extend(self.Object.Additions) + if hasattr(self.Object,"Subtractions") and prefs.GetBool("swallowSubtractions",True): + objs.extend(self.Object.Subtractions) + return objs + + def setEdit(self,vobj,mode): + if mode == 0: + import ArchComponent + taskd = ArchComponent.ComponentTaskPanel() + taskd.obj = self.Object + taskd.update() + FreeCADGui.Control.showDialog(taskd) + return True + return False + + def unsetEdit(self,vobj,mode): + FreeCADGui.Control.closeDialog() + return False + if FreeCAD.GuiUp: FreeCADGui.addCommand('Arch_Site',_CommandSite())