See #5919. You can now add openings through "Add Element" just like everything else.

This means that openings can (and default to) start with extrusions. It
also starts to open up the possibility to add other types of feature
elements. Still really incomplete.
This commit is contained in:
Dion Moult
2025-01-25 20:43:08 +11:00
parent 2c531501c3
commit 75c6505121
6 changed files with 66 additions and 25 deletions
@@ -735,16 +735,10 @@ class OverrideDelete(bpy.types.Operator):
else:
bpy.data.objects.remove(obj)
while True:
has_removed_opening = False
openings = context.scene.BIMModelProperties.openings
for i, opening in enumerate(openings):
if not (element := tool.Ifc.get_entity(opening.obj)):
has_removed_opening = True
bpy.data.objects.remove(opening.obj)
context.scene.BIMModelProperties.openings.remove(i)
if not has_removed_opening:
break
for opening in context.scene.BIMModelProperties.openings:
if not tool.Ifc.get_entity(opening.obj):
bpy.data.objects.remove(opening.obj)
tool.Model.purge_scene_openings()
if self.is_batch:
old_file = tool.Ifc.get()
+7 -5
View File
@@ -22,7 +22,6 @@ import ifcopenshell.util.element
import ifcopenshell.util.schema
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc, get_class_suggestions
import bonsai.tool as tool
from bonsai.bim.ifc import IfcStore
from typing import Union
@@ -58,6 +57,7 @@ class IfcClassData:
products = [
"IfcElementType",
"IfcElement",
"IfcFeatureElement",
"IfcSpatialElement",
"IfcSpatialElementType",
"IfcStructuralItem",
@@ -69,6 +69,7 @@ class IfcClassData:
products = [
"IfcElementType",
"IfcElement",
"IfcFeatureElement",
"IfcSpatialStructureElement",
"IfcStructuralItem",
"IfcAnnotation",
@@ -87,10 +88,11 @@ class IfcClassData:
if tool.Ifc.get_schema() in ("IFC2X3", "IFC4"):
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
if ifc_product == "IfcElement":
names.remove("IfcOpeningElement")
if tool.Ifc.get_schema() == "IFC4":
# Yeah, weird isn't it.
names.remove("IfcOpeningStandardCase")
feature_elements = ifcopenshell.util.schema.get_subtypes(
tool.Ifc.schema().declaration_by_name("IfcFeatureElement")
)
for feature_element in feature_elements:
names.remove(feature_element.name())
version = tool.Ifc.get_schema()
return [(c, c, (get_entity_doc(version, c) or {}).get("description", "")) for c in sorted(names)]
+23 -2
View File
@@ -350,8 +350,13 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
def _invoke(self, context, event):
props = context.scene.BIMRootProperties
# For convenience, preselect OBJ representation template if applicable
if (obj := context.active_object) and obj.type == "MESH":
# For convenience, preselect OBJs if applicable
if props.ifc_product == "IfcFeatureElement":
if (obj := tool.Blender.get_active_object(is_selected=True)) and obj.type == "MESH":
props.featured_obj = obj
props.representation_template = "EXTRUSION"
props.representation_obj = None
elif (obj := tool.Blender.get_active_object(is_selected=True)) and obj.type == "MESH":
props.representation_template = "OBJ"
props.representation_obj = obj
# For convenience, preselect IFC class
@@ -369,6 +374,9 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
representation_template = props.representation_template
ifc_file = tool.Ifc.get()
if props.ifc_product == "IfcFeatureElement" and not props.featured_obj:
return self.report({"WARNING"}, "A featured element must be nominated.")
ifc_context = None
if get_enum_items(props, "contexts", context):
ifc_context = int(props.contexts or "0") or None
@@ -556,6 +564,16 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
elif representation_template == "ROOF":
with context.temp_override(active_object=obj, selected_objects=[]):
bpy.ops.bim.add_roof()
bpy.context.view_layer.update() # Ensures obj.matrix_world is correct
if props.ifc_product == "IfcFeatureElement":
tool.Feature.add_feature(props.featured_obj, [obj])
new = context.scene.BIMModelProperties.openings.add()
new.obj = obj
bpy.ops.bim.show_openings()
tool.Model.purge_scene_openings()
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
tool.Blender.set_active_object(obj)
@@ -577,6 +595,9 @@ class AddElement(bpy.types.Operator, tool.Ifc.Operator):
if props.ifc_predefined_type == "USERDEFINED":
row = self.layout.row()
row.prop(props, "ifc_userdefined_type")
if props.ifc_product == "IfcFeatureElement":
row = self.layout.row()
row.prop(props, "featured_obj", text="Featured Object")
prop_with_search(self.layout, props, "representation_template", text="Representation", should_click_ok=True)
if props.representation_template == "OBJ":
row = self.layout.row()
+23 -4
View File
@@ -57,17 +57,26 @@ def refresh_classes(self, context):
context.scene.BIMRootProperties.ifc_class = enum[0][0]
IfcClassData.load()
if self.ifc_product == "IfcFeatureElement":
if (obj := tool.Blender.get_active_object(is_selected=True)) and obj.type == "MESH":
self.featured_obj = obj
self.representation_template = "EXTRUSION"
self.representation_obj = None
elif (obj := tool.Blender.get_active_object(is_selected=True)) and obj.type == "MESH":
self.representation_template = "OBJ"
self.representation_obj = obj
# When switching between ElementType and Element, keep the same class and predefined type if possible
if context.scene.BIMRootProperties.ifc_product == "IfcElement":
if self.ifc_product == "IfcElement":
ifc_class = next(iter(ifcopenshell.util.type.get_applicable_entities(old_class)), None)
elif context.scene.BIMRootProperties.ifc_product == "IfcElementType":
elif self.ifc_product == "IfcElementType":
ifc_class = next(iter(ifcopenshell.util.type.get_applicable_types(old_class)), None)
else:
return
if ifc_class:
context.scene.BIMRootProperties.ifc_class = ifc_class
self.ifc_class = ifc_class
if old_predefined_type:
context.scene.BIMRootProperties.ifc_predefined_type = old_predefined_type
self.ifc_predefined_type = old_predefined_type
def refresh_predefined_types(self, context):
@@ -142,6 +151,10 @@ def poll_representation_obj(self, obj):
return obj.type == "MESH" and obj.data.polygons
def poll_featured_obj(self, obj):
return tool.Ifc.get_entity(obj)
class BIMRootProperties(PropertyGroup):
contexts: EnumProperty(items=get_contexts, name="Contexts", options=set())
name: StringProperty(name="Name")
@@ -150,6 +163,12 @@ class BIMRootProperties(PropertyGroup):
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_userdefined_type: StringProperty(name="Userdefined Type")
featured_obj: bpy.props.PointerProperty(
type=bpy.types.Object,
name="Featured Object",
poll=poll_featured_obj,
description="The feature will be applied to this object",
)
representation_template: bpy.props.EnumProperty(
items=get_representation_template, name="Representation Template", default=0
)
+4
View File
@@ -381,6 +381,10 @@ class Drawing:
def update_text_value(cls, obj): pass
@interface
class Feature:
def add_feature(cls, featured_obj, featured_objs): pass
@interface
class Geometry:
def change_object_data(cls, obj, data, is_global=False): pass
+5 -4
View File
@@ -27,10 +27,13 @@ from bonsai.tool.clash import Clash
from bonsai.tool.classification import Classification
from bonsai.tool.collector import Collector
from bonsai.tool.context import Context
from bonsai.tool.cost import Cost
from bonsai.tool.covering import Covering
from bonsai.tool.debug import Debug
from bonsai.tool.demo import Demo
from bonsai.tool.document import Document
from bonsai.tool.drawing import Drawing
from bonsai.tool.feature import Feature
from bonsai.tool.geometry import Geometry
from bonsai.tool.georeference import Georeference
from bonsai.tool.ifc import Ifc
@@ -45,18 +48,18 @@ from bonsai.tool.nest import Nest
from bonsai.tool.owner import Owner
from bonsai.tool.patch import Patch
from bonsai.tool.polyline import Polyline
from bonsai.tool.project import Project
from bonsai.tool.profile import Profile
from bonsai.tool.project import Project
from bonsai.tool.pset import Pset
from bonsai.tool.pset_template import PsetTemplate
from bonsai.tool.qto import Qto
from bonsai.tool.raycast import Raycast
from bonsai.tool.resource import Resource
from bonsai.tool.root import Root
from bonsai.tool.search import Search
from bonsai.tool.sequence import Sequence
from bonsai.tool.snap import Snap
from bonsai.tool.spatial import Spatial
from bonsai.tool.covering import Covering
from bonsai.tool.structural import Structural
from bonsai.tool.style import Style
from bonsai.tool.surveyor import Surveyor
@@ -64,6 +67,4 @@ from bonsai.tool.system import System
from bonsai.tool.tester import Tester
from bonsai.tool.type import Type
from bonsai.tool.unit import Unit
from bonsai.tool.search import Search
from bonsai.tool.cost import Cost
from bonsai.tool.web import Web