BIM: Quantities support for nativeIFC objects (#18689)
* BIM: Quantities support for nativeIFC objects * BIM: Added nativeIFC support for schedules
This commit is contained in:
@@ -187,6 +187,8 @@ def get_object_type(ifcentity, objecttype=None):
|
||||
objecttype = "text"
|
||||
elif ifcentity.is_a("IfcGridAxis"):
|
||||
objecttype = "axis"
|
||||
elif ifcentity.is_a("IfcControl"):
|
||||
objecttype = "schedule"
|
||||
return objecttype
|
||||
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ class ifc_object:
|
||||
obj.ViewObject.signalChangeIcon()
|
||||
elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "Geometry":
|
||||
self.edit_geometry(obj, prop)
|
||||
elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) == "Quantities":
|
||||
self.edit_quantity(obj, prop)
|
||||
elif hasattr(obj, prop) and obj.getGroupOfProperty(prop) not in NON_PSETS:
|
||||
# Treat all property groups outside the default ones as Psets
|
||||
# print("DEBUG: editinog pset prop",prop)
|
||||
@@ -353,6 +355,11 @@ class ifc_object:
|
||||
# Not doing anything right now because an unset Type property could screw the ifc file
|
||||
pass
|
||||
|
||||
|
||||
def edit_quantity(self, obj, prop):
|
||||
"""Edits the given quantity"""
|
||||
pass # TODO implement
|
||||
|
||||
def get_section_data(self, obj):
|
||||
"""Returns two things: a list of objects and a cut plane"""
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
|
||||
import os
|
||||
|
||||
import FreeCAD
|
||||
|
||||
params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/NativeIFC")
|
||||
@@ -197,8 +196,10 @@ class ifc_observer:
|
||||
return
|
||||
del self.docname
|
||||
del self.objname
|
||||
if obj.isDerivedFrom("Part::Feature") or "IfcType" in obj.PropertiesList:
|
||||
FreeCAD.Console.PrintLog("Converting" + obj.Label + "to IFC\n")
|
||||
if obj.isDerivedFrom("Part::Feature") \
|
||||
or "IfcType" in obj.PropertiesList \
|
||||
or "CreateSpreadsheet" in obj.PropertiesList:
|
||||
FreeCAD.Console.PrintLog("Converting " + obj.Label + " to IFC\n")
|
||||
from nativeifc import ifc_geometry # lazy loading
|
||||
from nativeifc import ifc_tools # lazy loading
|
||||
|
||||
|
||||
@@ -105,34 +105,35 @@ def show_psets(obj):
|
||||
ttip = (
|
||||
ptype + ":" + oname
|
||||
) # setting IfcType:PropName as a tooltip to desambiguate
|
||||
while pname in obj.PropertiesList:
|
||||
#while pname in obj.PropertiesList:
|
||||
# print("DEBUG: property", pname, "(", value, ") already exists in", obj.Label)
|
||||
pname += "_"
|
||||
# pname += "_"
|
||||
ftype = None
|
||||
if ptype in [
|
||||
"IfcPositiveLengthMeasure",
|
||||
"IfcLengthMeasure",
|
||||
"IfcNonNegativeLengthMeasure",
|
||||
]:
|
||||
obj.addProperty("App::PropertyDistance", pname, gname, ttip)
|
||||
ftype = "App::PropertyDistance"
|
||||
elif ptype in ["IfcVolumeMeasure"]:
|
||||
obj.addProperty("App::PropertyVolume", pname, gname, ttip)
|
||||
ftype = "App::PropertyVolume"
|
||||
elif ptype in ["IfcPositivePlaneAngleMeasure", "IfcPlaneAngleMeasure"]:
|
||||
obj.addProperty("App::PropertyAngle", pname, gname, ttip)
|
||||
ftype = "App::PropertyAngle"
|
||||
value = float(value)
|
||||
while value > 360:
|
||||
value = value - 360
|
||||
elif ptype in ["IfcMassMeasure"]:
|
||||
obj.addProperty("App::PropertyMass", pname, gname, ttip)
|
||||
ftype = "App::PropertyMass"
|
||||
elif ptype in ["IfcAreaMeasure"]:
|
||||
obj.addProperty("App::PropertyArea", pname, gname, ttip)
|
||||
ftype = "App::PropertyArea"
|
||||
elif ptype in ["IfcCountMeasure", "IfcInteger"]:
|
||||
obj.addProperty("App::PropertyInteger", pname, gname, ttip)
|
||||
ftype = "App::PropertyInteger"
|
||||
value = int(value.strip("."))
|
||||
elif ptype in ["IfcReal"]:
|
||||
obj.addProperty("App::PropertyFloat", pname, gname, ttip)
|
||||
ftype = "App::PropertyFloat"
|
||||
value = float(value)
|
||||
elif ptype in ["IfcBoolean", "IfcLogical"]:
|
||||
obj.addProperty("App::PropertyBool", pname, gname, ttip)
|
||||
ftype = "App::PropertyBool"
|
||||
if value in [".T."]:
|
||||
value = True
|
||||
else:
|
||||
@@ -144,14 +145,30 @@ def show_psets(obj):
|
||||
"IfcDuration",
|
||||
"IfcTimeStamp",
|
||||
]:
|
||||
obj.addProperty("App::PropertyTime", pname, gname, ttip)
|
||||
ftype = "App::PropertyTime"
|
||||
elif isinstance(value, str) and "::" in value:
|
||||
# FreeCAD-specific: split strings by :: delimiter
|
||||
ftype = "App::PropertyStringList"
|
||||
value = value.split("::")
|
||||
else:
|
||||
obj.addProperty("App::PropertyString", pname, gname, ttip)
|
||||
ftype = "App::PropertyString"
|
||||
# print("DEBUG: setting",pname, ptype, value)
|
||||
setattr(obj, pname, value)
|
||||
if ftype:
|
||||
if pname in obj.PropertiesList \
|
||||
and obj.getGroupOfProperty(pname) == gname:
|
||||
if obj.getTypeOfProperty(pname) == ftype:
|
||||
pass
|
||||
if ftype == "App::PropertyString" \
|
||||
and obj.getTypeOfProperty(pname) == "App::PropertyStringList":
|
||||
value = [value]
|
||||
else:
|
||||
print(pname, gname, obj.PropertiesList)
|
||||
obj.addProperty(ftype, pname, gname, ttip)
|
||||
if pname in obj.PropertiesList:
|
||||
setattr(obj, pname, value)
|
||||
|
||||
|
||||
def edit_pset(obj, prop, value=None, force=False):
|
||||
def edit_pset(obj, prop, value=None, force=False, ifcfile=None, element=None):
|
||||
"""Edits the corresponding property. If force is True,
|
||||
the property is created even if it has no value"""
|
||||
|
||||
@@ -159,8 +176,14 @@ def edit_pset(obj, prop, value=None, force=False):
|
||||
ptype = obj.getDocumentationOfProperty(prop)
|
||||
if value is None:
|
||||
value = getattr(obj, prop)
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not ifcfile:
|
||||
ifcfile = ifc_tools.get_ifcfile(obj)
|
||||
if not ifcfile:
|
||||
return
|
||||
if not element:
|
||||
element = ifc_tools.get_ifc_element(obj)
|
||||
if not element:
|
||||
return
|
||||
pset_exist = get_psets(element)
|
||||
target_prop = None
|
||||
value_exist = None
|
||||
@@ -242,7 +265,7 @@ def edit_pset(obj, prop, value=None, force=False):
|
||||
"IFC: property changed for "
|
||||
+ obj.Label
|
||||
+ " ("
|
||||
+ str(obj.StepId)
|
||||
+ str(element.id())
|
||||
+ "): "
|
||||
+ str(target_prop)
|
||||
+ ": "
|
||||
@@ -356,3 +379,7 @@ def remove_property(obj, prop):
|
||||
# delete the pset too
|
||||
FreeCAD.Console.PrintMessage(translate("BIM","Removing property set")+": "+psetname)
|
||||
ifc_tools.api_run("pset.remove_pset", ifcfile, product=element, pset=pset)
|
||||
|
||||
|
||||
# Quantity types
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/ifcsharedbldgelements/content.html#6.1.5-Quantity-Sets
|
||||
|
||||
@@ -46,6 +46,7 @@ from nativeifc import ifc_import
|
||||
from nativeifc import ifc_layers
|
||||
from nativeifc import ifc_status
|
||||
from nativeifc import ifc_export
|
||||
from nativeifc import ifc_psets
|
||||
|
||||
from draftviewproviders import view_layer
|
||||
from PySide import QtCore
|
||||
@@ -443,7 +444,7 @@ def get_ifcfile(obj):
|
||||
if getattr(project, "Proxy", None):
|
||||
if hasattr(project.Proxy, "ifcfile"):
|
||||
return project.Proxy.ifcfile
|
||||
if project.IfcFilePath:
|
||||
if getattr(project, "IfcFilePath", None):
|
||||
ifcfile = ifcopenshell.open(project.IfcFilePath)
|
||||
if hasattr(project, "Proxy"):
|
||||
if project.Proxy is None:
|
||||
@@ -453,7 +454,7 @@ def get_ifcfile(obj):
|
||||
project.Proxy.ifcfile = ifcfile
|
||||
return ifcfile
|
||||
else:
|
||||
FreeCAD.Console.PrintError("Error: No IFC file attached to this project")
|
||||
FreeCAD.Console.PrintError("Error: No IFC file attached to this project: "+project.Label)
|
||||
return None
|
||||
|
||||
|
||||
@@ -508,11 +509,14 @@ def add_object(document, otype=None, oname="IfcObject"):
|
||||
'dimension',
|
||||
'sectionplane',
|
||||
'axis',
|
||||
'schedule'
|
||||
or anything else for a standard IFC object"""
|
||||
|
||||
if not document:
|
||||
return None
|
||||
if otype == "sectionplane":
|
||||
if otype == "schedule":
|
||||
obj = Arch.makeSchedule()
|
||||
elif otype == "sectionplane":
|
||||
obj = Arch.makeSectionPlane()
|
||||
obj.Proxy = ifc_objects.ifc_object(otype)
|
||||
elif otype == "axis":
|
||||
@@ -746,6 +750,8 @@ def add_properties(
|
||||
obj.addProperty("App::PropertyStringList", "Text", "Base")
|
||||
obj.Text = [text.Literal]
|
||||
obj.Placement = ifc_export.get_placement(ifcentity.ObjectPlacement, ifcfile)
|
||||
elif ifcentity.is_a("IfcControl"):
|
||||
ifc_psets.show_psets(obj)
|
||||
|
||||
# link Label2 and Description
|
||||
if "Description" in obj.PropertiesList and hasattr(obj, "setExpression"):
|
||||
@@ -1150,6 +1156,7 @@ def aggregate(obj, parent, mode=None):
|
||||
if not ifcfile:
|
||||
return
|
||||
product = None
|
||||
new = False
|
||||
stepid = getattr(obj, "StepId", None)
|
||||
if stepid:
|
||||
# obj might be dragging at this point and has no project anymore
|
||||
@@ -1163,7 +1170,6 @@ def aggregate(obj, parent, mode=None):
|
||||
# this object already has an associated IFC product
|
||||
print("DEBUG:", obj.Label, "is already part of the IFC document")
|
||||
newobj = obj
|
||||
new = False
|
||||
else:
|
||||
ifcclass = None
|
||||
if mode == "opening":
|
||||
@@ -1173,12 +1179,16 @@ def aggregate(obj, parent, mode=None):
|
||||
product = ifc_export.create_annotation(obj, ifcfile)
|
||||
if Draft.get_type(obj) in ["DraftText","Text"]:
|
||||
objecttype = "text"
|
||||
elif "CreateSpreadsheet" in obj.PropertiesList:
|
||||
obj.Proxy.create_ifc(obj, ifcfile)
|
||||
newobj = obj
|
||||
else:
|
||||
product = ifc_export.create_product(obj, parent, ifcfile, ifcclass)
|
||||
if product:
|
||||
shapemode = getattr(parent, "ShapeMode", DEFAULT_SHAPEMODE)
|
||||
newobj = create_object(product, obj.Document, ifcfile, shapemode, objecttype)
|
||||
new = True
|
||||
create_relationship(obj, newobj, parent, product, ifcfile, mode)
|
||||
create_relationship(obj, newobj, parent, product, ifcfile, mode)
|
||||
base = getattr(obj, "Base", None)
|
||||
if base:
|
||||
# make sure the base is used only by this object before deleting
|
||||
@@ -1524,6 +1534,12 @@ def get_orphan_elements(ifcfile):
|
||||
products = [
|
||||
p for p in products if not hasattr(p, "VoidsElements") or not p.VoidsElements
|
||||
]
|
||||
# add control elements
|
||||
proj = ifcfile.by_type("IfcProject")[0]
|
||||
for rel in proj.Declares:
|
||||
for ctrl in getattr(rel,"RelatedDefinitions", []):
|
||||
if ctrl.is_a("IfcControl"):
|
||||
products.append(ctrl)
|
||||
groups = []
|
||||
for o in products:
|
||||
for rel in getattr(o, "HasAssignments", []):
|
||||
|
||||
Reference in New Issue
Block a user