ArchSpace - Correct computed dimensions (floor area and perimeter length) (#14211)

* Arch: Improved ArchComponent.computeAreas function

All non vertical faces get projected now. Changed Part.Wire to DraftGeomUtils.findWires

* Arch: Removed Area property and add AreaCalculationType to Space object

The horizontal area and its perimeter are now calculated in two different ways. Eiter at the centre of mass or as a projection on the XY-plane. The results are filled in the filed horizontal area and perimeter in the Component section.

* BIM: Added property Area for Spaces again
This commit is contained in:
DeniseBryson
2024-06-10 18:22:51 +02:00
committed by GitHub
parent b9dfc493b8
commit 5b0eccac07
2 changed files with 39 additions and 34 deletions

View File

@@ -994,6 +994,8 @@ class Component(ArchIFC.IfcProduct):
import Part
import TechDraw
import DraftGeomUtils
fmax = params.get_param_arch("MaxComputeAreas")
if len(obj.Shape.Faces) > fmax:
obj.VerticalArea = 0
@@ -1015,7 +1017,7 @@ class Component(ArchIFC.IfcProduct):
else:
if (ang > 1.57) and (ang < 1.571):
a += f.Area
if ang < 1.5707:
else:
fset.append(f)
if a and hasattr(obj,"VerticalArea"):
@@ -1024,22 +1026,19 @@ class Component(ArchIFC.IfcProduct):
if fset and hasattr(obj,"HorizontalArea"):
pset = []
for f in fset:
if f.normalAt(0,0).getAngle(FreeCAD.Vector(0,0,1)) < 0.00001:
# already horizontal
pset.append(f)
try:
pf = Part.Face(DraftGeomUtils.findWires(TechDraw.project(f,FreeCAD.Vector(0,0,1))[0].Edges))
except Part.OCCError:
# error in computing the areas. Better set them to zero than show a wrong value
if obj.HorizontalArea.Value != 0:
print("Debug: Error computing areas for ",obj.Label,": unable to project face: ",str([v.Point for v in f.Vertexes])," (face normal:",f.normalAt(0,0),")")
obj.HorizontalArea = 0
if hasattr(obj,"PerimeterLength"):
if obj.PerimeterLength.Value != 0:
obj.PerimeterLength = 0
else:
try:
pf = Part.Face(Part.Wire(TechDraw.project(f,FreeCAD.Vector(0,0,1))[0].Edges))
except Part.OCCError:
# error in computing the areas. Better set them to zero than show a wrong value
if obj.HorizontalArea.Value != 0:
print("Debug: Error computing areas for ",obj.Label,": unable to project face: ",str([v.Point for v in f.Vertexes])," (face normal:",f.normalAt(0,0),")")
obj.HorizontalArea = 0
if hasattr(obj,"PerimeterLength"):
if obj.PerimeterLength.Value != 0:
obj.PerimeterLength = 0
else:
pset.append(pf)
pset.append(pf)
if pset:
self.flatarea = pset.pop()
@@ -1049,8 +1048,11 @@ class Component(ArchIFC.IfcProduct):
if obj.HorizontalArea.Value != self.flatarea.Area:
obj.HorizontalArea = self.flatarea.Area
if hasattr(obj,"PerimeterLength") and (len(self.flatarea.Faces) == 1):
if obj.PerimeterLength.Value != self.flatarea.Faces[0].OuterWire.Length:
obj.PerimeterLength = self.flatarea.Faces[0].OuterWire.Length
edges_table = {}
for e in self.flatarea.Edges:
edges_table.setdefault(e.hashCode(),[]).append(e)
border_edges = [pair[0] for pair in edges_table.values() if len(pair) == 1]
obj.PerimeterLength = sum([e.Length for e in border_edges])
def isStandardCase(self,obj):
"""Determine if the component is a standard case of its IFC type.

View File

@@ -145,6 +145,12 @@ ConditioningTypes = [
"NaturallyVentedOnly"
]
AreaCalculationType = [
"XY-plane projection",
"At Center of Mass"
]
import FreeCAD
import ArchComponent
import ArchCommands
@@ -213,7 +219,7 @@ class _Space(ArchComponent.Component):
if not "Boundaries" in pl:
obj.addProperty("App::PropertyLinkSubList","Boundaries", "Space",QT_TRANSLATE_NOOP("App::Property","The objects that make the boundaries of this space object"))
if not "Area" in pl:
obj.addProperty("App::PropertyArea", "Area", "Space",QT_TRANSLATE_NOOP("App::Property","The computed floor area of this space"))
obj.addProperty("App::PropertyArea", "Area", "Space",QT_TRANSLATE_NOOP("App::Property","Identical to Horizontal Area"))
if not "FinishFloor" in pl:
obj.addProperty("App::PropertyString", "FinishFloor", "Space",QT_TRANSLATE_NOOP("App::Property","The finishing of the floor of this space"))
if not "FinishWalls" in pl:
@@ -241,8 +247,10 @@ class _Space(ArchComponent.Component):
if not "Internal" in pl:
obj.addProperty("App::PropertyBool", "Internal", "Space",QT_TRANSLATE_NOOP("App::Property","Specifies if this space is internal or external"))
obj.Internal = True
if not "AreaCalculationType" in pl:
obj.addProperty("App::PropertyEnumeration", "AreaCalculationType", "Space",QT_TRANSLATE_NOOP("App::Property","Defines the calculation type for the horizontal area and its perimeter length"))
obj.AreaCalculationType = AreaCalculationType
self.Type = "Space"
obj.setEditorMode("HorizontalArea",2)
def onDocumentRestored(self,obj):
@@ -374,10 +382,14 @@ class _Space(ArchComponent.Component):
#print("setting objects shape")
shape = shape.Solids[0]
self.applyShape(obj,shape,pl)
if hasattr(obj.Area,"Value"):
a = self.getArea(obj)
if obj.Area.Value != a:
obj.Area = a
if hasattr(obj.HorizontalArea,"Value"):
if hasattr(obj,"AreaCalculationType"):
if obj.AreaCalculationType == "At Center of Mass":
a = self.getArea(obj)
obj.HorizontalArea = a
if hasattr(obj,"Area"):
obj.Area = obj.HorizontalArea
return
print("Arch: error computing space boundary for",obj.Label)
@@ -392,22 +404,13 @@ class _Space(ArchComponent.Component):
if hasattr(obj,"PerimeterLength"):
if self.face.OuterWire.Length != obj.PerimeterLength.Value:
obj.PerimeterLength = self.face.OuterWire.Length
if hasattr(obj,"VerticalArea"):
a = 0
for f in obj.Shape.Faces:
ang = f.normalAt(0,0).getAngle(FreeCAD.Vector(0,0,1))
if (ang > 1.57) and (ang < 1.571):
a += f.Area
if a != obj.VerticalArea.Value:
obj.VerticalArea = a
#print "area of ",obj.Label," : ",f.Area
return self.face.Area
else:
return 0
def getFootprint(self,obj):
"returns a face that represents the footprint of this space"
"returns a face that represents the footprint of this space at the center of mass"
import Part
import DraftGeomUtils