mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
Deprecate type_class in favour of new simpler type manager workflow
This commit is contained in:
@@ -59,10 +59,8 @@ class AuthoringData:
|
||||
cls.data["relating_type_id"] = cls.relating_type_id() # only after .ifc_classes()
|
||||
cls.data["relating_type_name"] = cls.relating_type_name() # only after .relating_type_id()
|
||||
cls.data["predefined_type"] = cls.predefined_type() # only after .relating_type_id()
|
||||
cls.data["type_class"] = cls.type_class()
|
||||
|
||||
# only after .type_class()
|
||||
cls.data["type_predefined_type"] = cls.type_predefined_type()
|
||||
# only after .ifc_classes()
|
||||
cls.data["total_types"] = cls.total_types()
|
||||
cls.data["total_pages"] = cls.total_pages()
|
||||
cls.data["next_page"] = cls.next_page()
|
||||
@@ -78,7 +76,6 @@ class AuthoringData:
|
||||
cls.data["active_representation_type"] = cls.active_representation_type()
|
||||
cls.data["boundary_class"] = cls.boundary_class()
|
||||
cls.data["selected_material_usages"] = cls.selected_material_usages()
|
||||
cls.data["type_template"] = cls.type_template()
|
||||
|
||||
@classmethod
|
||||
def default_container(cls) -> str | None:
|
||||
@@ -97,41 +94,6 @@ class AuthoringData:
|
||||
version = tool.Ifc.get_schema()
|
||||
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def type_class(cls):
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcElementType")
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
names = [d.name() for d in declarations]
|
||||
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcSpatialStructureElementType")
|
||||
else:
|
||||
declaration = tool.Ifc.schema().declaration_by_name("IfcSpatialElementType")
|
||||
declarations = ifcopenshell.util.schema.get_subtypes(declaration)
|
||||
names.extend([d.name() for d in declarations])
|
||||
|
||||
if tool.Ifc.get_schema() in ("IFC2X3", "IFC4"):
|
||||
names.extend(("IfcDoorStyle", "IfcWindowStyle"))
|
||||
|
||||
version = tool.Ifc.get_schema()
|
||||
return [(c, c, get_entity_doc(version, c).get("description", "")) for c in sorted(names)]
|
||||
|
||||
@classmethod
|
||||
def type_predefined_type(cls):
|
||||
results = []
|
||||
declaration = tool.Ifc().schema().declaration_by_name(cls.props.type_class)
|
||||
version = tool.Ifc.get_schema()
|
||||
for attribute in declaration.attributes():
|
||||
if attribute.name() == "PredefinedType":
|
||||
results.extend(
|
||||
[
|
||||
(e, e, get_predefined_type_doc(version, cls.props.type_class, e))
|
||||
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||
]
|
||||
)
|
||||
break
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def type_thumbnail(cls):
|
||||
if not cls.data["relating_type_id"]:
|
||||
@@ -142,76 +104,15 @@ class AuthoringData:
|
||||
element = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
return cls.type_thumbnails.get(element.id(), None) or 0
|
||||
|
||||
@classmethod
|
||||
def type_template(cls):
|
||||
type_class = cls.props.type_class
|
||||
templates = [
|
||||
(
|
||||
"MESH",
|
||||
"Custom Mesh",
|
||||
"Use as a representation currently active object mesh or default cube if no object selected",
|
||||
),
|
||||
(
|
||||
"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",
|
||||
),
|
||||
("EMPTY", "Non-Geometric Type", "Start with an empty object"),
|
||||
]
|
||||
if not type_class:
|
||||
pass
|
||||
elif type_class in ("IfcWindowType", "IfcWindowStyle"):
|
||||
templates.append(("WINDOW", "Window", "Parametric window"))
|
||||
elif type_class in ("IfcDoorType", "IfcDoorStyle"):
|
||||
templates.append(("DOOR", "Door", "Parametric door"))
|
||||
elif type_class in ("IfcStairType", "IfcStairFlightType"):
|
||||
templates.append(("STAIR", "Stair", "Parametric stair"))
|
||||
elif type_class == "IfcRailingType":
|
||||
templates.append(("RAILING", "Railing", "Parametric railing"))
|
||||
elif type_class == "IfcRoofType":
|
||||
templates.append(("Roof", "Roof", "Parametric roof"))
|
||||
|
||||
templates.extend(
|
||||
(
|
||||
(
|
||||
"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
|
||||
def total_types(cls):
|
||||
type_class = cls.props.type_class
|
||||
return len(tool.Ifc.get().by_type(type_class)) if type_class else 0
|
||||
ifc_class = cls.props.ifc_class
|
||||
return len(tool.Ifc.get().by_type(ifc_class)) if ifc_class else 0
|
||||
|
||||
@classmethod
|
||||
def total_pages(cls):
|
||||
type_class = cls.props.type_class
|
||||
total_types = len(tool.Ifc.get().by_type(type_class)) if type_class else 0
|
||||
ifc_class = cls.props.ifc_class
|
||||
total_types = len(tool.Ifc.get().by_type(ifc_class)) if ifc_class else 0
|
||||
return math.ceil(total_types / cls.types_per_page)
|
||||
|
||||
@classmethod
|
||||
@@ -226,11 +127,11 @@ class AuthoringData:
|
||||
|
||||
@classmethod
|
||||
def paginated_relating_types(cls):
|
||||
type_class = cls.props.type_class
|
||||
if not type_class:
|
||||
ifc_class = cls.props.ifc_class
|
||||
if not ifc_class:
|
||||
return []
|
||||
results = []
|
||||
elements = natsorted(tool.Ifc.get().by_type(type_class), key=lambda e: e.Name or "Unnamed")
|
||||
elements = natsorted(tool.Ifc.get().by_type(ifc_class), key=lambda e: e.Name or "Unnamed")
|
||||
elements = elements[(cls.props.type_page - 1) * cls.types_per_page : cls.props.type_page * cls.types_per_page]
|
||||
for element in elements:
|
||||
predefined_type = ifcopenshell.util.element.get_predefined_type(element)
|
||||
@@ -250,45 +151,50 @@ class AuthoringData:
|
||||
|
||||
@classmethod
|
||||
def is_voidable_element(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
return element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement")
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
element = tool.Ifc.get_entity(active_object)
|
||||
return element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement")
|
||||
|
||||
@classmethod
|
||||
def has_visible_openings(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement"):
|
||||
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]:
|
||||
if tool.Ifc.get_object(opening):
|
||||
return True
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
element = tool.Ifc.get_entity(active_object)
|
||||
if element and element.is_a("IfcElement") and not element.is_a("IfcOpeningElement"):
|
||||
for opening in [r.RelatedOpeningElement for r in element.HasOpenings]:
|
||||
if tool.Ifc.get_object(opening):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def has_visible_boundaries(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element:
|
||||
if element.is_a("IfcRelSpaceBoundary"):
|
||||
return True
|
||||
for boundary in getattr(element, "BoundedBy", []):
|
||||
if tool.Ifc.get_object(boundary):
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
element = tool.Ifc.get_entity(active_object)
|
||||
if element:
|
||||
if element.is_a("IfcRelSpaceBoundary"):
|
||||
return True
|
||||
for boundary in getattr(element, "BoundedBy", []):
|
||||
if tool.Ifc.get_object(boundary):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def active_class(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element:
|
||||
return element.is_a()
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
element = tool.Ifc.get_entity(active_object)
|
||||
if element:
|
||||
return element.is_a()
|
||||
|
||||
@classmethod
|
||||
def active_material_usage(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element:
|
||||
return tool.Model.get_usage_type(element)
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
element = tool.Ifc.get_entity(active_object)
|
||||
if element:
|
||||
return tool.Model.get_usage_type(element)
|
||||
|
||||
@classmethod
|
||||
def active_representation_type(cls):
|
||||
if bpy.context.active_object:
|
||||
representation = tool.Geometry.get_active_representation(bpy.context.active_object)
|
||||
if active_object := tool.Blender.get_active_object():
|
||||
representation = tool.Geometry.get_active_representation(active_object)
|
||||
if representation and representation.is_a("IfcShapeRepresentation"):
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
return representation.RepresentationType
|
||||
@@ -317,7 +223,7 @@ class AuthoringData:
|
||||
if not ifc_classes:
|
||||
return []
|
||||
results = []
|
||||
ifc_class = cls.props.ifc_class
|
||||
ifc_class = tool.Blender.get_enum_safe(cls.props, "ifc_class")
|
||||
if not ifc_class and ifc_classes:
|
||||
ifc_class = ifc_classes[0][0]
|
||||
if ifc_class:
|
||||
@@ -328,7 +234,11 @@ class AuthoringData:
|
||||
|
||||
@classmethod
|
||||
def relating_type_name(cls):
|
||||
if relating_type_id := cls.props.relating_type_id:
|
||||
relating_type_id = tool.Blender.get_enum_safe(cls.props, "relating_type_id")
|
||||
relating_type_id_data = cls.data["relating_type_id"]
|
||||
if not relating_type_id and relating_type_id_data:
|
||||
relating_type_id = relating_type_id_data[0][0]
|
||||
if relating_type_id:
|
||||
return tool.Ifc.get().by_id(int(relating_type_id)).Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
@@ -345,7 +255,7 @@ class AuthoringData:
|
||||
@classmethod
|
||||
def selected_material_usages(cls):
|
||||
selected_usages = {}
|
||||
for obj in bpy.context.selected_objects:
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
|
||||
@@ -516,7 +516,7 @@ class MEPGenerator:
|
||||
break
|
||||
|
||||
def create_obstruction_type(self, segment):
|
||||
# code is very similar to "bim.add_type"
|
||||
# code is very similar to "bim.add_element"
|
||||
profile_set = ifcopenshell.util.element.get_material(segment, should_skip_usage=True)
|
||||
material_profile = profile_set.MaterialProfiles[0]
|
||||
profile = material_profile.Profile
|
||||
|
||||
@@ -67,37 +67,37 @@ class AddDefaultType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_element_type: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
ifc_file = tool.Ifc.get()
|
||||
props.type_class = self.ifc_element_type
|
||||
props = context.scene.BIMRootProperties
|
||||
props.ifc_product = "IfcElementType"
|
||||
props.ifc_class = self.ifc_element_type
|
||||
if self.ifc_element_type == "IfcWallType":
|
||||
if ifc_file.schema == "IFC2X3":
|
||||
props.type_predefined_type = "STANDARD"
|
||||
if tool.Ifc.get().schema == "IFC2X3":
|
||||
props.ifc_predefined_type = "STANDARD"
|
||||
else:
|
||||
props.type_predefined_type = "SOLIDWALL"
|
||||
props.type_template = "LAYERSET_AXIS2"
|
||||
props.ifc_predefined_type = "SOLIDWALL"
|
||||
props.representation_template = "LAYERSET_AXIS2"
|
||||
elif self.ifc_element_type == "IfcSlabType":
|
||||
props.type_predefined_type = "FLOOR"
|
||||
props.type_template = "LAYERSET_AXIS3"
|
||||
props.ifc_predefined_type = "FLOOR"
|
||||
props.representation_template = "LAYERSET_AXIS3"
|
||||
elif self.ifc_element_type == "IfcDoorType":
|
||||
props.type_predefined_type = "DOOR"
|
||||
props.type_template = "DOOR"
|
||||
props.ifc_predefined_type = "DOOR"
|
||||
props.representation_template = "DOOR"
|
||||
elif self.ifc_element_type == "IfcWindowType":
|
||||
props.type_predefined_type = "WINDOW"
|
||||
props.type_template = "WINDOW"
|
||||
props.ifc_predefined_type = "WINDOW"
|
||||
props.representation_template = "WINDOW"
|
||||
elif self.ifc_element_type == "IfcColumnType":
|
||||
props.type_predefined_type = "COLUMN"
|
||||
props.type_template = "PROFILESET"
|
||||
props.ifc_predefined_type = "COLUMN"
|
||||
props.representation_template = "PROFILESET"
|
||||
elif self.ifc_element_type == "IfcBeamType":
|
||||
props.type_predefined_type = "BEAM"
|
||||
props.type_template = "PROFILESET"
|
||||
props.ifc_predefined_type = "BEAM"
|
||||
props.representation_template = "PROFILESET"
|
||||
elif self.ifc_element_type == "IfcDuctSegmentType":
|
||||
props.type_predefined_type = "RIGIDSEGMENT"
|
||||
props.type_template = "FLOW_SEGMENT_RECTANGULAR"
|
||||
props.ifc_predefined_type = "RIGIDSEGMENT"
|
||||
props.representation_template = "FLOW_SEGMENT_RECTANGULAR"
|
||||
elif self.ifc_element_type == "IfcPipeSegmentType":
|
||||
props.type_predefined_type = "RIGIDSEGMENT"
|
||||
props.type_template = "FLOW_SEGMENT_CIRCULAR"
|
||||
bpy.ops.bim.add_type()
|
||||
props.ifc_predefined_type = "RIGIDSEGMENT"
|
||||
props.representation_template = "FLOW_SEGMENT_CIRCULAR"
|
||||
bpy.ops.bim.add_element()
|
||||
|
||||
|
||||
class AddOccurrence(bpy.types.Operator, PolylineOperator):
|
||||
@@ -111,10 +111,6 @@ class AddOccurrence(bpy.types.Operator, PolylineOperator):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.relating_type = None
|
||||
relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id
|
||||
if relating_type_id:
|
||||
self.relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
|
||||
def create_occurrence(self, context, event):
|
||||
if not self.relating_type:
|
||||
@@ -151,8 +147,20 @@ class AddOccurrence(bpy.types.Operator, PolylineOperator):
|
||||
snap_obj.select_set(False)
|
||||
|
||||
def modal(self, context, event):
|
||||
# Ensure state of BIM tool props is valid
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
relating_type_id = tool.Blender.get_enum_safe(props, "relating_type_id")
|
||||
relating_type_id_data = AuthoringData.data["relating_type_id"]
|
||||
if not relating_type_id and relating_type_id_data:
|
||||
props.relating_type_id = relating_type_id_data[0][0]
|
||||
|
||||
self.relating_type = None
|
||||
relating_type_id = props.relating_type_id
|
||||
if relating_type_id:
|
||||
self.relating_type = tool.Ifc.get().by_id(int(relating_type_id))
|
||||
|
||||
if not self.relating_type:
|
||||
self.report({"WARNING"}, "You need to select a wall type.")
|
||||
self.report({"WARNING"}, "You need to select a type.")
|
||||
PolylineDecorator.uninstall()
|
||||
tool.Blender.update_viewport()
|
||||
return {"FINISHED"}
|
||||
@@ -463,7 +471,7 @@ class ChangeTypePage(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=props.type_class, offset=9 * (self.page - 1), limit=9)
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=props.ifc_class, offset=9 * (self.page - 1), limit=9)
|
||||
props.type_page = self.page
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -32,12 +32,6 @@ def get_ifc_class(self, context):
|
||||
return AuthoringData.data["ifc_classes"]
|
||||
|
||||
|
||||
def get_type_class(self, context):
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
return AuthoringData.data["type_class"]
|
||||
|
||||
|
||||
def get_boundary_class(self, context):
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
@@ -50,18 +44,6 @@ def get_relating_type_id(self, context):
|
||||
return AuthoringData.data["relating_type_id"]
|
||||
|
||||
|
||||
def get_type_predefined_type(self, context):
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
return AuthoringData.data["type_predefined_type"]
|
||||
|
||||
|
||||
def get_type_template(self, context):
|
||||
if not AuthoringData.is_loaded:
|
||||
AuthoringData.load()
|
||||
return AuthoringData.data["type_template"]
|
||||
|
||||
|
||||
def update_ifc_class(self, context):
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=self.ifc_class)
|
||||
AuthoringData.data["relating_type_id"] = AuthoringData.relating_type_id()
|
||||
@@ -69,19 +51,11 @@ def update_ifc_class(self, context):
|
||||
if tool.Blender.get_enum_safe(self, "relating_type_id") is None:
|
||||
self["relating_type_id"] = 0
|
||||
|
||||
|
||||
def update_type_class(self, context):
|
||||
AuthoringData.data["total_types"] = AuthoringData.total_types()
|
||||
AuthoringData.data["total_pages"] = AuthoringData.total_pages()
|
||||
AuthoringData.data["prev_page"] = AuthoringData.prev_page()
|
||||
AuthoringData.data["next_page"] = AuthoringData.next_page()
|
||||
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
|
||||
AuthoringData.data["type_predefined_type"] = AuthoringData.type_predefined_type()
|
||||
AuthoringData.data["type_template"] = AuthoringData.type_template()
|
||||
|
||||
type_class = self.type_class
|
||||
if (type_class, type_class, "") in get_ifc_class(self, context):
|
||||
self.ifc_class = type_class
|
||||
|
||||
|
||||
def update_relating_type_id(self, context):
|
||||
@@ -173,9 +147,6 @@ class BIMModelProperties(PropertyGroup):
|
||||
rl3: bpy.props.FloatProperty(name="RL", default=1, subtype="DISTANCE", description="Z offset for space calculation")
|
||||
x_angle: bpy.props.FloatProperty(name="X Angle", default=0, subtype="ANGLE", min=-pi / 180 * 89, max=pi / 180 * 89)
|
||||
type_page: bpy.props.IntProperty(name="Type Page", default=1, update=update_type_page)
|
||||
type_template: bpy.props.EnumProperty(items=get_type_template, name="Type Template", default=0, options=set())
|
||||
type_class: bpy.props.EnumProperty(items=get_type_class, name="IFC Class", update=update_type_class)
|
||||
type_predefined_type: bpy.props.EnumProperty(items=get_type_predefined_type, name="Predefined Type", default=None)
|
||||
type_name: bpy.props.StringProperty(name="Name", default="TYPEX")
|
||||
boundary_class: bpy.props.EnumProperty(items=get_boundary_class, name="Boundary Class")
|
||||
|
||||
|
||||
@@ -100,7 +100,6 @@ class LaunchTypeManager(bpy.types.Operator):
|
||||
|
||||
# will be None if project has no types
|
||||
if ifc_class is not None:
|
||||
props.type_class = ifc_class
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class, offset=0, limit=9)
|
||||
return context.window_manager.invoke_props_dialog(
|
||||
self, width=550, title="Type Manager", confirm_text="Add Type"
|
||||
@@ -165,7 +164,7 @@ class LaunchTypeManager(bpy.types.Operator):
|
||||
else:
|
||||
row = box.row()
|
||||
op = box.operator("bim.load_type_thumbnails", text="Load Thumbnails", icon="FILE_REFRESH")
|
||||
op.ifc_class = props.type_class
|
||||
op.ifc_class = props.ifc_class
|
||||
|
||||
|
||||
class BIM_PT_authoring(Panel):
|
||||
|
||||
@@ -20,6 +20,7 @@ import os
|
||||
import sys
|
||||
import bpy
|
||||
import bpy.utils.previews
|
||||
import bonsai.bim
|
||||
import bonsai.tool as tool
|
||||
import bonsai.core.model as core
|
||||
from bonsai.bim.module.model.wall import DumbWallJoiner
|
||||
@@ -339,10 +340,12 @@ class CreateObjectUI:
|
||||
)
|
||||
cls.layout.label(text=tool_name, icon="TOOL_SETTINGS")
|
||||
|
||||
cls.draw_type_manager_launcher(context)
|
||||
cls.draw_add_object(context)
|
||||
cls.draw_thumbnail() if context.region.type != "TOOL_HEADER" else cls
|
||||
cls.draw_add_object_parameters(context)
|
||||
if AuthoringData.data["ifc_classes"] and AuthoringData.data["relating_type_id"]:
|
||||
cls.draw_add_object(context)
|
||||
cls.draw_thumbnail() if context.region.type != "TOOL_HEADER" else cls
|
||||
cls.draw_add_object_parameters(context)
|
||||
else:
|
||||
cls.draw_type_manager_launcher(context)
|
||||
|
||||
@classmethod
|
||||
def draw_container_info(cls, context):
|
||||
@@ -355,50 +358,46 @@ class CreateObjectUI:
|
||||
@classmethod
|
||||
def draw_type_manager_launcher(cls, context):
|
||||
row = cls.layout.row(align=True)
|
||||
if not AuthoringData.data["ifc_classes"]:
|
||||
if AuthoringData.data["ifc_element_type"]:
|
||||
row.label(text=f"No {AuthoringData.data['ifc_element_type']} Found", icon="ERROR")
|
||||
row = cls.layout.row(align=True)
|
||||
op = row.operator(
|
||||
"bim.add_default_type",
|
||||
icon_value=custom_icon_previews["ADD_TYPE"].icon_id,
|
||||
text=f"Create default {AuthoringData.data['ifc_element_type']}",
|
||||
)
|
||||
op.ifc_element_type = AuthoringData.data["ifc_element_type"]
|
||||
else:
|
||||
row.label(text="No Element Types Found", icon="ERROR")
|
||||
row.label(text="No Element Types Found", icon="ERROR")
|
||||
row = cls.layout.row(align=True)
|
||||
row.operator("bim.add_element", icon_value=bonsai.bim.icons["IFC"].icon_id, text="Add New Type")
|
||||
if AuthoringData.data["ifc_element_type"]:
|
||||
row = cls.layout.row(align=True)
|
||||
row.operator("bim.launch_type_manager", icon=tool.Blender.TYPE_MANAGER_ICON, text="Launch Type Manager")
|
||||
op = row.operator(
|
||||
"bim.add_default_type",
|
||||
icon_value=custom_icon_previews["ADD_TYPE"].icon_id,
|
||||
text=f"Quick Create Default {AuthoringData.data['ifc_element_type']}",
|
||||
)
|
||||
op.ifc_element_type = AuthoringData.data["ifc_element_type"]
|
||||
|
||||
@classmethod
|
||||
def draw_add_object(cls, context):
|
||||
ui_context = str(context.region.type)
|
||||
if AuthoringData.data["ifc_classes"]:
|
||||
if not AuthoringData.data["ifc_element_type"]:
|
||||
row = cls.layout.row(align=True)
|
||||
prop_with_search(row, cls.props, "ifc_class", text="Type Class" if ui_context != "TOOL_HEADER" else "")
|
||||
if AuthoringData.data["relating_type_id"]:
|
||||
row = cls.layout.row(align=True)
|
||||
box = row.box()
|
||||
# This trick creates a fake dropdown
|
||||
row2 = box.row(align=True)
|
||||
row2.operator(
|
||||
"bim.launch_type_manager",
|
||||
icon="FILE_3D",
|
||||
text=AuthoringData.data["relating_type_name"],
|
||||
emboss=False,
|
||||
)
|
||||
row2.operator(
|
||||
"bim.launch_type_manager",
|
||||
icon="DOWNARROW_HLT",
|
||||
text="",
|
||||
emboss=False,
|
||||
)
|
||||
row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row
|
||||
op = row.operator("bim.hotkey", text="Add", icon_value=custom_icon_previews["ADD"].icon_id)
|
||||
op.hotkey = "S_A"
|
||||
else:
|
||||
row.label(text="No Construction Type", icon="FILE_3D")
|
||||
if not AuthoringData.data["ifc_element_type"]:
|
||||
row = cls.layout.row(align=True)
|
||||
prop_with_search(row, cls.props, "ifc_class", text="Type Class" if ui_context != "TOOL_HEADER" else "")
|
||||
if AuthoringData.data["relating_type_id"]:
|
||||
row = cls.layout.row(align=True)
|
||||
box = row.box()
|
||||
# This trick creates a fake dropdown
|
||||
row2 = box.row(align=True)
|
||||
row2.operator(
|
||||
"bim.launch_type_manager",
|
||||
icon="FILE_3D",
|
||||
text=AuthoringData.data["relating_type_name"],
|
||||
emboss=False,
|
||||
)
|
||||
row2.operator(
|
||||
"bim.launch_type_manager",
|
||||
icon="DOWNARROW_HLT",
|
||||
text="",
|
||||
emboss=False,
|
||||
)
|
||||
row = cls.layout.row(align=True) if ui_context != "TOOL_HEADER" else row
|
||||
op = row.operator("bim.hotkey", text="Add", icon_value=custom_icon_previews["ADD"].icon_id)
|
||||
op.hotkey = "S_A"
|
||||
else:
|
||||
row.label(text="No Construction Type", icon="FILE_3D")
|
||||
|
||||
@classmethod
|
||||
def draw_add_object_parameters(cls, context):
|
||||
@@ -858,13 +857,15 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props = bpy.context.scene.BIMModelProperties
|
||||
if bpy.context.scene.BIMGeometryProperties.mode == "ITEM":
|
||||
bpy.ops.wm.call_menu(name="BIM_MT_add_representation_item")
|
||||
elif (
|
||||
props.relating_type_id
|
||||
and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(props.relating_type_id))) == "LAYER2"
|
||||
):
|
||||
bpy.ops.bim.draw_polyline_wall("INVOKE_DEFAULT")
|
||||
else:
|
||||
bpy.ops.bim.add_occurrence("INVOKE_DEFAULT")
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
obj.select_set(False)
|
||||
if (
|
||||
relating_type_id := tool.Blender.get_enum_safe(props, "relating_type_id")
|
||||
) and tool.Model.get_usage_type(tool.Ifc.get().by_id(int(relating_type_id))) == "LAYER2":
|
||||
bpy.ops.bim.draw_polyline_wall("INVOKE_DEFAULT")
|
||||
else:
|
||||
bpy.ops.bim.add_occurrence("INVOKE_DEFAULT")
|
||||
|
||||
def hotkey_S_Q(self):
|
||||
if not bpy.context.selected_objects:
|
||||
|
||||
@@ -20,7 +20,6 @@ import bpy
|
||||
from . import ui, prop, operator
|
||||
|
||||
classes = (
|
||||
operator.AddType,
|
||||
operator.AssignType,
|
||||
operator.AutoRenameOccurrences,
|
||||
operator.DisableEditingType,
|
||||
|
||||
@@ -229,257 +229,6 @@ class SelectTypeObjects(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AddType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_type"
|
||||
bl_label = "Add Type"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMModelProperties
|
||||
ifc_class = props.type_class
|
||||
predefined_type = props.type_predefined_type
|
||||
name = props.type_name
|
||||
template = props.type_template
|
||||
ifc_file = tool.Ifc.get()
|
||||
body = ifcopenshell.util.representation.get_context(ifc_file, "Model", "Body", "MODEL_VIEW")
|
||||
if not body:
|
||||
props.type_class = props.type_class
|
||||
self.report({"ERROR"}, "No Model/Body/MODEL_VIEW context found.")
|
||||
return {"FINISHED"}
|
||||
|
||||
if template == "MESH":
|
||||
location = context.scene.cursor.location
|
||||
if context.active_object and context.selected_objects and context.active_object.data:
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
mesh = obj.data.copy()
|
||||
mesh.BIMMeshProperties.ifc_definition_id = 0
|
||||
obj = bpy.data.objects.new(element.Name or name, mesh)
|
||||
else:
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
bm = bmesh.new()
|
||||
bmesh.ops.create_cube(bm, size=1)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
obj.matrix_world.translation = location
|
||||
bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
should_add_representation=True,
|
||||
context=body,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
|
||||
elif template in ("LAYERSET_AXIS2", "LAYERSET_AXIS3"):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
obj = bpy.data.objects.new(name, None)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
should_add_representation=True,
|
||||
context=body,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
materials = ifc_file.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", ifc_file, products=[element], type="IfcMaterialLayerSet"
|
||||
)
|
||||
layer_set = rel.RelatingMaterial
|
||||
layer = ifcopenshell.api.run("material.add_layer", ifc_file, 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", ifc_file, product=element, name="EPset_Parametric")
|
||||
if template == "LAYERSET_AXIS2":
|
||||
axis = "AXIS2"
|
||||
elif template == "LAYERSET_AXIS3":
|
||||
axis = "AXIS3"
|
||||
ifcopenshell.api.run("pset.edit_pset", ifc_file, pset=pset, properties={"LayerSetDirection": axis})
|
||||
|
||||
elif template == "PROFILESET" or template.startswith("FLOW_SEGMENT_"):
|
||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(ifc_file)
|
||||
obj = bpy.data.objects.new(name, None)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
should_add_representation=True,
|
||||
context=body,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
materials = ifc_file.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 template == "PROFILESET":
|
||||
named_profiles = [p for p in ifc_file.by_type("IfcProfileDef") if p.ProfileName]
|
||||
if named_profiles:
|
||||
profile = named_profiles[0]
|
||||
else:
|
||||
size = 0.5 / unit_scale
|
||||
profile = ifc_file.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 template == "FLOW_SEGMENT_RECTANGULAR":
|
||||
default_x_dim = 0.4
|
||||
default_y_dim = 0.2
|
||||
profile_name = f"{ifc_class}-{default_x_dim*1000}x{default_y_dim*1000}"
|
||||
profile = ifc_file.create_entity(
|
||||
"IfcRectangleProfileDef",
|
||||
ProfileName=profile_name,
|
||||
ProfileType="AREA",
|
||||
XDim=default_x_dim / unit_scale,
|
||||
YDim=default_y_dim / unit_scale,
|
||||
)
|
||||
elif template == "FLOW_SEGMENT_CIRCULAR":
|
||||
default_diameter = 0.1
|
||||
profile_name = f"{ifc_class}-{default_diameter*1000}"
|
||||
profile = ifc_file.create_entity(
|
||||
"IfcCircleProfileDef",
|
||||
ProfileName=profile_name,
|
||||
ProfileType="AREA",
|
||||
Radius=(default_diameter / 2) / unit_scale,
|
||||
)
|
||||
elif template == "FLOW_SEGMENT_CIRCULAR_HOLLOW":
|
||||
default_diameter = 0.15
|
||||
default_thickness = 0.005
|
||||
profile_name = f"{ifc_class}-{default_diameter*1000}x{default_thickness*1000}"
|
||||
profile = ifc_file.create_entity(
|
||||
"IfcCircleHollowProfileDef",
|
||||
ProfileName=profile_name,
|
||||
ProfileType="AREA",
|
||||
Radius=(default_diameter / 2) / unit_scale,
|
||||
WallThickness=default_thickness,
|
||||
)
|
||||
|
||||
rel = ifcopenshell.api.run(
|
||||
"material.assign_material", ifc_file, products=[element], type="IfcMaterialProfileSet"
|
||||
)
|
||||
profile_set = rel.RelatingMaterial
|
||||
material_profile = ifcopenshell.api.run(
|
||||
"material.add_profile", ifc_file, profile_set=profile_set, material=material
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"material.assign_profile", ifc_file, material_profile=material_profile, profile=profile
|
||||
)
|
||||
|
||||
elif template == "EMPTY":
|
||||
obj = bpy.data.objects.new(name, None)
|
||||
bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
should_add_representation=True,
|
||||
context=body,
|
||||
ifc_representation_class=None,
|
||||
)
|
||||
|
||||
elif template == "WINDOW":
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
predefined_type=predefined_type if tool.Ifc.get_schema() != "IFC2X3" else None,
|
||||
ifc_class="IfcWindowType" if tool.Ifc.get_schema() != "IFC2X3" else "IfcWindowStyle",
|
||||
should_add_representation=False,
|
||||
)
|
||||
with context.temp_override(active_object=obj):
|
||||
bpy.ops.bim.add_window()
|
||||
|
||||
elif template == "DOOR":
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
predefined_type=predefined_type if tool.Ifc.get_schema() != "IFC2X3" else None,
|
||||
ifc_class="IfcDoorType" if tool.Ifc.get_schema() != "IFC2X3" else "IfcDoorStyle",
|
||||
should_add_representation=False,
|
||||
)
|
||||
with context.temp_override(active_object=obj):
|
||||
bpy.ops.bim.add_door()
|
||||
|
||||
elif template == "STAIR":
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
predefined_type=predefined_type,
|
||||
ifc_class=ifc_class,
|
||||
should_add_representation=False,
|
||||
)
|
||||
with context.temp_override(active_object=obj):
|
||||
bpy.ops.bim.add_stair()
|
||||
|
||||
elif template == "RAILING":
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
predefined_type=predefined_type,
|
||||
ifc_class="IfcRailingType",
|
||||
should_add_representation=True,
|
||||
context=body,
|
||||
)
|
||||
with context.temp_override(active_object=obj):
|
||||
bpy.ops.bim.add_railing()
|
||||
|
||||
elif template == "ROOF":
|
||||
mesh = bpy.data.meshes.new(name)
|
||||
obj = bpy.data.objects.new(name, mesh)
|
||||
element = bonsai.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
predefined_type=predefined_type,
|
||||
ifc_class="IfcRoofType",
|
||||
should_add_representation=True,
|
||||
context=body,
|
||||
)
|
||||
with context.temp_override(active_object=obj):
|
||||
bpy.ops.bim.add_roof()
|
||||
|
||||
bpy.ops.bim.load_type_thumbnails(ifc_class=ifc_class)
|
||||
props.type_class = props.type_class
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveType(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_type"
|
||||
bl_label = "Remove Type"
|
||||
|
||||
@@ -144,11 +144,11 @@ class Blender(bonsai.core.tool.Blender):
|
||||
|
||||
@classmethod
|
||||
def get_selected_objects(cls) -> set[bpy.types.Object]:
|
||||
if bpy.context.selected_objects:
|
||||
if active_obj := bpy.context.active_object:
|
||||
return set(bpy.context.selected_objects + [active_obj])
|
||||
return set(bpy.context.selected_objects)
|
||||
if active_obj := bpy.context.active_object:
|
||||
if selected_objects := getattr(bpy.context, "selected_objects", None):
|
||||
if active_obj := cls.get_active_object():
|
||||
return set(selected_objects + [active_obj])
|
||||
return set(selected_objects)
|
||||
if active_obj := cls.get_active_object():
|
||||
return {active_obj}
|
||||
return set()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user