mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-27 02:31:09 +00:00
WIP implement qtos. See #1222.
This commit is contained in:
@@ -63,8 +63,6 @@ if bpy is not None:
|
|||||||
operator.SelectSweptSolidInnerCurves,
|
operator.SelectSweptSolidInnerCurves,
|
||||||
operator.AssignSweptSolidExtrusion,
|
operator.AssignSweptSolidExtrusion,
|
||||||
operator.SelectSweptSolidExtrusion,
|
operator.SelectSweptSolidExtrusion,
|
||||||
operator.AddQto,
|
|
||||||
operator.RemoveQto,
|
|
||||||
operator.AddMaterialPset,
|
operator.AddMaterialPset,
|
||||||
operator.RemoveMaterialPset,
|
operator.RemoveMaterialPset,
|
||||||
operator.AddMaterialLayer,
|
operator.AddMaterialLayer,
|
||||||
@@ -159,7 +157,6 @@ if bpy is not None:
|
|||||||
operator.PropagateTextData,
|
operator.PropagateTextData,
|
||||||
operator.ConvertLocalToGlobal,
|
operator.ConvertLocalToGlobal,
|
||||||
operator.ConvertGlobalToLocal,
|
operator.ConvertGlobalToLocal,
|
||||||
operator.GuessQuantity,
|
|
||||||
operator.ExecuteBIMTester,
|
operator.ExecuteBIMTester,
|
||||||
operator.BIMTesterPurge,
|
operator.BIMTesterPurge,
|
||||||
operator.SelectIfcPatchInput,
|
operator.SelectIfcPatchInput,
|
||||||
@@ -270,7 +267,6 @@ if bpy is not None:
|
|||||||
ui.BIM_PT_material,
|
ui.BIM_PT_material,
|
||||||
ui.BIM_PT_presentation_layer_data,
|
ui.BIM_PT_presentation_layer_data,
|
||||||
ui.BIM_PT_object_material,
|
ui.BIM_PT_object_material,
|
||||||
ui.BIM_PT_object_qto,
|
|
||||||
ui.BIM_PT_classification_references,
|
ui.BIM_PT_classification_references,
|
||||||
ui.BIM_PT_documents,
|
ui.BIM_PT_documents,
|
||||||
ui.BIM_PT_constraint_relations,
|
ui.BIM_PT_constraint_relations,
|
||||||
|
|||||||
@@ -975,7 +975,6 @@ class IfcImporter:
|
|||||||
self.add_element_classifications(element, obj)
|
self.add_element_classifications(element, obj)
|
||||||
self.add_element_document_relations(element, obj)
|
self.add_element_document_relations(element, obj)
|
||||||
self.add_opening_relation(element, obj)
|
self.add_opening_relation(element, obj)
|
||||||
self.add_product_definitions(element, obj)
|
|
||||||
self.added_data[element.GlobalId] = obj
|
self.added_data[element.GlobalId] = obj
|
||||||
|
|
||||||
if element.is_a("IfcOpeningElement"):
|
if element.is_a("IfcOpeningElement"):
|
||||||
@@ -1355,15 +1354,6 @@ class IfcImporter:
|
|||||||
bpy.ops.mesh.normals_make_consistent(context_override)
|
bpy.ops.mesh.normals_make_consistent(context_override)
|
||||||
bpy.ops.object.editmode_toggle(context_override)
|
bpy.ops.object.editmode_toggle(context_override)
|
||||||
|
|
||||||
def add_product_definitions(self, element, obj):
|
|
||||||
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
|
|
||||||
return
|
|
||||||
for definition in element.IsDefinedBy:
|
|
||||||
if not definition.is_a("IfcRelDefinesByProperties"):
|
|
||||||
continue
|
|
||||||
if definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
|
||||||
self.add_qto(definition.RelatingPropertyDefinition, obj)
|
|
||||||
|
|
||||||
def add_pset(self, pset, props):
|
def add_pset(self, pset, props):
|
||||||
new_pset = props.psets.add()
|
new_pset = props.psets.add()
|
||||||
new_pset.name = pset.Name
|
new_pset.name = pset.Name
|
||||||
@@ -1392,27 +1382,6 @@ class IfcImporter:
|
|||||||
new_prop.name = prop.Name
|
new_prop.name = prop.Name
|
||||||
new_prop.string_value = str(prop.NominalValue.wrappedValue)
|
new_prop.string_value = str(prop.NominalValue.wrappedValue)
|
||||||
|
|
||||||
def add_qto(self, qto, obj):
|
|
||||||
new_qto = obj.BIMObjectProperties.qtos.add()
|
|
||||||
new_qto.name = str(qto.Name)
|
|
||||||
qto_template = schema.ifc.psetqto.get_by_name(new_qto.name)
|
|
||||||
if qto_template:
|
|
||||||
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
|
|
||||||
prop = new_qto.properties.add()
|
|
||||||
prop.name = prop_name
|
|
||||||
for prop in qto.Quantities:
|
|
||||||
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
|
||||||
value = getattr(prop, "{}Value".format(prop.is_a()[len("IfcQuantity") :]))
|
|
||||||
if not value:
|
|
||||||
continue
|
|
||||||
index = new_qto.properties.find(prop.Name)
|
|
||||||
if index >= 0:
|
|
||||||
new_qto.properties[index].string_value = str(value)
|
|
||||||
else:
|
|
||||||
new_prop = new_qto.properties.add()
|
|
||||||
new_prop.name = prop.Name
|
|
||||||
new_prop.string_value = str(value)
|
|
||||||
|
|
||||||
def add_opening_relation(self, element, obj):
|
def add_opening_relation(self, element, obj):
|
||||||
if not element.is_a("IfcOpeningElement"):
|
if not element.is_a("IfcOpeningElement"):
|
||||||
return
|
return
|
||||||
@@ -1708,7 +1677,6 @@ class IfcImporter:
|
|||||||
|
|
||||||
self.add_element_classifications(element, obj)
|
self.add_element_classifications(element, obj)
|
||||||
self.add_element_document_relations(element, obj)
|
self.add_element_document_relations(element, obj)
|
||||||
self.add_product_definitions(element, obj)
|
|
||||||
self.aggregates[element.GlobalId] = obj
|
self.aggregates[element.GlobalId] = obj
|
||||||
self.aggregate_collections[rel_aggregate.id()] = collection
|
self.aggregate_collections[rel_aggregate.id()] = collection
|
||||||
|
|
||||||
@@ -1769,7 +1737,6 @@ class IfcImporter:
|
|||||||
obj.matrix_world = self.get_element_matrix(element, mesh_name)
|
obj.matrix_world = self.get_element_matrix(element, mesh_name)
|
||||||
self.add_element_classifications(element, obj)
|
self.add_element_classifications(element, obj)
|
||||||
self.add_element_document_relations(element, obj)
|
self.add_element_document_relations(element, obj)
|
||||||
self.add_product_definitions(element, obj)
|
|
||||||
self.added_data[element.GlobalId] = obj
|
self.added_data[element.GlobalId] = obj
|
||||||
|
|
||||||
def add_element_document_relations(self, element, obj):
|
def add_element_document_relations(self, element, obj):
|
||||||
|
|||||||
@@ -10,11 +10,9 @@ classes = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
bpy.types.Scene.COBieProperties = bpy.props.PointerProperty(type=prop.COBieProperties)
|
bpy.types.Scene.COBieProperties = bpy.props.PointerProperty(type=prop.COBieProperties)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Scene.COBieProperties
|
del bpy.types.Scene.COBieProperties
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from . import ui, operator
|
from . import ui, prop, operator
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.TogglePsetExpansion,
|
operator.TogglePsetExpansion,
|
||||||
@@ -8,13 +8,17 @@ classes = (
|
|||||||
operator.EditPset,
|
operator.EditPset,
|
||||||
operator.RemovePset,
|
operator.RemovePset,
|
||||||
operator.AddPset,
|
operator.AddPset,
|
||||||
|
operator.AddQto,
|
||||||
|
operator.GuessQuantity,
|
||||||
|
prop.PsetProperties,
|
||||||
ui.BIM_PT_object_psets,
|
ui.BIM_PT_object_psets,
|
||||||
|
ui.BIM_PT_object_qtos,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
pass
|
bpy.types.Object.PsetProperties = bpy.props.PointerProperty(type=prop.PsetProperties)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
pass
|
del bpy.types.Object.PsetProperties
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import ifcopenshell
|
||||||
|
import blenderbim.bim.schema # TODO: refactor
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"product": None, "Name": None}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
if self.settings["product"].is_a("IfcObject"):
|
||||||
|
qto = self.file.create_entity(
|
||||||
|
"IfcElementQuantity", **{"GlobalId": ifcopenshell.guid.new(), "Name": self.settings["Name"]}
|
||||||
|
)
|
||||||
|
self.file.create_entity(
|
||||||
|
"IfcRelDefinesByProperties",
|
||||||
|
**{
|
||||||
|
"GlobalId": ifcopenshell.guid.new(),
|
||||||
|
# TODO: owner history
|
||||||
|
"RelatedObjects": [self.settings["product"]],
|
||||||
|
"RelatingPropertyDefinition": qto,
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -6,6 +6,7 @@ from blenderbim.bim.ifc import IfcStore
|
|||||||
class Data:
|
class Data:
|
||||||
products = {}
|
products = {}
|
||||||
psets = {}
|
psets = {}
|
||||||
|
qtos = {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, product_id):
|
def load(cls, product_id):
|
||||||
@@ -13,7 +14,7 @@ class Data:
|
|||||||
if not file:
|
if not file:
|
||||||
return
|
return
|
||||||
product = file.by_id(product_id)
|
product = file.by_id(product_id)
|
||||||
cls.products[product_id] = set()
|
cls.products[product_id] = {"psets": set(), "qtos": set()}
|
||||||
if product.is_a("IfcElementType"):
|
if product.is_a("IfcElementType"):
|
||||||
cls.add_type_product_psets(product, product_id)
|
cls.add_type_product_psets(product, product_id)
|
||||||
else:
|
else:
|
||||||
@@ -36,47 +37,18 @@ class Data:
|
|||||||
continue
|
continue
|
||||||
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
if definition.RelatingPropertyDefinition.is_a("IfcPropertySet"):
|
||||||
cls.add_pset(definition.RelatingPropertyDefinition, product_id)
|
cls.add_pset(definition.RelatingPropertyDefinition, product_id)
|
||||||
|
elif definition.RelatingPropertyDefinition.is_a("IfcElementQuantity"):
|
||||||
|
cls.add_qto(definition.RelatingPropertyDefinition, product_id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_pset(cls, pset, product_id):
|
def add_pset(cls, pset, product_id):
|
||||||
new_pset = {
|
new_pset = {
|
||||||
"Name": pset.Name,
|
"Name": pset.Name,
|
||||||
"is_expanded": True,
|
"is_expanded": True,
|
||||||
"Properties": []
|
"Properties": cls.get_properties_from_template(pset.Name) or []
|
||||||
}
|
}
|
||||||
cls.products[product_id].add(int(pset.id()))
|
cls.products[product_id]["psets"].add(int(pset.id()))
|
||||||
cls.psets[int(pset.id())] = new_pset
|
cls.psets[int(pset.id())] = new_pset
|
||||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(pset.Name)
|
|
||||||
if pset_template:
|
|
||||||
for prop_template in pset_template.HasPropertyTemplates:
|
|
||||||
if not prop_template.is_a("IfcSimplePropertyTemplate"):
|
|
||||||
continue # Other types not yet supported
|
|
||||||
enum_items = []
|
|
||||||
if prop_template.TemplateType == "P_SINGLEVALUE":
|
|
||||||
data_type = str(IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType))
|
|
||||||
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
|
||||||
data_type = "enum"
|
|
||||||
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
|
||||||
else:
|
|
||||||
continue # Other types not yet supported
|
|
||||||
if "<string>" in data_type:
|
|
||||||
data_type = "string"
|
|
||||||
elif "<real>" in data_type:
|
|
||||||
data_type = "float"
|
|
||||||
elif "<integer>" in data_type:
|
|
||||||
data_type = "integer"
|
|
||||||
elif "<boolean>" in data_type:
|
|
||||||
data_type = "boolean"
|
|
||||||
elif "<enumeration" in data_type:
|
|
||||||
data_type = "enum"
|
|
||||||
enum_items = attribute.type_of_attribute().declared_type().enumeration_items()
|
|
||||||
new_pset["Properties"].append({
|
|
||||||
"Name": prop_template.Name,
|
|
||||||
"value": None,
|
|
||||||
"type": data_type,
|
|
||||||
"enum_items": enum_items,
|
|
||||||
"is_null": True
|
|
||||||
})
|
|
||||||
try:
|
try:
|
||||||
if hasattr(pset, "HasProperties"):
|
if hasattr(pset, "HasProperties"):
|
||||||
props = pset.HasProperties
|
props = pset.HasProperties
|
||||||
@@ -127,3 +99,74 @@ class Data:
|
|||||||
"enum_items": [],
|
"enum_items": [],
|
||||||
"is_null": prop.NominalValue.wrappedValue is None
|
"is_null": prop.NominalValue.wrappedValue is None
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_qto(cls, qto, product_id):
|
||||||
|
new_qto = {
|
||||||
|
"Name": qto.Name,
|
||||||
|
"is_expanded": True,
|
||||||
|
"Properties": cls.get_properties_from_template(qto.Name) or []
|
||||||
|
}
|
||||||
|
cls.products[product_id]["qtos"].add(int(qto.id()))
|
||||||
|
cls.qtos[int(qto.id())] = new_qto
|
||||||
|
for prop in qto.Quantities or []:
|
||||||
|
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
||||||
|
value = prop[3]
|
||||||
|
has_existing_prop = False
|
||||||
|
for existing_prop in new_qto["Properties"]:
|
||||||
|
if existing_prop["Name"] == prop.Name:
|
||||||
|
existing_prop["value"] = float(value)
|
||||||
|
existing_prop["is_null"] = value is None
|
||||||
|
has_existing_prop = True
|
||||||
|
break
|
||||||
|
if not has_existing_prop:
|
||||||
|
new_qto["Properties"].append({
|
||||||
|
"Name": prop.Name,
|
||||||
|
"value": float(value),
|
||||||
|
"type": "float",
|
||||||
|
"enum_items": [],
|
||||||
|
"is_null": value is None
|
||||||
|
})
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_properties_from_template(cls, name):
|
||||||
|
template = blenderbim.bim.schema.ifc.psetqto.get_by_name(name)
|
||||||
|
if not template:
|
||||||
|
return
|
||||||
|
properties = []
|
||||||
|
for prop_template in template.HasPropertyTemplates:
|
||||||
|
if not prop_template.is_a("IfcSimplePropertyTemplate"):
|
||||||
|
continue # Other types not yet supported
|
||||||
|
enum_items = []
|
||||||
|
|
||||||
|
if prop_template.TemplateType == "P_SINGLEVALUE":
|
||||||
|
data_type = str(IfcStore.get_schema().declaration_by_name(prop_template.PrimaryMeasureType or "IfcLabel"))
|
||||||
|
elif prop_template.TemplateType == "P_ENUMERATEDVALUE":
|
||||||
|
data_type = "enum"
|
||||||
|
enum_items = [v.wrappedValue for v in prop_template.Enumerators.EnumerationValues]
|
||||||
|
elif prop_template.TemplateType in ["Q_LENGTH", "Q_AREA", "Q_VOLUME", "Q_WEIGHT", "Q_TIME"]:
|
||||||
|
data_type = "float"
|
||||||
|
elif prop_template.TemplateType == "Q_COUNT":
|
||||||
|
data_type = "integer"
|
||||||
|
else:
|
||||||
|
continue # Other types not yet supported
|
||||||
|
|
||||||
|
if "<string>" in data_type:
|
||||||
|
data_type = "string"
|
||||||
|
elif "<real>" in data_type:
|
||||||
|
data_type = "float"
|
||||||
|
elif "<integer>" in data_type:
|
||||||
|
data_type = "integer"
|
||||||
|
elif "<boolean>" in data_type:
|
||||||
|
data_type = "boolean"
|
||||||
|
elif "<enumeration" in data_type:
|
||||||
|
data_type = "enum"
|
||||||
|
enum_items = attribute.type_of_attribute().declared_type().enumeration_items()
|
||||||
|
properties.append({
|
||||||
|
"Name": prop_template.Name,
|
||||||
|
"value": None,
|
||||||
|
"type": data_type,
|
||||||
|
"enum_items": enum_items,
|
||||||
|
"is_null": True
|
||||||
|
})
|
||||||
|
return properties
|
||||||
|
|||||||
@@ -9,47 +9,67 @@ class Usecase:
|
|||||||
self.settings[key] = value
|
self.settings[key] = value
|
||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
if hasattr(self.settings["pset"], "HasProperties"):
|
self.update_pset_name()
|
||||||
attr_name = "HasProperties"
|
self.load_pset_template()
|
||||||
elif hasattr(self.settings["pset"], "Properties"): # For IfcMaterialProperties
|
self.update_existing_properties()
|
||||||
attr_name = "Properties"
|
new_properties = self.add_new_properties()
|
||||||
props = getattr(self.settings["pset"], attr_name) or []
|
self.extend_pset_with_new_properties(new_properties)
|
||||||
|
|
||||||
|
def update_pset_name(self):
|
||||||
if self.settings["Name"]:
|
if self.settings["Name"]:
|
||||||
self.settings["pset"].Name = self.settings["Name"]
|
self.settings["pset"].Name = self.settings["Name"]
|
||||||
|
|
||||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(self.settings["pset"].Name)
|
def load_pset_template(self):
|
||||||
|
self.pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(self.settings["pset"].Name)
|
||||||
|
|
||||||
for prop in props:
|
def update_existing_properties(self):
|
||||||
if prop.Name in self.settings["Properties"]:
|
for prop in self.get_properties():
|
||||||
value = self.settings["Properties"][prop.Name]
|
self.update_existing_property(prop)
|
||||||
primary_measure_type = self.get_primary_measure_type(
|
|
||||||
pset_template, prop.Name, previous_value=prop.NominalValue
|
def update_existing_property(self, prop):
|
||||||
)
|
if prop.Name not in self.settings["Properties"]:
|
||||||
if value is not None:
|
return
|
||||||
prop.NominalValue = self.file.create_entity(primary_measure_type, value)
|
value = self.settings["Properties"][prop.Name]
|
||||||
else:
|
if value is None:
|
||||||
prop.NominalValue = None
|
prop.NominalValue = None
|
||||||
del self.settings["Properties"][prop.Name]
|
else:
|
||||||
|
primary_measure_type = self.get_primary_measure_type(prop.Name, previous_value=prop.NominalValue)
|
||||||
|
prop.NominalValue = self.file.create_entity(primary_measure_type, value)
|
||||||
|
del self.settings["Properties"][prop.Name]
|
||||||
|
|
||||||
|
def add_new_properties(self):
|
||||||
properties = []
|
properties = []
|
||||||
for name, value in self.settings["Properties"].items():
|
for name, value in self.settings["Properties"].items():
|
||||||
if value is None:
|
if value is None:
|
||||||
continue
|
continue
|
||||||
primary_measure_type = self.get_primary_measure_type(pset_template, name)
|
primary_measure_type = self.get_primary_measure_type(name)
|
||||||
properties.append(
|
properties.append(
|
||||||
self.file.create_entity(
|
self.file.create_entity(
|
||||||
"IfcPropertySingleValue",
|
"IfcPropertySingleValue",
|
||||||
**{"Name": name, "NominalValue": self.file.create_entity(primary_measure_type, value)},
|
**{"Name": name, "NominalValue": self.file.create_entity(primary_measure_type, value)},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
props = list(props)
|
return properties
|
||||||
props.extend(properties)
|
|
||||||
setattr(self.settings["pset"], attr_name, props)
|
|
||||||
|
|
||||||
def get_primary_measure_type(self, pset_template, name, previous_value=None):
|
def extend_pset_with_new_properties(self, new_properties):
|
||||||
if not pset_template:
|
props = list(self.get_properties())
|
||||||
|
props.extend(new_properties)
|
||||||
|
if hasattr(self.settings["pset"], "HasProperties"):
|
||||||
|
self.settings["pset"].HasProperties = props
|
||||||
|
elif hasattr(self.settings["pset"], "Properties"):
|
||||||
|
self.settings["pset"].Properties = props
|
||||||
|
|
||||||
|
def get_properties(self):
|
||||||
|
if hasattr(self.settings["pset"], "HasProperties"):
|
||||||
|
return self.settings["pset"].HasProperties or []
|
||||||
|
elif hasattr(self.settings["pset"], "Properties"): # For IfcMaterialProperties
|
||||||
|
return self.settings["pset"].Properties or []
|
||||||
|
|
||||||
|
def get_primary_measure_type(self, name, previous_value=None):
|
||||||
|
if not self.pset_template:
|
||||||
return previous_value.is_a() if previous_value else "IfcLabel"
|
return previous_value.is_a() if previous_value else "IfcLabel"
|
||||||
for prop_template in pset_template.HasPropertyTemplates:
|
for prop_template in self.pset_template.HasPropertyTemplates:
|
||||||
if prop_template.Name == name:
|
if prop_template.Name != name:
|
||||||
return prop_template.PrimaryMeasureType
|
continue
|
||||||
|
return prop_template.PrimaryMeasureType or "IfcLabel"
|
||||||
return previous_value.is_a() if previous_value else "IfcLabel"
|
return previous_value.is_a() if previous_value else "IfcLabel"
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import blenderbim.bim.schema # TODO: refactor
|
||||||
|
|
||||||
|
|
||||||
|
class Usecase:
|
||||||
|
def __init__(self, file, settings=None):
|
||||||
|
self.file = file
|
||||||
|
self.settings = {"qto": None, "Name": None, "Properties": {}}
|
||||||
|
for key, value in settings.items():
|
||||||
|
self.settings[key] = value
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.update_qto_name()
|
||||||
|
self.load_qto_template()
|
||||||
|
self.update_existing_properties()
|
||||||
|
new_properties = self.add_new_properties()
|
||||||
|
self.extend_qto_with_new_properties(new_properties)
|
||||||
|
|
||||||
|
def update_qto_name(self):
|
||||||
|
if self.settings["Name"]:
|
||||||
|
self.settings["qto"].Name = self.settings["Name"]
|
||||||
|
|
||||||
|
def load_qto_template(self):
|
||||||
|
self.qto_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(self.settings["qto"].Name)
|
||||||
|
|
||||||
|
def update_existing_properties(self):
|
||||||
|
for prop in self.settings["qto"].Quantities or []:
|
||||||
|
self.update_existing_property(prop)
|
||||||
|
|
||||||
|
def update_existing_property(self, prop):
|
||||||
|
if prop.Name not in self.settings["Properties"]:
|
||||||
|
return
|
||||||
|
value = self.settings["Properties"][prop.Name]
|
||||||
|
if prop.is_a("IfcPhysicalSimpleQuantity"):
|
||||||
|
prop[3] = float(value) if value else None
|
||||||
|
del self.settings["Properties"][prop.Name]
|
||||||
|
|
||||||
|
def add_new_properties(self):
|
||||||
|
properties = []
|
||||||
|
for name, value in self.settings["Properties"].items():
|
||||||
|
if value is None:
|
||||||
|
continue
|
||||||
|
property_type = self.get_canonical_property_type(name)
|
||||||
|
properties.append(
|
||||||
|
self.file.create_entity(
|
||||||
|
"IfcQuantity{}".format(property_type),
|
||||||
|
**{"Name": name, "{}Value".format(property_type): value},
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return properties
|
||||||
|
|
||||||
|
def extend_qto_with_new_properties(self, new_properties):
|
||||||
|
props = list(self.settings["qto"].Quantities) if self.settings["qto"].Quantities else []
|
||||||
|
props.extend(new_properties)
|
||||||
|
self.settings["qto"].Quantities = props
|
||||||
|
|
||||||
|
def get_canonical_property_type(self, name):
|
||||||
|
if not self.qto_template:
|
||||||
|
return "Length"
|
||||||
|
for prop_template in self.qto_template.HasPropertyTemplates:
|
||||||
|
if prop_template.Name != name:
|
||||||
|
continue
|
||||||
|
return prop_template.TemplateType[2:].lower().capitalize()
|
||||||
|
return "Length"
|
||||||
|
|
||||||
|
def get_primary_measure_type(self, name, previous_value=None):
|
||||||
|
if not self.qto_template:
|
||||||
|
return previous_value.is_a() if previous_value else "IfcLabel"
|
||||||
|
for prop_template in self.qto_template.HasPropertyTemplates:
|
||||||
|
if prop_template.Name != name:
|
||||||
|
continue
|
||||||
|
return prop_template.PrimaryMeasureType or "IfcLabel"
|
||||||
|
return previous_value.is_a() if previous_value else "IfcLabel"
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
import bpy
|
import bpy
|
||||||
import json
|
import json
|
||||||
|
import ifcopenshell.util.unit
|
||||||
import blenderbim.bim.module.pset.add_pset as add_pset
|
import blenderbim.bim.module.pset.add_pset as add_pset
|
||||||
import blenderbim.bim.module.pset.edit_pset as edit_pset
|
import blenderbim.bim.module.pset.edit_pset as edit_pset
|
||||||
import blenderbim.bim.module.pset.remove_pset as remove_pset
|
import blenderbim.bim.module.pset.remove_pset as remove_pset
|
||||||
|
import blenderbim.bim.module.pset.add_qto as add_qto
|
||||||
|
import blenderbim.bim.module.pset.edit_qto as edit_qto
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from blenderbim.bim.module.pset.data import Data
|
from blenderbim.bim.module.pset.data import Data
|
||||||
|
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||||
|
|
||||||
|
|
||||||
class TogglePsetExpansion(bpy.types.Operator):
|
class TogglePsetExpansion(bpy.types.Operator):
|
||||||
@@ -14,8 +18,8 @@ class TogglePsetExpansion(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
props = obj.BIMObjectProperties
|
data = Data.psets if self.pset_id in Data.psets else Data.qtos
|
||||||
Data.psets[self.pset_id]["is_expanded"] = not Data.psets[self.pset_id]["is_expanded"]
|
data[self.pset_id]["is_expanded"] = not data[self.pset_id]["is_expanded"]
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
@@ -26,13 +30,14 @@ class EnablePsetEditing(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
props = obj.BIMObjectProperties
|
props = obj.PsetProperties
|
||||||
|
|
||||||
while len(props.properties) > 0:
|
while len(props.properties) > 0:
|
||||||
props.properties.remove(0)
|
props.properties.remove(0)
|
||||||
|
|
||||||
props.active_pset_name = Data.psets[self.pset_id]["Name"]
|
data = Data.psets if self.pset_id in Data.psets else Data.qtos
|
||||||
for prop in Data.psets[self.pset_id]["Properties"]:
|
props.active_pset_name = data[self.pset_id]["Name"]
|
||||||
|
for prop in data[self.pset_id]["Properties"]:
|
||||||
new = props.properties.add()
|
new = props.properties.add()
|
||||||
new.name = prop["Name"]
|
new.name = prop["Name"]
|
||||||
new.is_null = prop["is_null"]
|
new.is_null = prop["is_null"]
|
||||||
@@ -59,7 +64,7 @@ class DisablePsetEditing(bpy.types.Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
props = obj.BIMObjectProperties
|
props = obj.PsetProperties
|
||||||
props.active_pset_id = 0
|
props.active_pset_id = 0
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -71,9 +76,11 @@ class EditPset(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
props = obj.BIMObjectProperties
|
oprops = obj.BIMObjectProperties
|
||||||
|
props = obj.PsetProperties
|
||||||
properties = {}
|
properties = {}
|
||||||
for prop in Data.psets[props.active_pset_id]["Properties"]:
|
data = Data.psets if props.active_pset_id in Data.psets else Data.qtos
|
||||||
|
for prop in data[props.active_pset_id]["Properties"]:
|
||||||
blender_prop = props.properties.get(prop["Name"])
|
blender_prop = props.properties.get(prop["Name"])
|
||||||
if not blender_prop:
|
if not blender_prop:
|
||||||
continue
|
continue
|
||||||
@@ -89,12 +96,19 @@ class EditPset(bpy.types.Operator):
|
|||||||
properties[prop["Name"]] = blender_prop.float_value
|
properties[prop["Name"]] = blender_prop.float_value
|
||||||
elif prop["type"] == "enum":
|
elif prop["type"] == "enum":
|
||||||
properties[prop["Name"]] = blender_prop.enum_value
|
properties[prop["Name"]] = blender_prop.enum_value
|
||||||
edit_pset.Usecase(self.file, {
|
if props.active_pset_id in Data.psets:
|
||||||
"pset": self.file.by_id(props.active_pset_id),
|
edit_pset.Usecase(self.file, {
|
||||||
"Name": props.active_pset_name,
|
"pset": self.file.by_id(props.active_pset_id),
|
||||||
"Properties": properties
|
"Name": props.active_pset_name,
|
||||||
}).execute()
|
"Properties": properties
|
||||||
Data.load(props.ifc_definition_id)
|
}).execute()
|
||||||
|
else:
|
||||||
|
edit_qto.Usecase(self.file, {
|
||||||
|
"qto": self.file.by_id(props.active_pset_id),
|
||||||
|
"Name": props.active_pset_name,
|
||||||
|
"Properties": properties
|
||||||
|
}).execute()
|
||||||
|
Data.load(oprops.ifc_definition_id)
|
||||||
bpy.ops.bim.disable_pset_editing()
|
bpy.ops.bim.disable_pset_editing()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -123,10 +137,72 @@ class AddPset(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
obj = bpy.context.active_object
|
obj = bpy.context.active_object
|
||||||
props = obj.BIMObjectProperties
|
oprops = obj.BIMObjectProperties
|
||||||
|
props = obj.PsetProperties
|
||||||
add_pset.Usecase(self.file, {
|
add_pset.Usecase(self.file, {
|
||||||
"product": self.file.by_id(props.ifc_definition_id),
|
"product": self.file.by_id(oprops.ifc_definition_id),
|
||||||
"Name": props.pset_name,
|
"Name": props.pset_name,
|
||||||
}).execute()
|
}).execute()
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(oprops.ifc_definition_id)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class AddQto(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.add_qto"
|
||||||
|
bl_label = "Add Qto"
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.file = IfcStore.get_file()
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
oprops = obj.BIMObjectProperties
|
||||||
|
props = obj.PsetProperties
|
||||||
|
add_qto.Usecase(self.file, {
|
||||||
|
"product": self.file.by_id(oprops.ifc_definition_id),
|
||||||
|
"Name": props.qto_name,
|
||||||
|
}).execute()
|
||||||
|
Data.load(oprops.ifc_definition_id)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class GuessQuantity(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.guess_quantity"
|
||||||
|
bl_label = "Guess Quantity"
|
||||||
|
prop: bpy.props.StringProperty()
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
self.qto_calculator = QtoCalculator()
|
||||||
|
obj = bpy.context.active_object
|
||||||
|
prop = obj.PsetProperties.properties.get(self.prop)
|
||||||
|
prop.float_value = self.guess_quantity(obj)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def guess_quantity(self, obj):
|
||||||
|
quantity = self.qto_calculator.guess_quantity(self.prop, [p.name for p in obj.PsetProperties.properties], obj)
|
||||||
|
if "area" in self.prop.lower():
|
||||||
|
if bpy.context.scene.BIMProperties.area_unit:
|
||||||
|
prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.area_unit)
|
||||||
|
quantity = ifcopenshell.util.unit.convert(quantity, None, "SQUARE_METRE", prefix, name)
|
||||||
|
elif "volume" in self.prop.lower():
|
||||||
|
if bpy.context.scene.BIMProperties.volume_unit:
|
||||||
|
prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.volume_unit)
|
||||||
|
quantity = ifcopenshell.util.unit.convert(quantity, None, "CUBIC_METRE", prefix, name)
|
||||||
|
else:
|
||||||
|
prefix, name = self.get_blender_prefix_name()
|
||||||
|
quantity = ifcopenshell.util.unit.convert(quantity, None, "METRE", prefix, name)
|
||||||
|
return round(quantity, 3)
|
||||||
|
|
||||||
|
def get_prefix_name(self, value):
|
||||||
|
if "/" in value:
|
||||||
|
return value.split("/")
|
||||||
|
return None, value
|
||||||
|
|
||||||
|
def get_blender_prefix_name(self):
|
||||||
|
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
||||||
|
if bpy.context.scene.unit_settings.length_unit == "INCHES":
|
||||||
|
return None, "inch"
|
||||||
|
elif bpy.context.scene.unit_settings.length_unit == "FEET":
|
||||||
|
return None, "foot"
|
||||||
|
elif bpy.context.scene.unit_settings.system == "METRIC":
|
||||||
|
if bpy.context.scene.unit_settings.length_unit == "METERS":
|
||||||
|
return None, "METRE"
|
||||||
|
return bpy.context.scene.unit_settings.length_unit[0 : -len("METERS")], "METRE"
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import bpy
|
||||||
|
from blenderbim.bim.prop import Attribute
|
||||||
|
import blenderbim.bim.schema
|
||||||
|
from bpy.types import PropertyGroup
|
||||||
|
from bpy.props import (
|
||||||
|
PointerProperty,
|
||||||
|
StringProperty,
|
||||||
|
EnumProperty,
|
||||||
|
BoolProperty,
|
||||||
|
IntProperty,
|
||||||
|
FloatProperty,
|
||||||
|
FloatVectorProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
psetnames = {}
|
||||||
|
qtonames = {}
|
||||||
|
|
||||||
|
|
||||||
|
def getPsetNames(self, context):
|
||||||
|
global psetnames
|
||||||
|
if "/" in context.active_object.name:
|
||||||
|
ifc_class = context.active_object.name.split("/")[0]
|
||||||
|
if ifc_class not in psetnames:
|
||||||
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
||||||
|
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||||
|
return psetnames[ifc_class]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def getQtoNames(self, context):
|
||||||
|
global qtonames
|
||||||
|
if "/" in context.active_object.name:
|
||||||
|
ifc_class = context.active_object.name.split("/")[0]
|
||||||
|
if ifc_class not in qtonames:
|
||||||
|
psets = blenderbim.bim.schema.ifc.psetqto.get_applicable(ifc_class, qto_only=True)
|
||||||
|
qtonames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
||||||
|
return qtonames[ifc_class]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
class PsetProperties(PropertyGroup):
|
||||||
|
active_pset_id: IntProperty(name="Active Pset ID")
|
||||||
|
active_pset_name: StringProperty(name="Pset Name")
|
||||||
|
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||||
|
pset_name: EnumProperty(items=getPsetNames, name="Pset Name")
|
||||||
|
qto_name: EnumProperty(items=getQtoNames, name="Qto Name")
|
||||||
@@ -1,6 +1,64 @@
|
|||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
from blenderbim.bim.module.pset.data import Data
|
from blenderbim.bim.module.pset.data import Data
|
||||||
|
|
||||||
|
def draw_psetqto_ui(pset_id, pset, props, layout):
|
||||||
|
box = layout.box()
|
||||||
|
row = box.row(align=True)
|
||||||
|
icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT"
|
||||||
|
row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id
|
||||||
|
if not props.active_pset_id:
|
||||||
|
row.label(text=pset["Name"], icon="COPY_ID")
|
||||||
|
row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="").pset_id = pset_id
|
||||||
|
row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id
|
||||||
|
elif props.active_pset_id != pset_id:
|
||||||
|
row.label(text=pset["Name"], icon="COPY_ID")
|
||||||
|
row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id
|
||||||
|
elif props.active_pset_id == pset_id:
|
||||||
|
row.prop(props, "active_pset_name", icon="COPY_ID", text="")
|
||||||
|
row.operator("bim.edit_pset", icon="CHECKMARK", text="")
|
||||||
|
row.operator("bim.disable_pset_editing", icon="X", text="")
|
||||||
|
if pset["is_expanded"]:
|
||||||
|
if props.active_pset_id == pset_id:
|
||||||
|
for prop in pset["Properties"]:
|
||||||
|
draw_psetqto_editable_ui(box, props, prop)
|
||||||
|
else:
|
||||||
|
for prop in pset["Properties"]:
|
||||||
|
if prop["value"] is None or prop["value"] == "":
|
||||||
|
continue
|
||||||
|
row = box.row(align=True)
|
||||||
|
row.scale_y = 0.8
|
||||||
|
row.label(text=prop["Name"])
|
||||||
|
row.label(text=str(prop["value"]))
|
||||||
|
|
||||||
|
def draw_psetqto_editable_ui(box, props, prop):
|
||||||
|
row = box.row(align=True)
|
||||||
|
blender_prop = props.properties.get(prop["Name"])
|
||||||
|
if prop["type"] == "string":
|
||||||
|
row.prop(blender_prop, "string_value", text=prop["Name"])
|
||||||
|
elif prop["type"] == "integer":
|
||||||
|
row.prop(blender_prop, "int_value", text=prop["Name"])
|
||||||
|
elif prop["type"] == "float":
|
||||||
|
row.prop(blender_prop, "float_value", text=prop["Name"])
|
||||||
|
elif prop["type"] == "boolean":
|
||||||
|
row.prop(blender_prop, "bool_value", text=prop["Name"])
|
||||||
|
elif prop["type"] == "enum":
|
||||||
|
row.prop(blender_prop, "enum_value", text=prop["Name"])
|
||||||
|
row.prop(blender_prop, "is_null", icon="RADIOBUT_OFF" if blender_prop.is_null else "RADIOBUT_ON", text="")
|
||||||
|
if (
|
||||||
|
"length" in prop["Name"].lower()
|
||||||
|
or "width" in prop["Name"].lower()
|
||||||
|
or "height" in prop["Name"].lower()
|
||||||
|
or "depth" in prop["Name"].lower()
|
||||||
|
or "perimeter" in prop["Name"].lower()
|
||||||
|
):
|
||||||
|
op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="")
|
||||||
|
op.prop = prop["Name"]
|
||||||
|
elif "area" in prop["Name"].lower():
|
||||||
|
op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="")
|
||||||
|
op.prop = prop["Name"]
|
||||||
|
elif "volume" in prop["Name"].lower():
|
||||||
|
op = row.operator("bim.guess_quantity", icon="SPHERE", text="")
|
||||||
|
op.prop = prop["Name"]
|
||||||
|
|
||||||
class BIM_PT_object_psets(Panel):
|
class BIM_PT_object_psets(Panel):
|
||||||
bl_label = "IFC Object Property Sets"
|
bl_label = "IFC Object Property Sets"
|
||||||
@@ -23,61 +81,57 @@ class BIM_PT_object_psets(Panel):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
props = context.active_object.BIMObjectProperties
|
oprops = context.active_object.BIMObjectProperties
|
||||||
if not props.ifc_definition_id:
|
props = context.active_object.PsetProperties
|
||||||
|
if not oprops.ifc_definition_id:
|
||||||
return
|
return
|
||||||
if props.ifc_definition_id not in Data.products:
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
Data.load(props.ifc_definition_id)
|
Data.load(oprops.ifc_definition_id)
|
||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
row.prop(props, "pset_name", text="")
|
row.prop(props, "pset_name", text="")
|
||||||
row.operator("bim.add_pset", icon="ADD", text="")
|
row.operator("bim.add_pset", icon="ADD", text="")
|
||||||
|
|
||||||
self.draw_psets_ui(props, Data.products[props.ifc_definition_id])
|
for pset_id in Data.products[oprops.ifc_definition_id]["psets"]:
|
||||||
|
pset = Data.psets[pset_id]
|
||||||
|
draw_psetqto_ui(pset_id, pset, props, self.layout)
|
||||||
|
|
||||||
# TODO reimplement. See #1222.
|
# TODO reimplement. See #1222.
|
||||||
#if props.relating_type and props.relating_type.BIMObjectProperties.psets:
|
#if props.relating_type and props.relating_type.PsetProperties.psets:
|
||||||
# self.layout.label(text="Inherited Psets:")
|
# self.layout.label(text="Inherited Psets:")
|
||||||
# self.draw_psets_ui(props.relating_type.BIMObjectProperties, enabled=False)
|
# self.draw_psets_ui(props.relating_type.PsetProperties, enabled=False)
|
||||||
|
|
||||||
def draw_psets_ui(self, props, pset_ids):
|
|
||||||
for pset_id in pset_ids:
|
class BIM_PT_object_qtos(Panel):
|
||||||
pset = Data.psets[pset_id]
|
bl_label = "IFC Object Quantity Sets"
|
||||||
box = self.layout.box()
|
bl_idname = "BIM_PT_object_qtos"
|
||||||
row = box.row(align=True)
|
bl_space_type = "PROPERTIES"
|
||||||
icon = "TRIA_DOWN" if pset["is_expanded"] else "TRIA_RIGHT"
|
bl_region_type = "WINDOW"
|
||||||
row.operator("bim.toggle_pset_expansion", icon=icon, text="", emboss=False).pset_id = pset_id
|
bl_context = "object"
|
||||||
if not props.active_pset_id:
|
|
||||||
row.label(text=pset["Name"], icon="COPY_ID")
|
@classmethod
|
||||||
row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="").pset_id = pset_id
|
def poll(cls, context):
|
||||||
row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id
|
if not context.active_object:
|
||||||
elif props.active_pset_id != pset_id:
|
return False
|
||||||
row.label(text=pset["Name"], icon="COPY_ID")
|
props = context.active_object.BIMObjectProperties
|
||||||
row.operator("bim.remove_pset", icon="X", text="").pset_id = pset_id
|
if not props.ifc_definition_id:
|
||||||
elif props.active_pset_id == pset_id:
|
return False
|
||||||
row.prop(props, "active_pset_name", icon="COPY_ID", text="")
|
if props.ifc_definition_id not in Data.products:
|
||||||
row.operator("bim.edit_pset", icon="CHECKMARK", text="")
|
Data.load(props.ifc_definition_id)
|
||||||
row.operator("bim.disable_pset_editing", icon="X", text="")
|
if not Data.products[props.ifc_definition_id]:
|
||||||
if pset["is_expanded"]:
|
return False
|
||||||
if props.active_pset_id == pset_id:
|
return True
|
||||||
for prop in pset["Properties"]:
|
|
||||||
row = box.row(align=True)
|
def draw(self, context):
|
||||||
blender_prop = props.properties.get(prop["Name"])
|
oprops = context.active_object.BIMObjectProperties
|
||||||
if prop["type"] == "string":
|
props = context.active_object.PsetProperties
|
||||||
row.prop(blender_prop, "string_value", text=prop["Name"])
|
if not oprops.ifc_definition_id:
|
||||||
elif prop["type"] == "integer":
|
return
|
||||||
row.prop(blender_prop, "int_value", text=prop["Name"])
|
if oprops.ifc_definition_id not in Data.products:
|
||||||
elif prop["type"] == "float":
|
Data.load(oprops.ifc_definition_id)
|
||||||
row.prop(blender_prop, "float_value", text=prop["Name"])
|
row = self.layout.row(align=True)
|
||||||
elif prop["type"] == "boolean":
|
row.prop(props, "qto_name", text="")
|
||||||
row.prop(blender_prop, "bool_value", text=prop["Name"])
|
row.operator("bim.add_qto", icon="ADD", text="")
|
||||||
elif prop["type"] == "enum":
|
|
||||||
row.prop(blender_prop, "enum_value", text=prop["Name"])
|
for qto_id in Data.products[oprops.ifc_definition_id]["qtos"]:
|
||||||
row.prop(blender_prop, "is_null", icon="RADIOBUT_OFF" if blender_prop.is_null else "RADIOBUT_ON", text="")
|
qto = Data.qtos[qto_id]
|
||||||
else:
|
draw_psetqto_ui(qto_id, qto, props, self.layout)
|
||||||
for prop in pset["Properties"]:
|
|
||||||
if prop["value"] is None or prop["value"] == "":
|
|
||||||
continue
|
|
||||||
row = box.row(align=True)
|
|
||||||
row.scale_y = 0.8
|
|
||||||
row.label(text=prop["Name"])
|
|
||||||
row.label(text=str(prop["value"]))
|
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ class ExportIFC(bpy.types.Operator):
|
|||||||
else:
|
else:
|
||||||
output_file = bpy.path.ensure_ext(self.filepath, ".ifc")
|
output_file = bpy.path.ensure_ext(self.filepath, ".ifc")
|
||||||
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
|
ifc_export_settings = export_ifc.IfcExportSettings.factory(context, output_file, logger)
|
||||||
qto_calculator = qto.QtoCalculator()
|
qto_calculator = None # TODO: remove from export
|
||||||
ifc_parser = export_ifc.IfcParser(ifc_export_settings, qto_calculator)
|
ifc_parser = export_ifc.IfcParser(ifc_export_settings, qto_calculator)
|
||||||
ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser)
|
ifc_exporter = export_ifc.IfcExporter(ifc_export_settings, ifc_parser)
|
||||||
ifc_export_settings.logger.info("Starting export")
|
ifc_export_settings.logger.info("Starting export")
|
||||||
@@ -2805,70 +2805,6 @@ class ConvertGlobalToLocal(bpy.types.Operator):
|
|||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class GuessQuantity(bpy.types.Operator):
|
|
||||||
bl_idname = "bim.guess_quantity"
|
|
||||||
bl_label = "Guess Quantity"
|
|
||||||
qto_index: bpy.props.IntProperty()
|
|
||||||
prop_index: bpy.props.IntProperty()
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
self.qto_calculator = qto.QtoCalculator()
|
|
||||||
source_qto = bpy.context.active_object.BIMObjectProperties.qtos[self.qto_index]
|
|
||||||
props = source_qto.properties
|
|
||||||
prop = props[self.prop_index]
|
|
||||||
for obj in bpy.context.selected_objects:
|
|
||||||
dest_qto = obj.BIMObjectProperties.qtos.get(source_qto.name)
|
|
||||||
if not dest_qto:
|
|
||||||
if source_qto.name not in obj.BIMObjectProperties.qto_name:
|
|
||||||
continue
|
|
||||||
dest_qto = self.add_qto(obj, source_qto.name)
|
|
||||||
prop = dest_qto.properties.get(prop.name)
|
|
||||||
self.guess_quantity(obj, prop, props)
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
def guess_quantity(self, obj, prop, props):
|
|
||||||
quantity = self.qto_calculator.guess_quantity(prop.name, [p.name for p in props], obj)
|
|
||||||
if "area" in prop.name.lower():
|
|
||||||
if bpy.context.scene.BIMProperties.area_unit:
|
|
||||||
prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.area_unit)
|
|
||||||
quantity = helper.SIUnitHelper.convert(quantity, None, "SQUARE_METRE", prefix, name)
|
|
||||||
elif "volume" in prop.name.lower():
|
|
||||||
if bpy.context.scene.BIMProperties.volume_unit:
|
|
||||||
prefix, name = self.get_prefix_name(bpy.context.scene.BIMProperties.volume_unit)
|
|
||||||
quantity = helper.SIUnitHelper.convert(quantity, None, "CUBIC_METRE", prefix, name)
|
|
||||||
else:
|
|
||||||
prefix, name = self.get_blender_prefix_name()
|
|
||||||
quantity = helper.SIUnitHelper.convert(quantity, None, "METRE", prefix, name)
|
|
||||||
prop.string_value = str(round(quantity, 3))
|
|
||||||
|
|
||||||
def add_qto(self, obj, name):
|
|
||||||
qto_template = schema.ifc.psetqto.get_by_name(name)
|
|
||||||
if not qto_template:
|
|
||||||
return
|
|
||||||
qto = obj.BIMObjectProperties.qtos.add()
|
|
||||||
qto.name = name
|
|
||||||
for prop_name in (p.Name for p in qto_template.HasPropertyTemplates):
|
|
||||||
prop = qto.properties.add()
|
|
||||||
prop.name = prop_name
|
|
||||||
return qto
|
|
||||||
|
|
||||||
def get_prefix_name(self, value):
|
|
||||||
if "/" in value:
|
|
||||||
return value.split("/")
|
|
||||||
return None, value
|
|
||||||
|
|
||||||
def get_blender_prefix_name(self):
|
|
||||||
if bpy.context.scene.unit_settings.system == "IMPERIAL":
|
|
||||||
if bpy.context.scene.unit_settings.length_unit == "INCHES":
|
|
||||||
return None, "inch"
|
|
||||||
elif bpy.context.scene.unit_settings.length_unit == "FEET":
|
|
||||||
return None, "foot"
|
|
||||||
elif bpy.context.scene.unit_settings.system == "METRIC":
|
|
||||||
if bpy.context.scene.unit_settings.length_unit == "METERS":
|
|
||||||
return None, "METRE"
|
|
||||||
return bpy.context.scene.unit_settings.length_unit[0 : -len("METERS")], "METRE"
|
|
||||||
|
|
||||||
|
|
||||||
class ExecuteBIMTester(bpy.types.Operator):
|
class ExecuteBIMTester(bpy.types.Operator):
|
||||||
bl_idname = "bim.execute_bim_tester"
|
bl_idname = "bim.execute_bim_tester"
|
||||||
bl_label = "Execute BIMTester"
|
bl_label = "Execute BIMTester"
|
||||||
@@ -2982,6 +2918,7 @@ class CalculateObjectVolumes(bpy.types.Operator):
|
|||||||
bl_label = "Calculate Object Volumes"
|
bl_label = "Calculate Object Volumes"
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
|
# TODO: reimplement
|
||||||
qto_calculator = qto.QtoCalculator()
|
qto_calculator = qto.QtoCalculator()
|
||||||
result = 0
|
result = 0
|
||||||
for obj in bpy.context.selected_objects:
|
for obj in bpy.context.selected_objects:
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ psettemplatefiles_enum = []
|
|||||||
propertysettemplates_enum = []
|
propertysettemplates_enum = []
|
||||||
classification_enum = []
|
classification_enum = []
|
||||||
attributes_enum = []
|
attributes_enum = []
|
||||||
psetnames = {}
|
|
||||||
qtonames_enum = []
|
|
||||||
materialattributes_enum = []
|
materialattributes_enum = []
|
||||||
materialtypes_enum = []
|
materialtypes_enum = []
|
||||||
contexts_enum = []
|
contexts_enum = []
|
||||||
@@ -445,17 +443,6 @@ def refreshReferences(self, context):
|
|||||||
context.scene.BIMProperties.classification_references.root = ""
|
context.scene.BIMProperties.classification_references.root = ""
|
||||||
|
|
||||||
|
|
||||||
def getPsetNames(self, context):
|
|
||||||
global psetnames
|
|
||||||
if "/" in context.active_object.name:
|
|
||||||
ifc_class = context.active_object.name.split("/")[0]
|
|
||||||
if ifc_class not in psetnames:
|
|
||||||
psets = schema.ifc.psetqto.get_applicable(ifc_class, pset_only=True)
|
|
||||||
psetnames[ifc_class] = [(p.Name, p.Name, "") for p in psets]
|
|
||||||
return psetnames[ifc_class]
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def getMaterialPsetNames(self, context):
|
def getMaterialPsetNames(self, context):
|
||||||
global materialpsetnames_enum
|
global materialpsetnames_enum
|
||||||
materialpsetnames_enum.clear()
|
materialpsetnames_enum.clear()
|
||||||
@@ -464,15 +451,6 @@ def getMaterialPsetNames(self, context):
|
|||||||
return materialpsetnames_enum
|
return materialpsetnames_enum
|
||||||
|
|
||||||
|
|
||||||
def getQtoNames(self, context):
|
|
||||||
global qtonames_enum
|
|
||||||
qtonames_enum.clear()
|
|
||||||
if "/" in context.active_object.name and context.active_object.name.split("/")[0] in schema.ifc.elements:
|
|
||||||
qto_names = schema.ifc.psetqto.get_applicable_names(context.active_object.name.split("/")[0], qto_only=True)
|
|
||||||
qtonames_enum.extend([(q, q, "") for q in qto_names])
|
|
||||||
return qtonames_enum
|
|
||||||
|
|
||||||
|
|
||||||
def getApplicableMaterialAttributes(self, context):
|
def getApplicableMaterialAttributes(self, context):
|
||||||
global materialattributes_enum
|
global materialattributes_enum
|
||||||
materialattributes_enum.clear()
|
materialattributes_enum.clear()
|
||||||
@@ -1492,9 +1470,6 @@ class BIMObjectProperties(PropertyGroup):
|
|||||||
is_editing_type: BoolProperty(name="Is Editing Type")
|
is_editing_type: BoolProperty(name="Is Editing Type")
|
||||||
relating_type: PointerProperty(name="Type Product", type=bpy.types.Object)
|
relating_type: PointerProperty(name="Type Product", type=bpy.types.Object)
|
||||||
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
|
relating_structure: PointerProperty(name="Spatial Container", type=bpy.types.Object)
|
||||||
active_pset_id: IntProperty(name="Active Pset ID")
|
|
||||||
active_pset_name: StringProperty(name="Pset Name")
|
|
||||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
|
||||||
psets: CollectionProperty(name="Psets", type=PsetQto)
|
psets: CollectionProperty(name="Psets", type=PsetQto)
|
||||||
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
qtos: CollectionProperty(name="Qtos", type=PsetQto)
|
||||||
document_references: CollectionProperty(name="Document References", type=DocumentReference)
|
document_references: CollectionProperty(name="Document References", type=DocumentReference)
|
||||||
@@ -1505,8 +1480,6 @@ class BIMObjectProperties(PropertyGroup):
|
|||||||
material_type: EnumProperty(items=getMaterialTypes, name="Material Type")
|
material_type: EnumProperty(items=getMaterialTypes, name="Material Type")
|
||||||
material: PointerProperty(name="Material", type=bpy.types.Material)
|
material: PointerProperty(name="Material", type=bpy.types.Material)
|
||||||
material_set: PointerProperty(name="Material Set", type=MaterialSet)
|
material_set: PointerProperty(name="Material Set", type=MaterialSet)
|
||||||
pset_name: EnumProperty(items=getPsetNames, name="Pset Name")
|
|
||||||
qto_name: EnumProperty(items=getQtoNames, name="Qto Name")
|
|
||||||
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
|
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
|
||||||
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
|
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
|
||||||
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
|
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
|
||||||
|
|||||||
@@ -143,61 +143,6 @@ class BIM_PT_object_material(Panel):
|
|||||||
row.prop(attribute, "string_value", text="")
|
row.prop(attribute, "string_value", text="")
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_object_qto(Panel):
|
|
||||||
bl_label = "IFC Object Quantity Sets"
|
|
||||||
bl_idname = "BIM_PT_object_qto"
|
|
||||||
bl_space_type = "PROPERTIES"
|
|
||||||
bl_region_type = "WINDOW"
|
|
||||||
bl_context = "object"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def poll(cls, context):
|
|
||||||
return context.active_object is not None and hasattr(context.active_object, "BIMObjectProperties")
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
if context.active_object is None:
|
|
||||||
return
|
|
||||||
layout = self.layout
|
|
||||||
props = context.active_object.BIMObjectProperties
|
|
||||||
row = layout.row(align=True)
|
|
||||||
row.prop(props, "qto_name", text="")
|
|
||||||
row.operator("bim.add_qto")
|
|
||||||
|
|
||||||
for index, qto in enumerate(props.qtos):
|
|
||||||
box = self.layout.box()
|
|
||||||
row = box.row(align=True)
|
|
||||||
|
|
||||||
row.prop(
|
|
||||||
qto, "is_expanded", icon="TRIA_DOWN" if qto.is_expanded else "TRIA_RIGHT", icon_only=True, emboss=False
|
|
||||||
)
|
|
||||||
row.prop(qto, "name", text="", icon="COPY_ID")
|
|
||||||
row.operator("bim.remove_qto", icon="X", text="").index = index
|
|
||||||
if not qto.is_expanded:
|
|
||||||
continue
|
|
||||||
for index2, prop in enumerate(qto.properties):
|
|
||||||
row = box.row(align=True)
|
|
||||||
row.prop(prop, "name", text="")
|
|
||||||
row.prop(prop, "string_value", text="")
|
|
||||||
if (
|
|
||||||
"length" in prop.name.lower()
|
|
||||||
or "width" in prop.name.lower()
|
|
||||||
or "height" in prop.name.lower()
|
|
||||||
or "depth" in prop.name.lower()
|
|
||||||
or "perimeter" in prop.name.lower()
|
|
||||||
):
|
|
||||||
op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="")
|
|
||||||
op.qto_index = index
|
|
||||||
op.prop_index = index2
|
|
||||||
elif "area" in prop.name.lower():
|
|
||||||
op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="")
|
|
||||||
op.qto_index = index
|
|
||||||
op.prop_index = index2
|
|
||||||
elif "volume" in prop.name.lower():
|
|
||||||
op = row.operator("bim.guess_quantity", icon="SPHERE", text="")
|
|
||||||
op.qto_index = index
|
|
||||||
op.prop_index = index2
|
|
||||||
|
|
||||||
|
|
||||||
class BIM_PT_object_structural(Panel):
|
class BIM_PT_object_structural(Panel):
|
||||||
bl_label = "IFC Structural Relationships"
|
bl_label = "IFC Structural Relationships"
|
||||||
bl_idname = "BIM_PT_object_structural"
|
bl_idname = "BIM_PT_object_structural"
|
||||||
|
|||||||
Reference in New Issue
Block a user