mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-30 12:09:51 +00:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user