Arch: Support of shared profiles in extrusions imported from IFC
This commit is contained in:
@@ -382,6 +382,8 @@ class Component:
|
||||
if hasattr(obj.Base,"LengthFwd"):
|
||||
if obj.Base.LengthFwd.Value:
|
||||
extrusion = extrusion.multiply(obj.Base.LengthFwd.Value)
|
||||
if not self.isIdentity(obj.Base.Placement):
|
||||
placement = placement.multiply(obj.Base.Placement)
|
||||
return (base,extrusion,placement)
|
||||
elif obj.Base.isDerivedFrom("Part::MultiFuse"):
|
||||
rshapes = []
|
||||
@@ -421,6 +423,7 @@ class Component:
|
||||
"""returns a shape that is a copy of the original shape
|
||||
but centered on the (0,0) origin, and a placement that is needed to
|
||||
reposition that shape to its original location/orientation"""
|
||||
|
||||
import DraftGeomUtils,math
|
||||
if not isinstance(shape,list):
|
||||
shape = [shape]
|
||||
@@ -473,7 +476,7 @@ class Component:
|
||||
#print("Processing subshapes of ",obj.Label, " : ",obj.Additions)
|
||||
|
||||
if placement:
|
||||
if placement.isIdentity():
|
||||
if self.isIdentity(placement):
|
||||
placement = None
|
||||
else:
|
||||
placement = FreeCAD.Placement(placement)
|
||||
@@ -570,6 +573,7 @@ class Component:
|
||||
def spread(self,obj,shape,placement=None):
|
||||
|
||||
"spreads this shape along axis positions"
|
||||
|
||||
points = None
|
||||
if hasattr(obj,"Axis"):
|
||||
if obj.Axis:
|
||||
@@ -589,6 +593,14 @@ class Component:
|
||||
shape = Part.makeCompound(shps)
|
||||
return shape
|
||||
|
||||
def isIdentity(self,placement):
|
||||
|
||||
"checks if a placement is *almost* zero"
|
||||
|
||||
if (placement.Base.Length < 0.000001) and (placement.Rotation.Angle < 0.000001):
|
||||
return True
|
||||
return False
|
||||
|
||||
def applyShape(self,obj,shape,placement,allowinvalid=False,allownosolid=False):
|
||||
|
||||
"checks and cleans the given shape, and apply it to the object"
|
||||
@@ -608,20 +620,23 @@ class Component:
|
||||
pass
|
||||
else:
|
||||
shape = r
|
||||
p = self.spread(obj,shape,placement).Placement.copy() # for some reason this gets zeroed in next line
|
||||
obj.Shape = self.spread(obj,shape,placement)
|
||||
if not placement.isIdentity():
|
||||
if not self.isIdentity(placement):
|
||||
obj.Placement = placement
|
||||
else:
|
||||
obj.Placement = p
|
||||
else:
|
||||
if allownosolid:
|
||||
obj.Shape = self.spread(obj,shape,placement)
|
||||
if not placement.isIdentity():
|
||||
if not self.isIdentity(placement):
|
||||
obj.Placement = placement
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch","has no solid")+"\n")
|
||||
else:
|
||||
if allowinvalid:
|
||||
obj.Shape = self.spread(obj,shape,placement)
|
||||
if not placement.isIdentity():
|
||||
if not self.isIdentity(placement):
|
||||
obj.Placement = placement
|
||||
else:
|
||||
FreeCAD.Console.PrintWarning(obj.Label + " " + translate("Arch","has an invalid shape")+"\n")
|
||||
@@ -1157,7 +1172,7 @@ class ComponentTaskPanel:
|
||||
self.ifcButton.setText(QtGui.QApplication.translate("Arch", "Edit IFC properties", None))
|
||||
|
||||
def editIfcProperties(self):
|
||||
|
||||
|
||||
if hasattr(self,"ifcEditor"):
|
||||
if self.ifcEditor:
|
||||
self.ifcEditor.hide()
|
||||
@@ -1169,7 +1184,7 @@ class ComponentTaskPanel:
|
||||
if not isinstance(self.obj.IfcProperties,dict):
|
||||
return
|
||||
import Arch_rc,csv,os
|
||||
|
||||
|
||||
# get presets
|
||||
self.ptypes = SimplePropertyTypes + MeasurePropertyTypes
|
||||
self.plabels = [''.join(map(lambda x: x if x.islower() else " "+x, t[3:]))[1:] for t in self.ptypes]
|
||||
@@ -1180,7 +1195,7 @@ class ComponentTaskPanel:
|
||||
reader = csv.reader(csvfile, delimiter=';')
|
||||
for row in reader:
|
||||
self.psetdefs[row[0]] = row[1:]
|
||||
self.psetkeys = [''.join(map(lambda x: x if x.islower() else " "+x, t[5:]))[1:] for t in self.psetdefs.keys()]
|
||||
self.psetkeys = [''.join(map(lambda x: x if x.islower() else " "+x, t[5:]))[1:] for t in self.psetdefs.keys()]
|
||||
self.psetkeys.sort()
|
||||
self.ifcEditor = FreeCADGui.PySideUic.loadUi(":/ui/DialogIfcProperties.ui")
|
||||
# center the dialog over FreeCAD window
|
||||
@@ -1253,7 +1268,7 @@ class ComponentTaskPanel:
|
||||
self.ifcEditor.show()
|
||||
|
||||
def acceptIfcProperties(self):
|
||||
|
||||
|
||||
if hasattr(self,"ifcEditor") and self.ifcEditor:
|
||||
self.ifcEditor.hide()
|
||||
ifcdict = {}
|
||||
@@ -1285,7 +1300,7 @@ class ComponentTaskPanel:
|
||||
del self.ifcEditor
|
||||
|
||||
def addIfcProperty(self,idx=0,pset=None,prop=None,ptype=None):
|
||||
|
||||
|
||||
if hasattr(self,"ifcEditor") and self.ifcEditor:
|
||||
if not pset:
|
||||
sel = self.ifcEditor.treeProperties.selectedIndexes()
|
||||
@@ -1311,9 +1326,9 @@ class ComponentTaskPanel:
|
||||
pset.appendRow([it1,it2,it3])
|
||||
if idx != 0:
|
||||
self.ifcEditor.comboProperty.setCurrentIndex(0)
|
||||
|
||||
|
||||
def addIfcPset(self,idx=0):
|
||||
|
||||
|
||||
if hasattr(self,"ifcEditor") and self.ifcEditor:
|
||||
if idx == 1:
|
||||
top = QtGui.QStandardItem(QtGui.QApplication.translate("Arch", "New property set", None))
|
||||
@@ -1338,7 +1353,7 @@ class ComponentTaskPanel:
|
||||
self.ifcEditor.treeProperties.setFirstColumnSpanned(i, idx, True)
|
||||
self.ifcEditor.treeProperties.expandAll()
|
||||
self.ifcEditor.comboPset.setCurrentIndex(0)
|
||||
|
||||
|
||||
def removeIfcProperty(self):
|
||||
|
||||
if hasattr(self,"ifcEditor") and self.ifcEditor:
|
||||
@@ -1353,17 +1368,17 @@ class ComponentTaskPanel:
|
||||
if FreeCAD.GuiUp:
|
||||
|
||||
class IfcEditorDelegate(QtGui.QStyledItemDelegate):
|
||||
|
||||
|
||||
|
||||
|
||||
def __init__(self, parent=None, dialog=None, ptypes=[], plabels=[], *args):
|
||||
|
||||
|
||||
self.dialog = dialog
|
||||
QtGui.QStyledItemDelegate.__init__(self, parent, *args)
|
||||
self.ptypes = ptypes
|
||||
self.plabels = plabels
|
||||
|
||||
|
||||
def createEditor(self,parent,option,index):
|
||||
|
||||
|
||||
if index.column() == 0: # property name
|
||||
editor = QtGui.QLineEdit(parent)
|
||||
elif index.column() == 1: # property type
|
||||
@@ -1385,9 +1400,9 @@ if FreeCAD.GuiUp:
|
||||
editor = QtGui.QLineEdit(parent)
|
||||
editor.setObjectName("editor_"+ptype)
|
||||
return editor
|
||||
|
||||
|
||||
def setEditorData(self, editor, index):
|
||||
|
||||
|
||||
if index.column() == 0:
|
||||
editor.setText(index.data())
|
||||
elif index.column() == 1:
|
||||
@@ -1418,9 +1433,9 @@ if FreeCAD.GuiUp:
|
||||
editor.setValue(0)
|
||||
else:
|
||||
editor.setText(index.data())
|
||||
|
||||
|
||||
def setModelData(self, editor, model, index):
|
||||
|
||||
|
||||
if index.column() == 0:
|
||||
model.setData(index,editor.text())
|
||||
elif index.column() == 1:
|
||||
@@ -1442,6 +1457,6 @@ if FreeCAD.GuiUp:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -385,6 +385,7 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
openings = ifcfile.by_type("IfcOpeningElement")
|
||||
annotations = ifcfile.by_type("IfcAnnotation")
|
||||
materials = ifcfile.by_type("IfcMaterial")
|
||||
profiles = {} # to store reused extrusion profiles {ifcid:fcobj,...}
|
||||
|
||||
if DEBUG: print("Building relationships table...",end="")
|
||||
|
||||
@@ -618,23 +619,48 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if GET_EXTRUSIONS:
|
||||
ex = Arch.getExtrusionData(shape)
|
||||
if ex:
|
||||
print("extrusion ",end="")
|
||||
baseface = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_footprint")
|
||||
# bug in ifcopenshell? Some faces of a shell may have non-null placement
|
||||
# workaround to remove the bad placement: exporting/reimporting as step
|
||||
if not ex[0].Placement.isNull():
|
||||
import tempfile
|
||||
fd, tf = tempfile.mkstemp(suffix=".stp")
|
||||
ex[0].exportStep(tf)
|
||||
f = Part.read(tf)
|
||||
os.close(fd)
|
||||
os.remove(tf)
|
||||
else:
|
||||
f = ex[0]
|
||||
baseface.Shape = f
|
||||
# check for extrusion profile
|
||||
baseface = None
|
||||
profileid = None
|
||||
addplacement = None
|
||||
if product.Representation:
|
||||
if product.Representation.Representations:
|
||||
if product.Representation.Representations[0].is_a("IfcShapeRepresentation"):
|
||||
if product.Representation.Representations[0].Items:
|
||||
if product.Representation.Representations[0].Items[0].is_a("IfcExtrudedAreaSolid"):
|
||||
profileid = product.Representation.Representations[0].Items[0].SweptArea.id()
|
||||
if profileid and profileid in profiles:
|
||||
# reuse existing profile
|
||||
print("shared extrusion ",end="")
|
||||
baseface = profiles[profileid]
|
||||
addplacement = FreeCAD.Placement()
|
||||
addplacement.Rotation = FreeCAD.Rotation(baseface.Shape.Faces[0].normalAt(0,0),ex[0].Faces[0].normalAt(0,0))
|
||||
addplacement.Base = addplacement.Rotation.multVec(ex[0].CenterOfMass.sub(baseface.Shape.CenterOfMass))
|
||||
if not baseface:
|
||||
print("extrusion ",end="")
|
||||
baseface = FreeCAD.ActiveDocument.addObject("Part::Feature",name+"_footprint")
|
||||
# bug in ifcopenshell? Some faces of a shell may have non-null placement
|
||||
# workaround to remove the bad placement: exporting/reimporting as step
|
||||
if not ex[0].Placement.isNull():
|
||||
import tempfile
|
||||
fd, tf = tempfile.mkstemp(suffix=".stp")
|
||||
ex[0].exportStep(tf)
|
||||
f = Part.read(tf)
|
||||
os.close(fd)
|
||||
os.remove(tf)
|
||||
else:
|
||||
f = ex[0]
|
||||
baseface.Shape = f
|
||||
if profileid:
|
||||
profiles[profileid] = baseface
|
||||
baseobj = FreeCAD.ActiveDocument.addObject("Part::Extrusion",name+"_body")
|
||||
baseobj.Base = baseface
|
||||
baseobj.Dir = ex[1]
|
||||
if addplacement:
|
||||
baseobj.Placement.Rotation = addplacement.Rotation
|
||||
baseobj.Placement.move(addplacement.Base)
|
||||
baseobj.Dir = addplacement.Rotation.inverted().multVec(ex[1])
|
||||
else:
|
||||
baseobj.Dir = ex[1]
|
||||
if FreeCAD.GuiUp:
|
||||
baseface.ViewObject.hide()
|
||||
if (not baseobj):
|
||||
@@ -694,13 +720,15 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if FreeCAD.GuiUp and baseobj:
|
||||
if hasattr(baseobj,"ViewObject"):
|
||||
baseobj.ViewObject.hide()
|
||||
if ptype == "IfcBuildingStorey": obj.Placement.Base.z = product.Elevation*1000
|
||||
if ptype == "IfcBuildingStorey":
|
||||
if product.Elevation:
|
||||
obj.Placement.Base.z = product.Elevation * 1000
|
||||
|
||||
# setting role
|
||||
|
||||
try:
|
||||
if hasattr(obj,"IfcRole"):
|
||||
obj.IfcRole = ptype[3:]
|
||||
obj.IfcRole = ''.join(map(lambda x: x if x.islower() else " "+x, ptype[3:]))[1:]
|
||||
else:
|
||||
# pre-0.18 objects, only support a small subset of types
|
||||
r = ptype[3:]
|
||||
@@ -726,8 +754,11 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
obj = Arch.makeComponent(baseobj,name=name)
|
||||
|
||||
if obj:
|
||||
sols = str(obj.Shape.Solids) if hasattr(obj,"Shape") else ""
|
||||
if DEBUG: print(sols,end="")
|
||||
s = ""
|
||||
if hasattr(obj,"Shape"):
|
||||
if obj.Shape.Solids:
|
||||
s = str(len(obj.Shape.Solids))+" solids"
|
||||
if DEBUG: print(s,end="")
|
||||
objects[pid] = obj
|
||||
|
||||
elif (MERGE_MODE_ARCH == 1 and archobj) or (MERGE_MODE_STRUCT == 0 and not archobj):
|
||||
@@ -738,7 +769,9 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
|
||||
if ptype == "IfcBuildingStorey": obj.Placement.Base.z = product.Elevation*1000
|
||||
if ptype == "IfcBuildingStorey":
|
||||
if product.Elevation:
|
||||
obj.Placement.Base.z = product.Elevation * 1000
|
||||
elif baseobj:
|
||||
obj = Arch.makeComponent(baseobj,name=name,delete=True)
|
||||
|
||||
@@ -750,7 +783,9 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
for freecadtype,ifctypes in typesmap.items():
|
||||
if ptype in ifctypes:
|
||||
obj = getattr(Arch,"make"+freecadtype)(baseobj=baseobj,name=name)
|
||||
if ptype == "IfcBuildingStorey": obj.Placement.Base.z = product.Elevation*1000
|
||||
if ptype == "IfcBuildingStorey":
|
||||
if product.Elevation:
|
||||
obj.Placement.Base.z = product.Elevation * 1000
|
||||
elif baseobj:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Part::Feature",name)
|
||||
obj.Shape = shape
|
||||
@@ -968,13 +1003,17 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
else:
|
||||
|
||||
if DEBUG: print("Processing Arch relationships...",end="")
|
||||
first = True
|
||||
|
||||
# subtractions
|
||||
|
||||
if SEPARATE_OPENINGS:
|
||||
for subtraction in subtractions:
|
||||
if (subtraction[0] in objects.keys()) and (subtraction[1] in objects.keys()):
|
||||
if DEBUG: print("subtracting ",objects[subtraction[0]].Label, " from ", objects[subtraction[1]].Label)
|
||||
if DEBUG and first:
|
||||
print("")
|
||||
first = False
|
||||
if DEBUG: print(" subtracting ",objects[subtraction[0]].Label, " from ", objects[subtraction[1]].Label)
|
||||
Arch.removeComponents(objects[subtraction[0]],objects[subtraction[1]])
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@@ -984,11 +1023,14 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
if host in objects.keys():
|
||||
cobs = [objects[child] for child in children if child in objects.keys()]
|
||||
if cobs:
|
||||
if DEBUG and first:
|
||||
print("")
|
||||
first = False
|
||||
if DEBUG and (len(cobs) > 10) and (not(Draft.getType(objects[host]) in ["Site","Building","Floor","BuildingPart"])):
|
||||
# avoid huge fusions
|
||||
print("more than 10 shapes to add: skipping.")
|
||||
else:
|
||||
if DEBUG: print("adding ",len(cobs), " object(s) to ", objects[host].Label)
|
||||
if DEBUG: print(" adding ",len(cobs), " object(s) to ", objects[host].Label)
|
||||
Arch.addComponents(cobs,objects[host])
|
||||
if DEBUG: FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@@ -1130,11 +1172,11 @@ def insert(filename,docname,skip=[],only=[],root=None):
|
||||
|
||||
|
||||
class recycler:
|
||||
|
||||
|
||||
"a mechanism to reuse ifc entities if needed"
|
||||
|
||||
def __init__(self,ifcfile):
|
||||
|
||||
|
||||
self.ifcfile = ifcfile
|
||||
self.compress = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Arch").GetBool("ifcCompress",True)
|
||||
self.cartesianpoints = {(0,0,0):self.ifcfile[8]} # from template
|
||||
@@ -1147,7 +1189,7 @@ class recycler:
|
||||
self.ssrenderings = {}
|
||||
self.transformationoperators = {}
|
||||
self.spared = 0
|
||||
|
||||
|
||||
def createIfcCartesianPoint(self,points):
|
||||
if self.compress and points in self.cartesianpoints:
|
||||
self.spared += 1
|
||||
@@ -1200,7 +1242,7 @@ class recycler:
|
||||
if self.compress:
|
||||
self.axis2placement3ds[key] = c
|
||||
return c
|
||||
|
||||
|
||||
def createIfcLocalPlacement(self,gpl):
|
||||
key = str(gpl.Location.Coordinates) + str(gpl.Axis.DirectionRatios) + str(gpl.RefDirection.DirectionRatios)
|
||||
if self.compress and key in self.localplacements:
|
||||
@@ -1211,7 +1253,7 @@ class recycler:
|
||||
if self.compress:
|
||||
self.localplacements[key] = c
|
||||
return c
|
||||
|
||||
|
||||
def createIfcColourRgb(self,r,g,b):
|
||||
key = (r,g,b)
|
||||
if self.compress and key in self.rgbs:
|
||||
@@ -1222,7 +1264,7 @@ class recycler:
|
||||
if self.compress:
|
||||
self.rgbs[key] = c
|
||||
return c
|
||||
|
||||
|
||||
def createIfcSurfaceStyleRendering(self,col):
|
||||
key = (col.Red,col.Green,col.Blue)
|
||||
if self.compress and key in self.ssrenderings:
|
||||
@@ -1317,9 +1359,9 @@ def export(exportList,filename):
|
||||
groups = {} # { Host: [Child,Child,...] }
|
||||
profiledefs = {} # { ProfileDefString:profiledef,...}
|
||||
shapedefs = {} # { ShapeDefString:[shapes],... }
|
||||
|
||||
|
||||
# reusable entity system
|
||||
|
||||
|
||||
global ifcbin
|
||||
ifcbin = recycler(ifcfile)
|
||||
|
||||
@@ -1381,9 +1423,9 @@ def export(exportList,filename):
|
||||
continue
|
||||
if (Draft.getType(obj) == "BuildingPart") and hasattr(obj,"IfcRole") and (obj.IfcRole == "Undefined"):
|
||||
ifctype = "IfcBuildingStorey" # export BuildingParts as Storeys if their type wasn't explicitely set
|
||||
|
||||
|
||||
# export grids
|
||||
|
||||
|
||||
if ifctype in ["IfcAxis","IfcAxisSystem","IfcGrid"]:
|
||||
ifctype = "IfcGrid"
|
||||
ifcaxes = []
|
||||
@@ -1946,7 +1988,7 @@ def export(exportList,filename):
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
os.remove(templatefile)
|
||||
|
||||
|
||||
if DEBUG and ifcbin.compress:
|
||||
f = pyopen(filename,"rb")
|
||||
s = len(f.read().split("\n"))
|
||||
|
||||
Reference in New Issue
Block a user