WIP refactor representation editing into geometry module. See #1222. Some new functionality as a result:

- Representations can now be added to any context, even invalid ones, non-subcontext, or missing target views
 - Switching representations now change all linked data objects
This commit is contained in:
Dion Moult
2021-01-03 19:09:01 +11:00
parent c5ec54ca7f
commit af5350bb9f
15 changed files with 334 additions and 423 deletions
@@ -8,6 +8,7 @@ if bpy is not None:
import blenderbim.bim.module.bcf as module_bcf
import blenderbim.bim.module.context as module_context
import blenderbim.bim.module.covetool as module_covetool
import blenderbim.bim.module.geometry as module_geometry
import blenderbim.bim.module.model as module_model
from . import ui, prop, operator
@@ -132,9 +133,6 @@ if bpy is not None:
operator.SmartClashGroup,
operator.SelectSmartGroup,
operator.LoadSmartGroupsForActiveClashSet,
operator.AddRepresentation,
operator.SwitchRepresentation,
operator.RemoveRepresentation,
operator.OpenUpstream,
operator.BIM_OT_ChangeClassificationLevel,
operator.AddPropertySetTemplate,
@@ -208,9 +206,6 @@ if bpy is not None:
operator.InspectFromObject,
operator.RewindInspector,
operator.RefreshDrawingList,
operator.GetRepresentationIfcParameters,
operator.BakeParametricGeometry,
operator.UpdateIfcRepresentation,
operator.SetBlenderClashSetA,
operator.SetBlenderClashSetB,
operator.ExecuteBlenderClash,
@@ -244,8 +239,6 @@ if bpy is not None:
prop.Schedule,
prop.DrawingStyle,
prop.Sheet,
prop.Subcontext,
prop.Representation,
prop.PresentationLayer,
prop.BIMProperties,
prop.BIMDebugProperties,
@@ -296,7 +289,6 @@ if bpy is not None:
ui.BIM_PT_object_material,
ui.BIM_PT_object_psets,
ui.BIM_PT_object_qto,
ui.BIM_PT_representations,
ui.BIM_PT_classification_references,
ui.BIM_PT_documents,
ui.BIM_PT_constraint_relations,
@@ -323,6 +315,7 @@ if bpy is not None:
classes.extend(module_bcf.classes)
classes.extend(module_context.classes)
classes.extend(module_covetool.classes)
classes.extend(module_geometry.classes)
classes.extend(module_model.classes)
def menu_func_export(self, context):
@@ -358,6 +351,7 @@ if bpy is not None:
module_bcf.register()
module_context.register()
module_covetool.register()
module_geometry.register()
module_model.register()
bpy.app.handlers.depsgraph_update_pre.append(operator.depsgraph_update_pre_handler)
@@ -380,6 +374,7 @@ if bpy is not None:
del bpy.types.TextCurve.BIMTextProperties
bpy.types.SCENE_PT_unit.remove(ui.ifc_units)
module_model.unregister()
module_geometry.unregister()
module_covetool.unregister()
module_context.unregister()
module_bcf.unregister()
@@ -3553,15 +3553,4 @@ class IfcExportSettings:
settings.should_roundtrip_native = scene_bim.import_export_should_roundtrip_native
settings.should_export_from_memory = scene_bim.export_should_export_from_memory
settings.context_tree = []
for ifc_context in ["model", "plan"]:
if getattr(scene_bim, "has_{}_context".format(ifc_context)):
subcontexts = {}
for subcontext in getattr(scene_bim, "{}_subcontexts".format(ifc_context)):
subcontexts.setdefault(subcontext.name, []).append(subcontext.target_view)
settings.context_tree.append(
{
"name": ifc_context.title(),
"subcontexts": [{"name": key, "target_views": value} for key, value in subcontexts.items()],
}
)
return settings
@@ -411,8 +411,6 @@ class IfcImporter:
self.profile_code("Patching ifc")
self.set_units()
self.profile_code("Set units")
self.create_geometric_representation_contexts()
self.profile_code("Create contexts")
self.create_project()
self.profile_code("Create project")
self.create_classifications()
@@ -853,7 +851,6 @@ class IfcImporter:
self.add_element_classifications(element, obj)
self.add_element_document_relations(element, obj)
self.add_type_product_psets(element, obj)
self.add_product_representations(element, obj)
self.type_collection.objects.link(obj)
self.type_products[element.GlobalId] = obj
@@ -984,7 +981,6 @@ class IfcImporter:
self.add_defines_by_type_relation(element, obj)
self.add_opening_relation(element, obj)
self.add_product_definitions(element, obj)
self.add_product_representations(element, obj)
self.added_data[element.GlobalId] = obj
if element.is_a("IfcOpeningElement"):
@@ -1365,24 +1361,6 @@ class IfcImporter:
bpy.ops.mesh.normals_make_consistent(context_override)
bpy.ops.object.editmode_toggle(context_override)
def add_product_representations(self, element, obj):
if element.is_a("IfcProduct"):
if not element.Representation:
return
for r in element.Representation.Representations:
new = obj.BIMObjectProperties.representations.add()
new.name = r.RepresentationIdentifier
new.type = r.RepresentationType
new.ifc_definition_id = r.id()
elif element.is_a("IfcTypeProduct"):
if not element.RepresentationMaps:
return
for r in element.RepresentationMaps:
new = obj.BIMObjectProperties.representations.add()
new.name = r.MappedRepresentation.RepresentationIdentifier
new.type = r.MappedRepresentation.RepresentationType
new.ifc_definition_id = r.MappedRepresentation.id()
def add_product_definitions(self, element, obj):
if not hasattr(element, "IsDefinedBy") or not element.IsDefinedBy:
return
@@ -1540,28 +1518,6 @@ class IfcImporter:
unit.Prefix + "/" if hasattr(unit, "Prefix") and unit.Prefix else "", name
)
def create_geometric_representation_contexts(self):
bpy.context.scene.BIMProperties.has_model_context = False
for context in self.file.by_type("IfcGeometricRepresentationContext"):
if context.is_a("IfcGeometricRepresentationSubContext"):
if not context.ContextIdentifier:
# Revit creates invalid contexts, so we just ignore them
continue
if context.ContextType == "Model":
subcontexts = bpy.context.scene.BIMProperties.model_subcontexts
elif context.ContextType == "Plan":
subcontexts = bpy.context.scene.BIMProperties.plan_subcontexts
if subcontexts.get(context.ContextIdentifier):
continue
subcontext = subcontexts.add()
subcontext.name = context.ContextIdentifier
subcontext.target_view = context.TargetView
subcontext.ifc_definition_id = context.id()
elif context.ContextType == "Model":
bpy.context.scene.BIMProperties.has_model_context = True
elif context.ContextType == "Plan":
bpy.context.scene.BIMProperties.has_plan_context = True
def create_project(self):
self.project = {"ifc": self.file.by_type("IfcProject")[0]}
if self.project["ifc"].GlobalId in self.existing_elements:
@@ -2064,23 +2020,6 @@ class IfcImporter:
):
return representation.Items[0].MappingTarget
def get_geometry_type(self, element):
tree = []
if hasattr(element, "Representation"):
tree = self.file.traverse(element.Representation)
elif hasattr(element, "RepresentationMaps"):
for representation_map in element.RepresentationMaps:
tree.extend(self.file.traverse(representation_map))
representations = [
e
for e in tree
if e.is_a("IfcRepresentation")
and e.RepresentationIdentifier == "Body"
and e.RepresentationType != "MappedRepresentation"
]
for representation in representations:
return representation.Items[0].is_a()
def create_mesh(self, element, shape, is_curve=False):
try:
if hasattr(shape, "geometry"):
@@ -2091,7 +2030,13 @@ class IfcImporter:
if is_curve:
return self.create_curve(geometry)
mesh = bpy.data.meshes.new(geometry.id)
representation_id = geometry.id
if "-" in representation_id:
representation_id = int(re.sub(r"\D", "", representation_id.split("-")[0]))
else:
representation_id = int(re.sub(r"\D", "", representation_id))
mesh = bpy.data.meshes.new("{}/{}".format(
self.file.by_id(representation_id).ContextOfItems.id(), geometry.id))
if geometry.faces:
num_vertices = len(geometry.verts) // 3
@@ -2132,7 +2077,6 @@ class IfcImporter:
ios_materials.append(mat.name)
mesh["ios_materials"] = ios_materials
mesh["ios_material_ids"] = geometry.material_ids
mesh.BIMMeshProperties.geometry_type = str(self.get_geometry_type(element))
return mesh
except:
self.ifc_import_settings.logger.error("Could not create mesh for %s", element)
@@ -1,6 +1,5 @@
import blenderbim.bim.ifc
from blenderbim.bim.ifc import IfcStore
is_loaded = False
class Data:
is_loaded = False
@@ -8,7 +7,7 @@ class Data:
@classmethod
def load(cls):
file = blenderbim.bim.ifc.IfcStore.get_file()
file = IfcStore.get_file()
if not file:
return
cls.contexts = {}
@@ -1,22 +1,25 @@
import bpy
import blenderbim.bim.ifc
import blenderbim.bim.module.context.add_context as add_context
import blenderbim.bim.module.context.remove_context as remove_context
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim.module.context.data import Data
class AddSubcontext(bpy.types.Operator):
bl_idname = "bim.add_subcontext"
bl_label = "Add Subcontext"
def execute(self, context):
self.file = blenderbim.bim.ifc.IfcStore.get_file()
usecase = add_context.Usecase(self.file, {
"context": bpy.context.scene.BIMProperties.available_contexts,
"subcontext": bpy.context.scene.BIMProperties.available_subcontexts,
"target_view": bpy.context.scene.BIMProperties.available_target_views,
})
self.file = IfcStore.get_file()
usecase = add_context.Usecase(
self.file,
{
"context": bpy.context.scene.BIMProperties.available_contexts,
"subcontext": bpy.context.scene.BIMProperties.available_subcontexts,
"target_view": bpy.context.scene.BIMProperties.available_target_views,
},
)
result = usecase.execute()
Data.load()
return {"FINISHED"}
@@ -27,11 +30,8 @@ class RemoveSubcontext(bpy.types.Operator):
ifc_definition_id: bpy.props.IntProperty()
def execute(self, context):
self.file = blenderbim.bim.ifc.IfcStore.get_file()
usecase = remove_context.Usecase(self.file, {
"context": self.file.by_id(self.ifc_definition_id)
})
self.file = IfcStore.get_file()
usecase = remove_context.Usecase(self.file, {"context": self.file.by_id(self.ifc_definition_id)})
usecase.execute()
Data.load()
return {"FINISHED"}
@@ -0,0 +1,20 @@
import bpy
from . import ui, operator
classes = (
operator.AddRepresentation,
operator.SwitchRepresentation,
operator.RemoveRepresentation,
operator.BakeParametricGeometry,
operator.UpdateIfcRepresentation,
operator.GetRepresentationIfcParameters,
ui.BIM_PT_representations,
)
def register():
pass
def unregister():
pass
@@ -0,0 +1,17 @@
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {
"product": None,
"representation": None
}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
definition = self.settings["product"].Representation
if not definition:
definition = self.file.createIfcProductDefinitionShape()
representations = list(definition.Representations)
representations.append(self.settings["representation"])
definition.Representations = representations
@@ -0,0 +1,26 @@
from blenderbim.bim.ifc import IfcStore
class Data:
products = {}
@classmethod
def load(cls, product_id):
file = IfcStore.get_file()
if not file:
return
cls.products[product_id] = {"Representations": {}}
product = file.by_id(product_id)
if not product.Representation:
return
for representation in product.Representation.Representations:
c = representation.ContextOfItems
cls.products[product_id]["Representations"][int(representation.id())] = {
"RepresentationIdentifier": representation.RepresentationIdentifier,
"RepresentationType": representation.RepresentationType,
"ContextOfItems": {
"ContextType": c.ContextType,
"ContextIdentifier": c.ContextIdentifier,
"TargetView": c.TargetView if c.is_a("IfcGeometricRepresentationSubContext") else "",
}
}
@@ -0,0 +1,185 @@
import bpy
import numpy as np
import ifcopenshell
import logging
import blenderbim.bim.module.geometry.add_object_placement as add_object_placement
import blenderbim.bim.module.geometry.add_representation as add_representation
import blenderbim.bim.module.geometry.assign_styles as assign_styles
import blenderbim.bim.module.geometry.assign_representation as assign_representation
import blenderbim.bim.module.geometry.remove_representation as remove_representation
from blenderbim.bim.ifc import IfcStore
from blenderbim.bim import import_ifc
from blenderbim.bim.module.geometry.data import Data
class AddRepresentation(bpy.types.Operator):
bl_idname = "bim.add_representation"
bl_label = "Add Representation"
def execute(self, context):
obj = bpy.context.active_object
self.file = IfcStore.get_file()
self.context_id = bpy.context.scene.BIMProperties.contexts
element = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
usecase = add_representation.Usecase(self.file, {
"context": self.file.by_id(int(self.context_id)),
"geometry": obj.data,
"total_items": max(1, len(obj.material_slots)),
})
result = usecase.execute()
if not result:
print("Failed to write shape representation")
return {"FINISHED"}
usecase = assign_representation.Usecase(self.file, {
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"representation": result
})
usecase.execute()
existing_mesh = obj.data
existing_mesh.use_fake_user = True
mesh = obj.data.copy()
mesh.name = "{}/{}".format(self.context_id, result.id())
mesh.BIMMeshProperties.ifc_definition_id = int(result.id())
obj.data = mesh
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class SwitchRepresentation(bpy.types.Operator):
bl_idname = "bim.switch_representation"
bl_label = "Switch Representation"
ifc_definition_id: bpy.props.IntProperty()
def execute(self, context):
self.obj = bpy.context.active_object
self.file = IfcStore.get_file()
context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems
self.mesh_name = "{}/{}".format(context_of_items.id(), self.ifc_definition_id)
mesh = bpy.data.meshes.get(self.mesh_name)
if mesh:
self.obj.data.user_remap(mesh)
self.pull_mesh_from_ifc()
return {"FINISHED"}
def pull_mesh_from_ifc(self):
self.file = IfcStore.get_file()
logger = logging.getLogger("ImportIFC")
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, IfcStore.path, logger)
element = self.file.by_id(self.obj.BIMObjectProperties.ifc_definition_id)
settings = ifcopenshell.geom.settings()
settings.set(settings.INCLUDE_CURVES, True)
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id))
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
mesh.name = self.mesh_name
mesh.BIMMeshProperties.ifc_definition_id = self.ifc_definition_id
self.obj.data.user_remap(mesh)
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
material_creator.create(element, self.obj, mesh)
class RemoveRepresentation(bpy.types.Operator):
bl_idname = "bim.remove_representation"
bl_label = "Remove Representation"
ifc_definition_id: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
representation = self.file.by_id(self.ifc_definition_id)
obj = bpy.context.active_object
mesh = bpy.data.meshes.get("{}/{}".format(representation.ContextOfItems.id(), representation.id()))
if mesh:
if obj.data == mesh:
# TODO we can do better than this
void_mesh = bpy.data.meshes.get("Void")
if not void_mesh:
void_mesh = bpy.data.meshes.new("Void")
obj.data = void_mesh
bpy.data.meshes.remove(mesh)
usecase = remove_representation.Usecase(self.file, {"representation": representation})
result = usecase.execute()
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class BakeParametricGeometry(bpy.types.Operator):
bl_idname = "bim.bake_parametric_geometry"
bl_label = "Bake Parametric Geometry"
def execute(self, context):
obj = bpy.context.active_object
self.file = IfcStore.get_file()
usecase = add_object_placement.Usecase(self.file, {
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"matrix": np.array(obj.matrix_world)
})
result = usecase.execute()
element = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
usecase = add_representation.Usecase(self.file, {
"context": element.ContextOfItems,
"geometry": obj.data,
"total_items": max(1, len(obj.material_slots)),
})
result = usecase.execute()
if not result:
print("Failed to write shape representation")
return {"FINISHED"}
usecase = assign_styles.Usecase(self.file, {
"shape_representation": result,
"styles": [self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id) for s in obj.material_slots if s.material]
})
usecase.execute()
for inverse in self.file.get_inverse(element):
ifcopenshell.util.element.replace_attribute(inverse, element, result)
obj.data.BIMMeshProperties.ifc_definition_id = int(result.id())
Data.load(obj.BIMObjectProperties.ifc_definition_id)
return {"FINISHED"}
class UpdateIfcRepresentation(bpy.types.Operator):
bl_idname = "bim.update_ifc_representation"
bl_label = "Update IFC Representation"
index: bpy.props.IntProperty()
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.context.active_object
props = obj.data.BIMMeshProperties
parameter = props.ifc_parameters[self.index]
element = IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value
bpy.ops.bim.switch_representation(ifc_definition_id = props.ifc_definition_id)
return {"FINISHED"}
class GetRepresentationIfcParameters(bpy.types.Operator):
bl_idname = "bim.get_representation_ifc_parameters"
bl_label = "Get Representation IFC Parameters"
def execute(self, context):
self.file = IfcStore.get_file()
obj = bpy.context.active_object
props = obj.data.BIMMeshProperties
elements = IfcStore.get_file().traverse(IfcStore.get_file().by_id(props.ifc_definition_id))
for element in elements:
if not element.is_a("IfcRepresentationItem"):
continue
for i in range(0, len(element)):
if element.attribute_type(i) == "DOUBLE":
new = props.ifc_parameters.add()
new.name = "{}/{}".format(element.is_a(), element.attribute_name(i))
new.step_id = element.id()
new.type = element.attribute_type(i)
new.index = i
if element[i]:
new.value = element[i]
return {"FINISHED"}
@@ -0,0 +1,9 @@
class Usecase:
def __init__(self, file, settings=None):
self.file = file
self.settings = {"representation": None}
for key, value in settings.items():
self.settings[key] = value
def execute(self):
self.file.remove(self.settings["representation"])
@@ -0,0 +1,37 @@
import bpy
from bpy.types import Panel
from blenderbim.bim.module.geometry.data import Data
class BIM_PT_representations(Panel):
bl_label = "IFC Representations"
bl_idname = "BIM_PT_representations"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
def draw(self, context):
layout = self.layout
props = context.active_object.BIMObjectProperties
if props.ifc_definition_id not in Data.products:
Data.load(props.ifc_definition_id)
representations = Data.products[props.ifc_definition_id]["Representations"]
if not representations:
layout.label(text="No representations found")
row = layout.row(align=True)
row.prop(bpy.context.scene.BIMProperties, "contexts", text="")
row.operator("bim.add_representation", icon="ADD", text="")
for ifc_definition_id, representation in representations.items():
row = self.layout.row(align=True)
row.label(text=representation["ContextOfItems"]["ContextType"])
row.label(text=representation["ContextOfItems"]["ContextIdentifier"])
row.label(text=representation["ContextOfItems"]["TargetView"])
row.label(text=representation["RepresentationType"])
row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="").ifc_definition_id = ifc_definition_id
row.operator("bim.remove_representation", icon="X", text="").ifc_definition_id = ifc_definition_id
@@ -2441,143 +2441,6 @@ class ActivateView(bpy.types.Operator):
return {"FINISHED"}
class AddRepresentation(bpy.types.Operator):
bl_idname = "bim.add_representation"
bl_label = "Add Representation"
def execute(self, context):
self.obj = bpy.context.active_object
self.file = ifc.IfcStore.get_file()
if "/" not in self.obj.data.name:
self.obj.data.name = ifcopenshell.guid.compress(str(uuid.uuid4()).replace("-", ""))
self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name
self.context = bpy.context.scene.BIMProperties.available_contexts
self.subcontext = bpy.context.scene.BIMProperties.available_subcontexts
self.target_view = bpy.context.scene.BIMProperties.available_target_views
existing_mesh = self.obj.data
existing_mesh.use_fake_user = True
mesh = self.obj.data.copy()
mesh.name = "{}/{}/{}/{}".format(
self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]
)
representation = self.obj.BIMObjectProperties.representations.add()
representation.name = self.subcontext
representation.type = "Brep" if self.file.schema == "IFC2X3" else "Tessellation"
mesh.use_fake_user = True
self.obj.data = mesh
# TODO: push representation
return {"FINISHED"}
def push_mesh_to_ifc(self):
self.file = ifc.IfcStore.get_file()
logger = logging.getLogger("ImportIFC")
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
element = self.file.by_id(self.obj.BIMObjectProperties.ifc_definition_id)
settings = ifcopenshell.geom.settings()
settings.set(settings.INCLUDE_CURVES, True)
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id))
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
mesh.name = "{}/{}/{}/{}".format(
self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]
)
self.obj.data = mesh
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
material_creator.create(element, self.obj, mesh)
return mesh
class SwitchRepresentation(bpy.types.Operator):
bl_idname = "bim.switch_representation"
bl_label = "Switch Representation"
ifc_definition_id: bpy.props.IntProperty()
def execute(self, context):
self.obj = bpy.context.active_object
self.file = ifc.IfcStore.get_file()
if "/" not in self.obj.data.name:
self.obj.data.name = ifcopenshell.guid.compress(str(uuid.uuid4()).replace("-", ""))
self.obj.data.name = "Model/Body/MODEL_VIEW/" + self.obj.data.name
context_of_items = self.file.by_id(self.ifc_definition_id).ContextOfItems
self.context = context_of_items.ContextType
self.subcontext = context_of_items.ContextIdentifier
self.target_view = context_of_items.TargetView
existing_mesh = self.obj.data
existing_mesh.use_fake_user = True
mesh = bpy.data.meshes.get(
"{}/{}/{}/{}".format(self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3])
)
if not mesh:
mesh = self.pull_mesh_from_ifc()
mesh.use_fake_user = True
self.obj.data = mesh
return {"FINISHED"}
def pull_mesh_from_ifc(self):
self.file = ifc.IfcStore.get_file()
logger = logging.getLogger("ImportIFC")
ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
element = self.file.by_id(self.obj.BIMObjectProperties.ifc_definition_id)
settings = ifcopenshell.geom.settings()
settings.set(settings.INCLUDE_CURVES, True)
shape = ifcopenshell.geom.create_shape(settings, self.file.by_id(self.ifc_definition_id))
ifc_importer = import_ifc.IfcImporter(ifc_import_settings)
ifc_importer.file = self.file
mesh = ifc_importer.create_mesh(element, shape)
mesh.name = "{}/{}/{}/{}".format(
self.context, self.subcontext, self.target_view, self.obj.data.name.split("/")[3]
)
self.obj.data = mesh
material_creator = import_ifc.MaterialCreator(ifc_import_settings, ifc_importer)
material_creator.create(element, self.obj, mesh)
return mesh
class RemoveRepresentation(bpy.types.Operator):
bl_idname = "bim.remove_representation"
bl_label = "Remove Representation"
index: bpy.props.IntProperty()
def execute(self, context):
# TODO This should be refactored into a testable agnostic module. See #1222.
# Prep work
self.file = ifc.IfcStore.get_file()
obj = bpy.context.active_object
representation = obj.BIMObjectProperties.representations[self.index]
element = self.file.by_id(representation.ifc_definition_id)
c = element.ContextOfItems
# Blender work
if "/" not in obj.data.name:
obj.data.name = "Model/Body/MODEL_VIEW/" + obj.data.name
mesh = bpy.data.meshes.get(
"{}/{}/{}/{}".format(c.ContextType, c.ContextIdentifier, c.TargetView, obj.data.name.split("/")[3])
)
if mesh:
if obj.data == mesh:
# TODO we can do better than this
void_name = "Void/Void/Void/" + obj.data.name.split("/")[3]
void_mesh = bpy.data.meshes.get(void_name)
if not void_mesh:
void_mesh = bpy.data.meshes.new(void_name)
obj.data = void_mesh
bpy.data.meshes.remove(mesh)
obj.BIMObjectProperties.representations.remove(self.index)
# IFC work
# TODO: this works as a MVP but leaves a bunch of junk in the file. See #1222.
self.file.remove(element)
return {"FINISHED"}
class OpenUpstream(bpy.types.Operator):
bl_idname = "bim.open_upstream"
bl_label = "Open Upstream Reference"
@@ -4281,122 +4144,6 @@ class RefreshDrawingList(bpy.types.Operator):
return {"FINISHED"}
class GetRepresentationIfcParameters(bpy.types.Operator):
bl_idname = "bim.get_representation_ifc_parameters"
bl_label = "Get Representation IFC Parameters"
def execute(self, context):
self.file = ifc.IfcStore.get_file()
obj = bpy.context.active_object
props = obj.data.BIMMeshProperties
element_id = self.get_ifc_definition_id(obj)
if not element_id:
return {"FINISHED"}
elements = ifc.IfcStore.get_file().traverse(ifc.IfcStore.get_file().by_id(element_id))
for element in elements:
if not element.is_a("IfcRepresentationItem"):
continue
for i in range(0, len(element)):
if element.attribute_type(i) == "DOUBLE":
new = props.ifc_parameters.add()
new.name = "{}/{}".format(element.is_a(), element.attribute_name(i))
new.step_id = element.id()
new.type = element.attribute_type(i)
new.index = i
if element[i]:
new.value = element[i]
return {"FINISHED"}
def get_ifc_definition_id(self, obj):
if "/" not in obj.data.name:
context, subcontext, target_view = ("Model", "Body", "MODEL_VIEW")
else:
context, subcontext, target_view = obj.data.name.split("/")[0:3]
for representation in obj.BIMObjectProperties.representations:
element = self.file.by_id(representation.ifc_definition_id)
if ifcopenshell.util.element.is_representation_of_context(element, context, subcontext, target_view):
return representation.ifc_definition_id or None
class BakeParametricGeometry(bpy.types.Operator):
bl_idname = "bim.bake_parametric_geometry"
bl_label = "Bake Parametric Geometry"
def execute(self, context):
obj = bpy.context.active_object
self.file = ifc.IfcStore.get_file()
element = self.file.by_id(obj.data.BIMMeshProperties.ifc_definition_id)
import blenderbim.bim.module.geometry.add_object_placement as add_object_placement
usecase = add_object_placement.Usecase(self.file, {
"product": self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
"matrix": np.array(obj.matrix_world)
})
result = usecase.execute()
import blenderbim.bim.module.geometry.add_shape_representation as add_shape_representation
usecase = add_shape_representation.Usecase(self.file, {
"context": element.ContextOfItems,
"geometry": obj.data,
"total_items": max(1, len(obj.material_slots)),
})
result = usecase.execute()
if not result:
print("Failed to write shape representation")
return {"FINISHED"}
import blenderbim.bim.module.geometry.assign_styles as assign_styles
usecase = assign_styles.Usecase(self.file, {
"shape_representation": result,
"styles": [self.file.by_id(s.material.BIMMaterialProperties.ifc_style_id) for s in obj.material_slots if s.material]
})
usecase.execute()
for inverse in self.file.get_inverse(element):
ifcopenshell.util.element.replace_attribute(inverse, element, result)
obj.data.BIMMeshProperties.ifc_definition_id = int(result.id())
return {"FINISHED"}
class UpdateIfcRepresentation(bpy.types.Operator):
bl_idname = "bim.update_ifc_representation"
bl_label = "Update IFC Representation"
index: bpy.props.IntProperty()
def execute(self, context):
self.file = ifc.IfcStore.get_file()
props = bpy.context.active_object.data.BIMMeshProperties
parameter = props.ifc_parameters[self.index]
element = ifc.IfcStore.get_file().by_id(parameter.step_id)[parameter.index] = parameter.value
self.recreate_ifc_representation()
return {"FINISHED"}
def recreate_ifc_representation(self):
props = bpy.context.active_object.data.BIMMeshProperties
logger = logging.getLogger("ImportIFC")
self.ifc_import_settings = import_ifc.IfcImportSettings.factory(bpy.context, ifc.IfcStore.path, logger)
element = ifc.IfcStore.get_file().by_id(self.get_ifc_definition_id(bpy.context.active_object))
settings = ifcopenshell.geom.settings()
shape = ifcopenshell.geom.create_shape(settings, element)
ifc_importer = import_ifc.IfcImporter(self.ifc_import_settings)
ifc_importer.file = ifc.IfcStore.get_file()
mesh = ifc_importer.create_mesh(element, shape)
bpy.context.active_object.data.user_remap(mesh)
ifc_importer.material_creator.mesh = mesh
if ifc_importer.material_creator.parse_representation(element):
ifc_importer.material_creator.assign_material_slots_to_faces(bpy.context.active_object)
def get_ifc_definition_id(self, obj):
if "/" not in obj.data.name:
context, subcontext, target_view = ("Model", "Body", "MODEL_VIEW")
else:
context, subcontext, target_view = obj.data.name.split("/")[0:3]
for representation in obj.BIMObjectProperties.representations:
c = self.file.by_id(self.ifc_definition_id).ContextOfItems
if c.ContextType == context and c.ContextIdentifier == subcontext and c.TargetView == target_view:
return representation.ifc_definition_id or None
class BlenderClasher:
def process_clash_set(self):
import collision
+14 -33
View File
@@ -54,20 +54,6 @@ vector_styles_enum = []
@persistent
def setDefaultProperties(scene):
if (
bpy.context.scene.BIMProperties.has_model_context
and len(bpy.context.scene.BIMProperties.model_subcontexts) == 0
):
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
subcontext.name = "Body"
subcontext.target_view = "MODEL_VIEW"
subcontext = bpy.context.scene.BIMProperties.model_subcontexts.add()
subcontext.name = "Box"
subcontext.target_view = "MODEL_VIEW"
if bpy.context.scene.BIMProperties.has_plan_context and len(bpy.context.scene.BIMProperties.plan_subcontexts) == 0:
subcontext = bpy.context.scene.BIMProperties.plan_subcontexts.add()
subcontext.name = "Annotation"
subcontext.target_view = "PLAN_VIEW"
if len(bpy.context.scene.DocProperties.drawing_styles) == 0:
drawing_style = bpy.context.scene.DocProperties.drawing_styles.add()
drawing_style.name = "Technical"
@@ -501,6 +487,19 @@ def getMaterialTypes(self, context):
return materialtypes_enum
def getContexts(self, context):
from blenderbim.bim.module.context.data import Data
if not Data.is_loaded:
Data.load()
results = []
for ifc_id, context in Data.contexts.items():
results.append((str(ifc_id), context["ContextType"], ""))
for ifc_id2, subcontext in context["HasSubContexts"].items():
results.append((str(ifc_id2), "{}/{}/{}".format(
subcontext["ContextType"], subcontext["ContextIdentifier"], subcontext["TargetView"]), ""))
return results
def getSubcontexts(self, context):
global subcontexts_enum
subcontexts_enum.clear()
@@ -551,19 +550,6 @@ class Attribute(PropertyGroup):
float_value: FloatProperty(name="Value")
class Subcontext(PropertyGroup):
name: StringProperty(name="Name")
context: StringProperty(name="Context")
target_view: StringProperty(name="Target View")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class Representation(PropertyGroup):
name: StringProperty(name="Name")
type: StringProperty(name="Type")
ifc_definition_id: IntProperty(name="IFC Definition ID")
class MaterialLayer(PropertyGroup):
name: StringProperty(name="Name")
material: PointerProperty(name="Material", type=bpy.types.Material)
@@ -1321,10 +1307,7 @@ class BIMProperties(PropertyGroup):
classification: EnumProperty(items=getClassifications, name="Classification", update=refreshReferences)
active_classification_name: StringProperty(name="Active Classification Name")
classifications: CollectionProperty(name="Classifications", type=Classification)
has_model_context: BoolProperty(name="Has Model Context", default=True)
has_plan_context: BoolProperty(name="Has Plan Context", default=True)
model_subcontexts: CollectionProperty(name="Model Subcontexts", type=Subcontext)
plan_subcontexts: CollectionProperty(name="Plan Subcontexts", type=Subcontext)
contexts: EnumProperty(items=getContexts, name="Contexts")
available_contexts: EnumProperty(items=[("Model", "Model", ""), ("Plan", "Plan", "")], name="Available Contexts")
available_subcontexts: EnumProperty(items=getSubcontexts, name="Available Subcontexts")
available_target_views: EnumProperty(items=getTargetViews, name="Available Target Views")
@@ -1502,7 +1485,6 @@ class BIMObjectProperties(PropertyGroup):
has_boundary_condition: BoolProperty(name="Has Boundary Condition")
boundary_condition: PointerProperty(name="Boundary Condition", type=BoundaryCondition)
structural_member_connection: PointerProperty(name="Structural Member Connection", type=bpy.types.Object)
representations: CollectionProperty(name="Representations", type=Representation)
# Address applies to IfcSite's SiteAddress and IfcBuilding's BuildingAddress
address: PointerProperty(name="Address", type=Address)
@@ -1553,7 +1535,6 @@ class BIMMeshProperties(PropertyGroup):
is_swept_solid: BoolProperty(name="Is Swept Solid")
swept_solids: CollectionProperty(name="Swept Solids", type=SweptSolid)
is_parametric: BoolProperty(name="Is Parametric", default=False)
geometry_type: StringProperty(name="Geometry Type")
ifc_definition: StringProperty(name="IFC Definition")
ifc_parameters: CollectionProperty(name="IFC Parameters", type=IfcParameter)
active_representation_item_index: IntProperty(name="Active Representation Item Index")
-38
View File
@@ -626,42 +626,6 @@ class BIM_PT_constraint_relations(Panel):
layout.label(text="Constraint is invalid")
class BIM_PT_representations(Panel):
bl_label = "IFC Representations"
bl_idname = "BIM_PT_representations"
bl_options = {"DEFAULT_CLOSED"}
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
def draw(self, context):
layout = self.layout
props = context.active_object.BIMObjectProperties
if not props.representations:
layout.label(text="No representations found")
row = layout.row(align=True)
row.prop(bpy.context.scene.BIMProperties, "available_contexts", text="")
row.prop(bpy.context.scene.BIMProperties, "available_subcontexts", text="")
row.prop(bpy.context.scene.BIMProperties, "available_target_views", text="")
row.operator("bim.add_representation", icon="ADD", text="")
self.file = ifc.IfcStore.get_file()
for index, representation in enumerate(props.representations):
row = layout.row(align=True)
row.prop(representation, "name", text="")
row.prop(representation, "type", text="")
if not representation.ifc_definition_id:
continue
context_of_items = self.file.by_id(representation.ifc_definition_id).ContextOfItems
if context_of_items.is_a() == "IfcGeometricRepresentationContext":
continue
op = row.operator("bim.switch_representation", icon="OUTLINER_DATA_MESH", text="")
op.ifc_definition_id = representation.ifc_definition_id
row.operator("bim.remove_representation", icon="X", text="").index = index
class BIM_PT_classification_references(Panel):
bl_label = "IFC Classification References"
bl_idname = "BIM_PT_classification_references"
@@ -809,8 +773,6 @@ class BIM_PT_mesh(Panel):
layout = self.layout
props = context.active_object.data.BIMMeshProperties
row = layout.row()
row.prop(props, "geometry_type")
layout.label(text="IFC Parameters:")
row = layout.row()
row.operator("bim.get_representation_ifc_parameters")