Finally, a way to actually add an IFC element directly

Previously, the way to add an IFC element would be to 1) create a blender element then assign or 2) activate bim tool, launch type manager, click create type, fill out field, or 3) using various menus for parametric stuff like stairs. This is now the new proposed way to do things and everything will consolidate to here - and it allows you to directly create non-mesh geometry immediately too!
This commit is contained in:
Dion Moult
2024-09-23 18:21:44 +10:00
parent 194acb078c
commit 23db38d0e9
11 changed files with 384 additions and 39 deletions
@@ -90,6 +90,8 @@ class ViewportData:
modes.append(item_mode) modes.append(item_mode)
elif tool.Geometry.is_representation_item(obj): elif tool.Geometry.is_representation_item(obj):
modes.append(edit_mode) modes.append(edit_mode)
else: # A regular Blender object
modes.append(edit_mode)
return modes return modes
@@ -115,17 +115,13 @@ class ItemDecorator:
continue continue
edges = selected_edges edges = selected_edges
verts = selected_verts verts = selected_verts
offset = len(selected_verts) tris = selected_tris
selected_verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices])
selected_tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles])
else: else:
offset = len(unselected_verts)
unselected_verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices])
unselected_tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles])
edges = unselected_edges edges = unselected_edges
verts = unselected_verts verts = unselected_verts
i = len(verts) tris = unselected_tris
i = len(verts)
matrix_world = obj.matrix_world matrix_world = obj.matrix_world
bbox_verts = [matrix_world @ Vector(co) for co in obj.bound_box] bbox_verts = [matrix_world @ Vector(co) for co in obj.bound_box]
bbox_edges = [ bbox_edges = [
@@ -145,6 +141,16 @@ class ItemDecorator:
edges.extend(bbox_edges) edges.extend(bbox_edges)
verts.extend(bbox_verts) verts.extend(bbox_verts)
if (total_tris := len(obj.data.loop_triangles)) == 0:
continue
if total_tris > 1000: # For performance
continue
offset = len(verts)
verts.extend([tuple(obj.matrix_world @ v.co) for v in obj.data.vertices])
tris.extend([tuple([i + offset for i in t.vertices]) for t in obj.data.loop_triangles])
if unselected_verts: if unselected_verts:
self.draw_batch("LINES", unselected_verts, transparent_color(unselected_elements_color), unselected_edges) self.draw_batch("LINES", unselected_verts, transparent_color(unselected_elements_color), unselected_edges)
self.draw_batch("TRIS", unselected_verts, transparent_color(special_elements_color), unselected_tris) self.draw_batch("TRIS", unselected_verts, transparent_color(special_elements_color), unselected_tris)
@@ -1631,6 +1631,8 @@ class OverrideModeSetEdit(bpy.types.Operator, tool.Ifc.Operator):
bpy.ops.bim.import_representation_items() bpy.ops.bim.import_representation_items()
elif tool.Geometry.is_representation_item(obj): elif tool.Geometry.is_representation_item(obj):
self.enable_editing_representation_item(context, obj) self.enable_editing_representation_item(context, obj)
else: # A regular Blender object
self.enable_edit_mode(context)
def handle_multiple_selected_objects(self, context): def handle_multiple_selected_objects(self, context):
obj = context.active_object obj = context.active_object
@@ -2300,9 +2302,7 @@ class ImportRepresentationItems(bpy.types.Operator, tool.Ifc.Operator):
context.scene.BIMGeometryProperties.mode = "ITEM" context.scene.BIMGeometryProperties.mode = "ITEM"
context.scene.BIMGeometryProperties.is_changing_mode = False context.scene.BIMGeometryProperties.is_changing_mode = False
tool.Loader.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get()) tool.Loader.load_settings()
tool.Loader.settings.context_settings = tool.Loader.create_settings()
tool.Loader.settings.gross_context_settings = tool.Loader.create_settings(is_gross=True)
for item_id in set(obj.data["ios_item_ids"]): for item_id in set(obj.data["ios_item_ids"]):
item = tool.Ifc.get().by_id(item_id) item = tool.Ifc.get().by_id(item_id)
@@ -139,7 +139,7 @@ classes = (
ui.BIM_PT_roof, ui.BIM_PT_roof,
ui.BIM_MT_type_manager_menu, ui.BIM_MT_type_manager_menu,
ui.LaunchTypeManager, ui.LaunchTypeManager,
ui.BIM_MT_model, ui.BIM_MT_elements,
grid.BIM_OT_add_object, grid.BIM_OT_add_object,
stair.BIM_OT_add_stair, stair.BIM_OT_add_stair,
stair.AddStair, stair.AddStair,
@@ -216,8 +216,7 @@ def register():
bpy.types.Object.BIMRailingProperties = bpy.props.PointerProperty(type=prop.BIMRailingProperties) bpy.types.Object.BIMRailingProperties = bpy.props.PointerProperty(type=prop.BIMRailingProperties)
bpy.types.Object.BIMRoofProperties = bpy.props.PointerProperty(type=prop.BIMRoofProperties) bpy.types.Object.BIMRoofProperties = bpy.props.PointerProperty(type=prop.BIMRoofProperties)
bpy.types.VIEW3D_MT_mesh_add.append(ui.add_mesh_object_menu) bpy.types.VIEW3D_MT_add.prepend(ui.add_menu)
bpy.types.VIEW3D_MT_add.append(ui.add_menu)
bpy.app.handlers.load_post.append(handler.load_post) bpy.app.handlers.load_post.append(handler.load_post)
workspace.load_custom_icons() workspace.load_custom_icons()
@@ -248,7 +247,6 @@ def unregister():
del bpy.types.Object.BIMRoofProperties del bpy.types.Object.BIMRoofProperties
bpy.app.handlers.load_post.remove(handler.load_post) bpy.app.handlers.load_post.remove(handler.load_post)
bpy.types.VIEW3D_MT_mesh_add.remove(ui.add_mesh_object_menu)
bpy.types.VIEW3D_MT_add.remove(ui.add_menu) bpy.types.VIEW3D_MT_add.remove(ui.add_menu)
workspace.unload_custom_icons() workspace.unload_custom_icons()
+10 -19
View File
@@ -116,7 +116,8 @@ class LaunchTypeManager(bpy.types.Operator):
row.operator("bim.disable_add_type", icon="CANCEL", text="") row.operator("bim.disable_add_type", icon="CANCEL", text="")
else: else:
row = self.layout.row() row = self.layout.row()
row.operator("bim.enable_add_type", icon="ADD", text="Create New Type") # row.operator("bim.enable_add_type", icon="ADD", text="Create New Type")
row.operator("bim.launch_add_element", icon="ADD", text="Create New Type")
flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True) flow = self.layout.grid_flow(row_major=True, columns=3, even_columns=True, even_rows=True, align=True)
@@ -656,26 +657,16 @@ class BIM_PT_roof(bpy.types.Panel):
row.operator("bim.add_roof", icon="ADD", text="") row.operator("bim.add_roof", icon="ADD", text="")
class BIM_MT_model(Menu): class BIM_MT_elements(Menu):
bl_idname = "BIM_MT_model" bl_idname = "BIM_MT_elements"
bl_label = "Objects" bl_label = "IFC Elements"
def draw(self, context): def draw(self, context):
layout = self.layout # TODO consolidate in Item mode UI then remove
layout.operator("bim.add_empty_type", text="Empty Type", icon="EMPTY_AXIS") self.layout.operator("bim.add_potential_half_space_solid", text="Half Space", icon="ORIENTATION_NORMAL")
layout.operator("bim.add_potential_half_space_solid", text="Half Space Proxy", icon="ORIENTATION_NORMAL") self.layout.operator("bim.add_potential_opening", text="Opening", icon="CUBE")
layout.operator("bim.add_potential_opening", text="Opening Proxy", icon="CUBE")
def add_menu(self, context): def add_menu(self, context):
self.layout.menu("BIM_MT_model", icon="FILE_3D") self.layout.operator("bim.launch_add_element", icon_value=bonsai.bim.icons["IFC"].icon_id, text="IFC Element")
self.layout.separator()
def add_mesh_object_menu(self, context):
if context.mode == "OBJECT":
self.layout.separator()
self.layout.operator("mesh.add_stair", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Stair (Untyped)")
self.layout.operator("mesh.add_window", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Window (Untyped)")
self.layout.operator("mesh.add_door", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Door (Untyped)")
self.layout.operator("mesh.add_railing", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Railing (Untyped)")
self.layout.operator("mesh.add_roof", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Roof (Untyped)")
@@ -131,6 +131,7 @@ class CreateProject(bpy.types.Operator):
) )
bonsai.bim.schema.reload(tool.Ifc.get().schema_identifier) bonsai.bim.schema.reload(tool.Ifc.get().schema_identifier)
tool.Blender.register_toolbar() tool.Blender.register_toolbar()
tool.Root.reload_item_decorator()
def rollback(self, data): def rollback(self, data):
IfcStore.file = None IfcStore.file = None
@@ -20,10 +20,12 @@ import bpy
from . import ui, prop, operator from . import ui, prop, operator
classes = ( classes = (
operator.AddElement,
operator.AssignClass, operator.AssignClass,
operator.CopyClass, operator.CopyClass,
operator.DisableReassignClass, operator.DisableReassignClass,
operator.EnableReassignClass, operator.EnableReassignClass,
operator.LaunchAddElement,
operator.ReassignClass, operator.ReassignClass,
operator.UnlinkObject, operator.UnlinkObject,
prop.BIMRootProperties, prop.BIMRootProperties,
+90 -3
View File
@@ -42,6 +42,7 @@ class IfcClassData:
cls.data["ifc_products"] = cls.ifc_products() cls.data["ifc_products"] = cls.ifc_products()
cls.data["ifc_classes"] = cls.ifc_classes() cls.data["ifc_classes"] = cls.ifc_classes()
cls.data["ifc_classes_suggestions"] = cls.ifc_classes_suggestions() # Call AFTER cls.ifc_classes() cls.data["ifc_classes_suggestions"] = cls.ifc_classes_suggestions() # Call AFTER cls.ifc_classes()
cls.data["representation_template"] = cls.representation_template()
cls.data["contexts"] = cls.contexts() cls.data["contexts"] = cls.contexts()
cls.data["has_entity"] = cls.has_entity() cls.data["has_entity"] = cls.has_entity()
@@ -58,9 +59,7 @@ class IfcClassData:
"IfcElement", "IfcElement",
"IfcSpatialElement", "IfcSpatialElement",
"IfcSpatialElementType", "IfcSpatialElementType",
"IfcGroup",
"IfcStructuralItem", "IfcStructuralItem",
"IfcContext",
"IfcAnnotation", "IfcAnnotation",
"IfcRelSpaceBoundary", "IfcRelSpaceBoundary",
] ]
@@ -70,7 +69,6 @@ class IfcClassData:
"IfcElementType", "IfcElementType",
"IfcElement", "IfcElement",
"IfcSpatialStructureElement", "IfcSpatialStructureElement",
"IfcGroup",
"IfcStructuralItem", "IfcStructuralItem",
"IfcAnnotation", "IfcAnnotation",
"IfcRelSpaceBoundary", "IfcRelSpaceBoundary",
@@ -130,6 +128,95 @@ class IfcClassData:
suggestions[ifc_class].append(suggestion_dict) suggestions[ifc_class].append(suggestion_dict)
return suggestions return suggestions
@classmethod
def representation_template(cls):
ifc_class = bpy.context.scene.BIMRootProperties.ifc_class
templates = [
("EMPTY", "No Geometry", "Start with an empty object"),
None,
]
if (
hasattr(bpy.context, "selected_objects")
and len(bpy.context.selected_objects) > 0
and (obj := bpy.context.active_object)
and obj.type == "MESH"
):
templates.append(
(
"OBJ",
"Tessellation From Active Selection",
"Use the actively selected object as a template to create a new tessellation",
)
)
templates.extend(
[
(
"MESH",
"Custom Tessellation",
"Create a basic tessellated or faceted cube",
),
(
"EXTRUSION",
"Custom Extruded Solid",
"An extrusion from an arbitrary profile",
),
]
)
if ifc_class.endswith("Type") or ifc_class.endswith("Style"):
templates.extend(
[
None,
(
"LAYERSET_AXIS2",
"Vertical Layers",
"For objects similar to walls, will automatically add IfcMaterialLayerSet",
),
(
"LAYERSET_AXIS3",
"Horizontal Layers",
"For objects similar to slabs, will automatically add IfcMaterialLayerSet",
),
(
"PROFILESET",
"Extruded Profile",
"Create profile type object, automatically defines IfcMaterialProfileSet with the first profile from library",
),
]
)
if ifc_class in ("IfcWindowType", "IfcWindowStyle", "IfcWindow"):
templates.extend([None, ("WINDOW", "Window", "Parametric window")])
elif ifc_class in ("IfcDoorType", "IfcDoorStyle", "IfcDoor"):
templates.extend([None, ("DOOR", "Door", "Parametric door")])
elif ifc_class in ("IfcStairType", "IfcStairFlightType", "IfcStair", "IfcStairFlight"):
templates.extend([None, ("STAIR", "Stair", "Parametric stair")])
elif ifc_class in ("IfcRailingType", "IfcRailing"):
templates.extend([None, ("RAILING", "Railing", "Parametric railing")])
elif ifc_class in ("IfcRoofType", "IfcRoof"):
templates.extend([None, ("ROOF", "Roof", "Parametric roof with a constant pitch")])
elif ifc_class and "Segment" in ifc_class:
templates.extend(
(
None,
(
"FLOW_SEGMENT_RECTANGULAR",
"Rectangular Distribution Segment",
"Works similarly to Profile, has distribution ports",
),
(
"FLOW_SEGMENT_CIRCULAR",
"Circular Distribution Segment",
"Works similarly to Profile, has distribution ports",
),
(
"FLOW_SEGMENT_CIRCULAR_HOLLOW",
"Circular Hollow Distribution Segment",
"Works similarly to Profile, has distribution ports",
),
)
)
return templates
@classmethod @classmethod
def contexts(cls): def contexts(cls):
results = [] results = []
+243 -1
View File
@@ -17,6 +17,7 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
import bpy import bpy
import bmesh
import ifcopenshell import ifcopenshell
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.schema import ifcopenshell.util.schema
@@ -24,9 +25,12 @@ import ifcopenshell.util.element
import ifcopenshell.util.type import ifcopenshell.util.type
import bonsai.bim.handler import bonsai.bim.handler
import bonsai.core.root as core import bonsai.core.root as core
import bonsai.core.geometry
import bonsai.tool as tool import bonsai.tool as tool
import bonsai.bim.module.root.prop as root_prop
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from bonsai.bim.helper import get_enum_items from bonsai.bim.helper import get_enum_items, prop_with_search
from mathutils import Vector
class EnableReassignClass(bpy.types.Operator): class EnableReassignClass(bpy.types.Operator):
@@ -302,3 +306,241 @@ class CopyClass(bpy.types.Operator, tool.Ifc.Operator):
for obj in objects: for obj in objects:
core.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj) core.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
bonsai.bim.handler.refresh_ui_data() bonsai.bim.handler.refresh_ui_data()
class AddElement(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.add_element"
bl_label = "Add Element"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Add an IFC physical product, construction type, and more"
def invoke(self, context, event):
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
def _invoke(self, context, event):
return context.window_manager.invoke_props_dialog(self)
def _execute(self, context):
props = context.scene.BIMRootProperties
predefined_type = (
props.userdefined_type if props.ifc_predefined_type == "USERDEFINED" else props.ifc_predefined_type
)
representation_template = props.representation_template
ifc_context = None
if get_enum_items(props, "contexts", context):
ifc_context = int(props.contexts or "0") or None
if ifc_context:
ifc_context = tool.Ifc.get().by_id(ifc_context)
if representation_template in (
"EMPTY",
"LAYERSET_AXIS2",
"LAYERSET_AXIS3",
"PROFILESET",
) or representation_template.startswith("FLOW_SEGMENT_"):
mesh = None
else:
mesh = bpy.data.meshes.new("Mesh")
obj = bpy.data.objects.new(props.ifc_class[3:], mesh)
obj.location = bpy.context.scene.cursor.location
element = core.assign_class(
tool.Ifc,
tool.Collector,
tool.Root,
obj=obj,
ifc_class=props.ifc_class,
predefined_type=predefined_type,
should_add_representation=False,
)
if representation_template == "EMTPY" or not ifc_context:
pass
elif (
representation_template == "OBJ"
and (template_obj := context.active_object)
and template_obj.type == "MESH"
and len(template_obj.data.vertices)
):
obj.matrix_world = template_obj.matrix_world
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=0.5)
verts = [v.co / unit_scale for v in template_obj.data.vertices]
faces = [p.vertices[:] for p in template_obj.data.polygons]
item = builder.mesh(verts, faces)
bm.free()
representation = builder.get_representation(ifc_context, [item])
ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation)
bonsai.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
if not tool.Ifc.get_entity(template_obj):
bpy.data.objects.remove(template_obj)
elif representation_template == "MESH":
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
bm = bmesh.new()
bmesh.ops.create_cube(bm, size=0.5)
verts = [v.co / unit_scale for v in bm.verts]
faces = [[v.index for v in p.verts] for p in bm.faces]
item = builder.mesh(verts, faces)
bm.free()
representation = builder.get_representation(ifc_context, [item])
ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation)
bonsai.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
elif representation_template == "EXTRUSION":
builder = ifcopenshell.util.shape_builder.ShapeBuilder(tool.Ifc.get())
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
curve = builder.rectangle(size=Vector((0.5, 0.5)) / unit_scale)
item = builder.extrude(curve, magnitude=0.5 / unit_scale)
representation = builder.get_representation(ifc_context, [item])
ifcopenshell.api.geometry.assign_representation(tool.Ifc.get(), element, representation)
bonsai.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=obj,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
)
elif representation_template in ("LAYERSET_AXIS2", "LAYERSET_AXIS3"):
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
materials = tool.Ifc.get().by_type("IfcMaterial")
if materials:
material = materials[0] # Arbitrarily pick a material
else:
material = ifcopenshell.api.run("material.add_material", tool.Ifc.get(), name="Unknown")
rel = ifcopenshell.api.run(
"material.assign_material", tool.Ifc.get(), products=[element], type="IfcMaterialLayerSet"
)
layer_set = rel.RelatingMaterial
layer = ifcopenshell.api.run("material.add_layer", tool.Ifc.get(), layer_set=layer_set, material=material)
thickness = 0.1 # Arbitrary metric thickness for now
layer.LayerThickness = thickness / unit_scale
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="EPset_Parametric")
if representation_template == "LAYERSET_AXIS2":
axis = "AXIS2"
elif representation_template == "LAYERSET_AXIS3":
axis = "AXIS3"
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"LayerSetDirection": axis})
elif representation_template == "PROFILESET" or representation_template.startswith("FLOW_SEGMENT_"):
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
materials = tool.Ifc.get().by_type("IfcMaterial")
if materials:
material = materials[0] # Arbitrarily pick a material
else:
material = ifcopenshell.api.run("material.add_material", tool.Ifc.get(), name="Unknown")
if representation_template == "PROFILESET":
named_profiles = [p for p in tool.Ifc.get().by_type("IfcProfileDef") if p.ProfileName]
if named_profiles:
profile = named_profiles[0]
else:
size = 0.5 / unit_scale
profile = tool.Ifc.get().create_entity(
"IfcRectangleProfileDef", ProfileName="New Profile", ProfileType="AREA", XDim=size, YDim=size
)
else:
# NOTE: defaults dims are in meters / mm
# for now default names are hardcoded to mm
if representation_template == "FLOW_SEGMENT_RECTANGULAR":
default_x_dim = 0.4
default_y_dim = 0.2
profile_name = f"{props.ifc_class}-{default_x_dim*1000}x{default_y_dim*1000}"
profile = tool.Ifc.get().create_entity(
"IfcRectangleProfileDef",
ProfileName=profile_name,
ProfileType="AREA",
XDim=default_x_dim / unit_scale,
YDim=default_y_dim / unit_scale,
)
elif representation_template == "FLOW_SEGMENT_CIRCULAR":
default_diameter = 0.1
profile_name = f"{props.ifc_class}-{default_diameter*1000}"
profile = tool.Ifc.get().create_entity(
"IfcCircleProfileDef",
ProfileName=profile_name,
ProfileType="AREA",
Radius=(default_diameter / 2) / unit_scale,
)
elif representation_template == "FLOW_SEGMENT_CIRCULAR_HOLLOW":
default_diameter = 0.15
default_thickness = 0.005
profile_name = f"{props.ifc_class}-{default_diameter*1000}x{default_thickness*1000}"
profile = tool.Ifc.get().create_entity(
"IfcCircleHollowProfileDef",
ProfileName=profile_name,
ProfileType="AREA",
Radius=(default_diameter / 2) / unit_scale,
WallThickness=default_thickness,
)
rel = ifcopenshell.api.run(
"material.assign_material", tool.Ifc.get(), products=[element], type="IfcMaterialProfileSet"
)
profile_set = rel.RelatingMaterial
material_profile = ifcopenshell.api.run(
"material.add_profile", tool.Ifc.get(), profile_set=profile_set, material=material
)
ifcopenshell.api.run(
"material.assign_profile", tool.Ifc.get(), material_profile=material_profile, profile=profile
)
elif representation_template == "WINDOW":
with context.temp_override(active_object=obj):
bpy.ops.bim.add_window()
elif representation_template == "DOOR":
with context.temp_override(active_object=obj):
bpy.ops.bim.add_door()
elif representation_template == "STAIR":
with context.temp_override(active_object=obj):
bpy.ops.bim.add_stair()
elif representation_template == "RAILING":
with context.temp_override(active_object=obj):
bpy.ops.bim.add_railing()
elif representation_template == "ROOF":
with context.temp_override(active_object=obj):
bpy.ops.bim.add_roof()
def draw(self, context):
props = context.scene.BIMRootProperties
self.layout.use_property_split = True
self.layout.use_property_decorate = False
prop_with_search(self.layout, props, "ifc_product", text="Definition")
prop_with_search(self.layout, props, "ifc_class", should_click_ok_to_validate=True)
ifc_predefined_types = root_prop.get_ifc_predefined_types(context.scene.BIMRootProperties, context)
if ifc_predefined_types:
prop_with_search(self.layout, props, "ifc_predefined_type")
if props.ifc_predefined_type == "USERDEFINED":
row = self.layout.row()
row.prop(props, "ifc_userdefined_type")
prop_with_search(self.layout, props, "representation_template", text="Representation")
if props.representation_template != "EMPTY":
prop_with_search(self.layout, props, "contexts")
class LaunchAddElement(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.launch_add_element"
bl_label = "Add Element"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Add an IFC physical product, construction type, and more"
def execute(self, context):
bpy.ops.bim.add_element("INVOKE_DEFAULT")
return {"FINISHED"}
+8 -1
View File
@@ -41,6 +41,12 @@ def get_ifc_predefined_types(self, context):
return IfcClassData.data["ifc_predefined_types"] return IfcClassData.data["ifc_predefined_types"]
def get_representation_template(self, context):
if not IfcClassData.is_loaded:
IfcClassData.load()
return IfcClassData.data["representation_template"]
def refresh_classes(self, context): def refresh_classes(self, context):
enum = get_ifc_classes(self, context) enum = get_ifc_classes(self, context)
context.scene.BIMRootProperties.ifc_class = enum[0][0] context.scene.BIMRootProperties.ifc_class = enum[0][0]
@@ -110,11 +116,12 @@ def is_object_class_applicable(self, obj):
class BIMRootProperties(PropertyGroup): class BIMRootProperties(PropertyGroup):
contexts: EnumProperty(items=get_contexts, name="Contexts") contexts: EnumProperty(items=get_contexts, name="Contexts", options=set())
ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes) ifc_product: EnumProperty(items=get_ifc_products, name="Products", update=refresh_classes)
ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refresh_predefined_types) ifc_class: EnumProperty(items=get_ifc_classes, name="Class", update=refresh_predefined_types)
ifc_predefined_type: EnumProperty(items=get_ifc_predefined_types, name="Predefined Type", default=None) ifc_predefined_type: EnumProperty(items=get_ifc_predefined_types, name="Predefined Type", default=None)
ifc_userdefined_type: StringProperty(name="Userdefined Type") ifc_userdefined_type: StringProperty(name="Userdefined Type")
representation_template: bpy.props.EnumProperty(items=get_representation_template, name="Representation Template", default=0)
relating_class_object: PointerProperty( relating_class_object: PointerProperty(
type=bpy.types.Object, type=bpy.types.Object,
name="Copy Class", name="Copy Class",
+10 -1
View File
@@ -19,9 +19,10 @@
from __future__ import annotations from __future__ import annotations
import os import os
import re import re
import math
import bpy import bpy
import math
import bmesh import bmesh
import logging
import ifcopenshell.geom import ifcopenshell.geom
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.geolocation import ifcopenshell.util.geolocation
@@ -56,6 +57,14 @@ class Loader(bonsai.core.tool.Loader):
def set_unit_scale(cls, unit_scale: float) -> None: def set_unit_scale(cls, unit_scale: float) -> None:
cls.unit_scale = unit_scale cls.unit_scale = unit_scale
@classmethod
def load_settings(cls) -> None:
logger = logging.getLogger("ImportIFC")
cls.settings = bonsai.bim.import_ifc.IfcImportSettings.factory(bpy.context, None, logger)
cls.settings.contexts = ifcopenshell.util.representation.get_prioritised_contexts(tool.Ifc.get())
cls.settings.context_settings = cls.create_settings()
cls.settings.gross_context_settings = cls.create_settings(is_gross=True)
@classmethod @classmethod
def set_settings(cls, settings: bonsai.bim.import_ifc.IfcImportSettings) -> None: def set_settings(cls, settings: bonsai.bim.import_ifc.IfcImportSettings) -> None:
cls.settings = settings cls.settings = settings