This commit is contained in:
Andrej
2025-06-04 19:49:35 +05:00
parent 5af7c9a32d
commit 87265d052b
14 changed files with 212 additions and 90 deletions
+2 -2
View File
@@ -1033,7 +1033,7 @@ class IfcImporter:
element: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance,
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType], shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
) -> bpy.types.Curve: ) -> bpy.types.Curve:
if hasattr(shape, "geometry"): if isinstance(shape, ifcopenshell.geom.ShapeElementType):
geometry = shape.geometry geometry = shape.geometry
else: else:
geometry = shape geometry = shape
@@ -1066,7 +1066,7 @@ class IfcImporter:
cartesian_point_offset: Union[npt.NDArray[np.float64], Literal[False]] = None, cartesian_point_offset: Union[npt.NDArray[np.float64], Literal[False]] = None,
) -> Union[bpy.types.Mesh, None]: ) -> Union[bpy.types.Mesh, None]:
try: try:
if hasattr(shape, "geometry"): if isinstance(shape, ifcopenshell.geom.ShapeElementType):
# shape is ShapeElementType # shape is ShapeElementType
geometry = shape.geometry geometry = shape.geometry
else: else:
@@ -17,12 +17,27 @@
# 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 json
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.constraint import ifcopenshell.api.constraint
import ifcopenshell.util.attribute
import bonsai.bim.helper import bonsai.bim.helper
import bonsai.tool as tool import bonsai.tool as tool
from typing import TYPE_CHECKING
def get_active_object(context: bpy.types.Context, obj_name: str) -> bpy.types.Object:
if obj_name:
obj = bpy.data.objects[obj_name]
else:
assert (obj := context.active_object)
return obj
def get_selected_objects(context: bpy.types.Context, obj_name: str) -> list[bpy.types.Object]:
if obj_name:
objs = [bpy.data.objects[obj_name]]
else:
objs = context.selected_objects
return objs
class LoadObjectives(bpy.types.Operator): class LoadObjectives(bpy.types.Operator):
@@ -31,7 +46,7 @@ class LoadObjectives(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
props = context.scene.BIMConstraintProperties props = tool.Blender.get_constraint_props()
props.constraints.clear() props.constraints.clear()
for constraint in tool.Ifc.get().by_type("IfcObjective"): for constraint in tool.Ifc.get().by_type("IfcObjective"):
new = props.constraints.add() new = props.constraints.add()
@@ -48,7 +63,8 @@ class DisableConstraintEditingUI(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
context.scene.BIMConstraintProperties.is_editing = "" props = tool.Blender.get_constraint_props()
props.is_editing = ""
bpy.ops.bim.disable_editing_constraint() bpy.ops.bim.disable_editing_constraint()
return {"FINISHED"} return {"FINISHED"}
@@ -59,8 +75,11 @@ class EnableEditingConstraint(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
constraint: bpy.props.IntProperty() constraint: bpy.props.IntProperty()
if TYPE_CHECKING:
constraint: int
def execute(self, context): def execute(self, context):
props = context.scene.BIMConstraintProperties props = tool.Blender.get_constraint_props()
props.constraint_attributes.clear() props.constraint_attributes.clear()
bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.constraint), props.constraint_attributes) bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.constraint), props.constraint_attributes)
props.active_constraint_id = self.constraint props.active_constraint_id = self.constraint
@@ -73,7 +92,8 @@ class DisableEditingConstraint(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
context.scene.BIMConstraintProperties.active_constraint_id = 0 props = tool.Blender.get_constraint_props()
props.active_constraint_id = 0
return {"FINISHED"} return {"FINISHED"}
@@ -83,7 +103,7 @@ class AddObjective(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
result = ifcopenshell.api.run("constraint.add_objective", tool.Ifc.get()) result = ifcopenshell.api.constraint.add_objective(tool.Ifc.get())
bpy.ops.bim.load_objectives() bpy.ops.bim.load_objectives()
bpy.ops.bim.enable_editing_constraint(constraint=result.id()) bpy.ops.bim.enable_editing_constraint(constraint=result.id())
return {"FINISHED"} return {"FINISHED"}
@@ -95,7 +115,7 @@ class EditObjective(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMConstraintProperties props = tool.Blender.get_constraint_props()
attributes = bonsai.bim.helper.export_attributes(props.constraint_attributes) attributes = bonsai.bim.helper.export_attributes(props.constraint_attributes)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
ifcopenshell.api.constraint.edit_objective( ifcopenshell.api.constraint.edit_objective(
@@ -113,12 +133,13 @@ class RemoveConstraint(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
constraint: bpy.props.IntProperty() constraint: bpy.props.IntProperty()
if TYPE_CHECKING:
constraint: int
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMConstraintProperties props = tool.Blender.get_constraint_props()
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
ifcopenshell.api.run( ifcopenshell.api.constraint.remove_constraint(self.file, constraint=self.file.by_id(self.constraint))
"constraint.remove_constraint", self.file, **{"constraint": self.file.by_id(self.constraint)}
)
if props.is_editing == "IfcObjective": if props.is_editing == "IfcObjective":
bpy.ops.bim.load_objectives() bpy.ops.bim.load_objectives()
return {"FINISHED"} return {"FINISHED"}
@@ -130,9 +151,12 @@ class EnableAssigningConstraint(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
if TYPE_CHECKING:
obj: str
def execute(self, context): def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object obj = get_active_object(context, self.obj)
props = obj.BIMObjectConstraintProperties props = tool.Blender.get_object_constraint_props(obj)
if props.available_constraint_types == "IfcObjective": if props.available_constraint_types == "IfcObjective":
bpy.ops.bim.load_objectives() bpy.ops.bim.load_objectives()
props.is_adding = props.available_constraint_types props.is_adding = props.available_constraint_types
@@ -145,9 +169,12 @@ class DisableAssigningConstraint(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
if TYPE_CHECKING:
obj: str
def execute(self, context): def execute(self, context):
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object obj = get_active_object(context, self.obj)
props = obj.BIMObjectConstraintProperties props = tool.Blender.get_object_constraint_props(obj)
props.is_adding = "" props.is_adding = ""
return {"FINISHED"} return {"FINISHED"}
@@ -159,8 +186,13 @@ class AssignConstraint(bpy.types.Operator, tool.Ifc.Operator):
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
constraint: bpy.props.IntProperty() constraint: bpy.props.IntProperty()
if TYPE_CHECKING:
obj: str
constraint: int
def _execute(self, context): def _execute(self, context):
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
objs = get_selected_objects(context, self.obj)
objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects
products = [ products = [
self.file.by_id(obj_id) self.file.by_id(obj_id)
@@ -168,13 +200,10 @@ class AssignConstraint(bpy.types.Operator, tool.Ifc.Operator):
if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id) if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id)
] ]
if products: if products:
ifcopenshell.api.run( ifcopenshell.api.constraint.assign_constraint(
"constraint.assign_constraint",
self.file, self.file,
**{ products=products,
"products": products, constraint=self.file.by_id(self.constraint),
"constraint": self.file.by_id(self.constraint),
},
) )
return {"FINISHED"} return {"FINISHED"}
@@ -186,21 +215,22 @@ class UnassignConstraint(bpy.types.Operator, tool.Ifc.Operator):
obj: bpy.props.StringProperty() obj: bpy.props.StringProperty()
constraint: bpy.props.IntProperty() constraint: bpy.props.IntProperty()
if TYPE_CHECKING:
obj: str
constraint: int
def _execute(self, context): def _execute(self, context):
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
objs = [bpy.data.objects[self.obj]] if self.obj else context.selected_objects objs = get_selected_objects(context, self.obj)
products = [ products = [
self.file.by_id(obj_id) self.file.by_id(obj_id)
for obj in objs for obj in objs
if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id) if (obj_id := tool.Blender.get_object_bim_props(obj).ifc_definition_id)
] ]
if products: if products:
ifcopenshell.api.run( ifcopenshell.api.constraint.unassign_constraint(
"constraint.unassign_constraint",
self.file, self.file,
**{ products=products,
"products": products, constraint=self.file.by_id(self.constraint),
"constraint": self.file.by_id(self.constraint),
},
) )
return {"FINISHED"} return {"FINISHED"}
@@ -32,6 +32,7 @@ from bpy.props import (
FloatVectorProperty, FloatVectorProperty,
CollectionProperty, CollectionProperty,
) )
from typing import TYPE_CHECKING, Literal
def get_available_constraint_types(self, context): def get_available_constraint_types(self, context):
@@ -44,6 +45,10 @@ class Constraint(PropertyGroup):
name: StringProperty(name="Name") name: StringProperty(name="Name")
ifc_definition_id: IntProperty(name="IFC Definition ID") ifc_definition_id: IntProperty(name="IFC Definition ID")
if TYPE_CHECKING:
name: str
ifc_definition_id: int
class BIMConstraintProperties(PropertyGroup): class BIMConstraintProperties(PropertyGroup):
constraint_attributes: CollectionProperty(name="Constraint Attributes", type=Attribute) constraint_attributes: CollectionProperty(name="Constraint Attributes", type=Attribute)
@@ -52,7 +57,18 @@ class BIMConstraintProperties(PropertyGroup):
active_constraint_index: IntProperty(name="Active Constraint Index") active_constraint_index: IntProperty(name="Active Constraint Index")
is_editing: StringProperty(name="Is Editing") is_editing: StringProperty(name="Is Editing")
if TYPE_CHECKING:
constraint_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
active_constraint_id: int
constraints: bpy.types.bpy_prop_collection_idprop[Constraint]
active_constraint_index: int
is_editing: str
class BIMObjectConstraintProperties(PropertyGroup): class BIMObjectConstraintProperties(PropertyGroup):
is_adding: StringProperty(name="Is Adding") is_adding: StringProperty(name="Is Adding")
available_constraint_types: EnumProperty(items=get_available_constraint_types, name="Available Constraint Types") available_constraint_types: EnumProperty(items=get_available_constraint_types, name="Available Constraint Types")
if TYPE_CHECKING:
is_adding: str
available_constraint_types: Literal["IfcObjective"]
+31 -8
View File
@@ -16,10 +16,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bonsai.tool as tool import bonsai.tool as tool
from bpy.types import Panel, UIList from bpy.types import Panel, UIList
from bonsai.bim.helper import draw_attributes from bonsai.bim.helper import draw_attributes
from bonsai.bim.module.constraint.data import ConstraintsData, ObjectConstraintsData from bonsai.bim.module.constraint.data import ConstraintsData, ObjectConstraintsData
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.constraint.prop import BIMConstraintProperties, BIMObjectConstraintProperties, Constraint
class BIM_PT_constraints(Panel): class BIM_PT_constraints(Panel):
@@ -39,7 +44,7 @@ class BIM_PT_constraints(Panel):
if not ConstraintsData.is_loaded: if not ConstraintsData.is_loaded:
ConstraintsData.load() ConstraintsData.load()
self.props = context.scene.BIMConstraintProperties self.props = tool.Blender.get_constraint_props()
if not self.props.is_editing or self.props.is_editing == "IfcObjective": if not self.props.is_editing or self.props.is_editing == "IfcObjective":
row = self.layout.row(align=True) row = self.layout.row(align=True)
@@ -91,8 +96,8 @@ class BIM_PT_object_constraints(Panel):
obj = context.active_object obj = context.active_object
assert obj assert obj
self.oprops = tool.Blender.get_object_bim_props(obj) self.oprops = tool.Blender.get_object_bim_props(obj)
self.sprops = context.scene.BIMConstraintProperties self.sprops = tool.Blender.get_constraint_props()
self.props = obj.BIMObjectConstraintProperties self.props = tool.Blender.get_object_constraint_props(obj)
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
self.draw_add_ui() self.draw_add_ui()
@@ -131,15 +136,24 @@ class BIM_PT_object_constraints(Panel):
class BIM_UL_constraints(UIList): class BIM_UL_constraints(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(
self,
context,
layout,
data: BIMConstraintProperties,
item: Constraint,
icon,
active_data,
active_propname,
):
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.label(text=item.name) row.label(text=item.name)
if context.scene.BIMConstraintProperties.active_constraint_id == item.ifc_definition_id: if data.active_constraint_id == item.ifc_definition_id:
if context.scene.BIMConstraintProperties.is_editing == "IfcObjective": if data.is_editing == "IfcObjective":
row.operator("bim.edit_objective", text="", icon="CHECKMARK") row.operator("bim.edit_objective", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_constraint", text="", icon="CANCEL") row.operator("bim.disable_editing_constraint", text="", icon="CANCEL")
elif context.scene.BIMConstraintProperties.active_constraint_id: elif data.active_constraint_id:
row.operator("bim.remove_constraint", text="", icon="X").constraint = item.ifc_definition_id row.operator("bim.remove_constraint", text="", icon="X").constraint = item.ifc_definition_id
else: else:
op = row.operator("bim.enable_editing_constraint", text="", icon="GREASEPENCIL") op = row.operator("bim.enable_editing_constraint", text="", icon="GREASEPENCIL")
@@ -148,7 +162,16 @@ class BIM_UL_constraints(UIList):
class BIM_UL_object_constraints(UIList): class BIM_UL_object_constraints(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(
self,
context,
layout,
data: BIMObjectConstraintProperties,
item: Constraint,
icon,
active_data,
active_propname,
):
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.label(text=item.name) row.label(text=item.name)
+49 -23
View File
@@ -17,13 +17,21 @@
# 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 json
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.api.layer import ifcopenshell.api.layer
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.attribute
import bonsai.bim.helper import bonsai.bim.helper
import bonsai.tool as tool import bonsai.tool as tool
from typing import TYPE_CHECKING
def get_active_mesh(context: bpy.types.Context, mesh_name: str) -> bpy.types.Mesh:
if mesh_name:
item_mesh = bpy.data.meshes[mesh_name]
else:
assert (obj := context.active_object)
assert isinstance(item_mesh := obj.data, bpy.types.Mesh)
return item_mesh
class LoadLayers(bpy.types.Operator): class LoadLayers(bpy.types.Operator):
@@ -32,10 +40,10 @@ class LoadLayers(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
self.file = tool.Ifc.get() ifc_file = tool.Ifc.get()
props = context.scene.BIMLayerProperties props = tool.Layer.get_layer_props()
props.layers.clear() props.layers.clear()
for layer in tool.Ifc.get().by_type("IfcPresentationLayerAssignment"): for layer in ifc_file.by_type("IfcPresentationLayerAssignment"):
new = props.layers.add() new = props.layers.add()
new.name = layer.Name or "Unnamed" new.name = layer.Name or "Unnamed"
new.ifc_definition_id = layer.id() new.ifc_definition_id = layer.id()
@@ -57,7 +65,8 @@ class DisableLayerEditingUI(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
context.scene.BIMLayerProperties.is_editing = False props = tool.Layer.get_layer_props()
props.is_editing = False
return {"FINISHED"} return {"FINISHED"}
@@ -67,8 +76,11 @@ class EnableEditingLayer(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
layer: bpy.props.IntProperty() layer: bpy.props.IntProperty()
if TYPE_CHECKING:
layer: int
def execute(self, context): def execute(self, context):
props = context.scene.BIMLayerProperties props = tool.Layer.get_layer_props()
props.layer_attributes.clear() props.layer_attributes.clear()
bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.layer), props.layer_attributes) bonsai.bim.helper.import_attributes2(tool.Ifc.get().by_id(self.layer), props.layer_attributes)
props.active_layer_id = self.layer props.active_layer_id = self.layer
@@ -81,7 +93,8 @@ class DisableEditingLayer(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
context.scene.BIMLayerProperties.active_layer_id = 0 props = tool.Layer.get_layer_props()
props.active_layer_id = 0
return {"FINISHED"} return {"FINISHED"}
@@ -92,7 +105,7 @@ class AddPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMLayerProperties props = tool.Layer.get_layer_props()
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
if props.layer_type == "IfcPresentationLayerWithStyle": if props.layer_type == "IfcPresentationLayerWithStyle":
layer = ifcopenshell.api.layer.add_layer_with_style(ifc_file) layer = ifcopenshell.api.layer.add_layer_with_style(ifc_file)
@@ -109,7 +122,7 @@ class EditPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMLayerProperties props = tool.Layer.get_layer_props()
attributes = bonsai.bim.helper.export_attributes(props.layer_attributes) attributes = bonsai.bim.helper.export_attributes(props.layer_attributes)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
ifcopenshell.api.layer.edit_layer(ifc_file, layer=ifc_file.by_id(props.active_layer_id), attributes=attributes) ifcopenshell.api.layer.edit_layer(ifc_file, layer=ifc_file.by_id(props.active_layer_id), attributes=attributes)
@@ -123,10 +136,12 @@ class RemovePresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
layer: bpy.props.IntProperty() layer: bpy.props.IntProperty()
if TYPE_CHECKING:
layer: int
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMLayerProperties ifc_file = tool.Ifc.get()
self.file = tool.Ifc.get() ifcopenshell.api.layer.remove_layer(ifc_file, layer=ifc_file.by_id(self.layer))
ifcopenshell.api.run("layer.remove_layer", self.file, **{"layer": self.file.by_id(self.layer)})
bpy.ops.bim.load_layers() bpy.ops.bim.load_layers()
return {"FINISHED"} return {"FINISHED"}
@@ -139,16 +154,17 @@ class AssignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
item: bpy.props.StringProperty() item: bpy.props.StringProperty()
layer: bpy.props.IntProperty() layer: bpy.props.IntProperty()
if TYPE_CHECKING:
item: str
layer: int
def _execute(self, context): def _execute(self, context):
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data item = get_active_mesh(context, self.item)
self.file = tool.Ifc.get() ifc_file = tool.Ifc.get()
ifcopenshell.api.run( ifcopenshell.api.layer.assign_layer(
"layer.assign_layer", ifc_file,
self.file, items=[ifc_file.by_id(tool.Geometry.get_mesh_props(item).ifc_definition_id)],
**{ layer=ifc_file.by_id(self.layer),
"items": [self.file.by_id(tool.Geometry.get_mesh_props(item).ifc_definition_id)],
"layer": self.file.by_id(self.layer),
},
) )
return {"FINISHED"} return {"FINISHED"}
@@ -161,8 +177,12 @@ class UnassignPresentationLayer(bpy.types.Operator, tool.Ifc.Operator):
item: bpy.props.StringProperty() item: bpy.props.StringProperty()
layer: bpy.props.IntProperty() layer: bpy.props.IntProperty()
if TYPE_CHECKING:
item: str
layer: int
def _execute(self, context): def _execute(self, context):
item = bpy.data.meshes.get(self.item) if self.item else context.active_object.data item = get_active_mesh(context, self.item)
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
representation = tool.Geometry.get_data_representation(item) representation = tool.Geometry.get_data_representation(item)
assert representation assert representation
@@ -176,6 +196,9 @@ class SelectLayerProducts(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
layer: bpy.props.IntProperty() layer: bpy.props.IntProperty()
if TYPE_CHECKING:
layer: int
def execute(self, context): def execute(self, context):
elements = ifcopenshell.util.element.get_elements_by_layer(tool.Ifc.get(), tool.Ifc.get().by_id(self.layer)) elements = ifcopenshell.util.element.get_elements_by_layer(tool.Ifc.get(), tool.Ifc.get().by_id(self.layer))
for obj in context.visible_objects: for obj in context.visible_objects:
@@ -192,6 +215,9 @@ class SelectLayerInLayerUI(bpy.types.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
layer_id: bpy.props.IntProperty() layer_id: bpy.props.IntProperty()
if TYPE_CHECKING:
layer_id: int
def execute(self, context): def execute(self, context):
props = tool.Layer.get_layer_props() props = tool.Layer.get_layer_props()
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
@@ -59,6 +59,14 @@ class Layer(PropertyGroup):
update=lambda self, context: update_layer_property(self, context, property="blocked"), update=lambda self, context: update_layer_property(self, context, property="blocked"),
) )
if TYPE_CHECKING:
name: str
ifc_definition_id: int
with_style: bool
on: bool
frozen: bool
blocked: bool
class BIMLayerProperties(PropertyGroup): class BIMLayerProperties(PropertyGroup):
layer_attributes: CollectionProperty(name="Layer Attributes", type=Attribute) layer_attributes: CollectionProperty(name="Layer Attributes", type=Attribute)
+18 -4
View File
@@ -16,11 +16,16 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
import bonsai.tool as tool import bonsai.tool as tool
from bpy.types import Panel, UIList, Mesh from bpy.types import Panel, UIList, Mesh
from bonsai.bim.helper import draw_attributes from bonsai.bim.helper import draw_attributes
from bonsai.bim.module.layer.data import LayersData from bonsai.bim.module.layer.data import LayersData
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from bonsai.bim.module.layer.prop import BIMLayerProperties, Layer
class BIM_PT_layers(Panel): class BIM_PT_layers(Panel):
@@ -40,7 +45,7 @@ class BIM_PT_layers(Panel):
if not LayersData.is_loaded: if not LayersData.is_loaded:
LayersData.load() LayersData.load()
self.props = context.scene.BIMLayerProperties self.props = tool.Layer.get_layer_props()
row = self.layout.row(align=True) row = self.layout.row(align=True)
row.label(text=f"{LayersData.data['total_layers']} Layers Found", icon="STICKY_UVS_LOC") row.label(text=f"{LayersData.data['total_layers']} Layers Found", icon="STICKY_UVS_LOC")
@@ -71,7 +76,16 @@ class BIM_PT_layers(Panel):
class BIM_UL_layers(UIList): class BIM_UL_layers(UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname): def draw_item(
self,
context: bpy.types.Context,
layout: bpy.types.UILayout,
data: BIMLayerProperties,
item: Layer,
icon,
active_data,
active_propname,
):
if item: if item:
row = layout.row(align=True) row = layout.row(align=True)
row.label(text=item.name) row.label(text=item.name)
@@ -89,12 +103,12 @@ class BIM_UL_layers(UIList):
row.prop(item, "frozen", text="", icon="FREEZE" if item.frozen else "MESH_PLANE", emboss=False) row.prop(item, "frozen", text="", icon="FREEZE" if item.frozen else "MESH_PLANE", emboss=False)
row.prop(item, "blocked", text="", icon="LOCKED" if item.blocked else "UNLOCKED", emboss=False) row.prop(item, "blocked", text="", icon="LOCKED" if item.blocked else "UNLOCKED", emboss=False)
if context.scene.BIMLayerProperties.active_layer_id == item.ifc_definition_id: if data.active_layer_id == item.ifc_definition_id:
op = row.operator("bim.select_layer_products", text="", icon="RESTRICT_SELECT_OFF") op = row.operator("bim.select_layer_products", text="", icon="RESTRICT_SELECT_OFF")
op.layer = item.ifc_definition_id op.layer = item.ifc_definition_id
row.operator("bim.edit_presentation_layer", text="", icon="CHECKMARK") row.operator("bim.edit_presentation_layer", text="", icon="CHECKMARK")
row.operator("bim.disable_editing_layer", text="", icon="CANCEL") row.operator("bim.disable_editing_layer", text="", icon="CANCEL")
elif context.scene.BIMLayerProperties.active_layer_id: elif data.active_layer_id:
op = row.operator("bim.select_layer_products", text="", icon="RESTRICT_SELECT_OFF") op = row.operator("bim.select_layer_products", text="", icon="RESTRICT_SELECT_OFF")
op.layer = item.ifc_definition_id op.layer = item.ifc_definition_id
row.operator("bim.remove_presentation_layer", text="", icon="X").layer = item.ifc_definition_id row.operator("bim.remove_presentation_layer", text="", icon="X").layer = item.ifc_definition_id
+14 -2
View File
@@ -50,6 +50,7 @@ if TYPE_CHECKING:
from bonsai.bim.prop import BIMProperties, BIMObjectProperties from bonsai.bim.prop import BIMProperties, BIMObjectProperties
from bonsai.bim.module.attribute.prop import BIMAttributeProperties from bonsai.bim.module.attribute.prop import BIMAttributeProperties
from bonsai.bim.module.csv.prop import CsvProperties from bonsai.bim.module.csv.prop import CsvProperties
from bonsai.bim.module.constraint.prop import BIMConstraintProperties, BIMObjectConstraintProperties
from bonsai.bim.module.diff.prop import DiffProperties from bonsai.bim.module.diff.prop import DiffProperties
T = TypeVar("T") T = TypeVar("T")
@@ -1644,13 +1645,24 @@ class Blender(bonsai.core.tool.Blender):
dct = {cls.bl_idname: cls.ifc_element_type for cls in (BimTool.__subclasses__())} dct = {cls.bl_idname: cls.ifc_element_type for cls in (BimTool.__subclasses__())}
return types.MappingProxyType(dct) return types.MappingProxyType(dct)
@classmethod
def get_object_constraint_props(cls, obj: bpy.types.Object) -> BIMObjectConstraintProperties:
return obj.BIMObjectConstraintProperties
@classmethod
def get_constraint_props(cls) -> BIMConstraintProperties:
assert (scene := bpy.context.scene)
return scene.BIMConstraintProperties
@classmethod @classmethod
def get_csv_props(cls) -> CsvProperties: def get_csv_props(cls) -> CsvProperties:
return bpy.context.scene.CsvProperties assert (scene := bpy.context.scene)
return scene.CsvProperties
@classmethod @classmethod
def get_diff_props(cls) -> DiffProperties: def get_diff_props(cls) -> DiffProperties:
return bpy.context.scene.DiffProperties assert (scene := bpy.context.scene)
return scene.DiffProperties
@classmethod @classmethod
def get_bim_props(cls, scene: Optional[bpy.types.Scene] = None) -> BIMProperties: def get_bim_props(cls, scene: Optional[bpy.types.Scene] = None) -> BIMProperties:
+1 -1
View File
@@ -101,7 +101,7 @@ class Loader(bonsai.core.tool.Loader):
shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType], shape: Union[ifcopenshell.geom.ShapeElementType, ifcopenshell.geom.ShapeType],
mesh: tool.Geometry.TYPES_WITH_MESH_PROPERTIES, mesh: tool.Geometry.TYPES_WITH_MESH_PROPERTIES,
) -> None: ) -> None:
geometry = shape.geometry if hasattr(shape, "geometry") else shape geometry = shape.geometry if isinstance(shape, ifcopenshell.geom.ShapeElementType) else shape
tool.Geometry.get_mesh_props(mesh).ifc_definition_id = int(geometry.id.split("-")[0]) tool.Geometry.get_mesh_props(mesh).ifc_definition_id = int(geometry.id.split("-")[0])
@classmethod @classmethod
+2 -1
View File
@@ -39,6 +39,7 @@ from dateutil import parser
from datetime import datetime from datetime import datetime
from datetime import time as datetime_time from datetime import time as datetime_time
from typing import Optional, Any, Union, Literal, TYPE_CHECKING, Iterable from typing import Optional, Any, Union, Literal, TYPE_CHECKING, Iterable
from mathutils import Color
if TYPE_CHECKING: if TYPE_CHECKING:
import bonsai.bim.prop import bonsai.bim.prop
@@ -1449,7 +1450,7 @@ class Sequence(bonsai.core.tool.Sequence):
obj.keyframe_insert(data_path="color", frame=product_frame["COMPLETED"]) obj.keyframe_insert(data_path="color", frame=product_frame["COMPLETED"])
@classmethod @classmethod
def animate_operation(cls, obj, start_frame, product_frame, color): def animate_operation(cls, obj: bpy.types.Object, start_frame: int, product_frame, color: Color) -> None:
if cls.earliest_frame is None or product_frame["STARTED"] < cls.earliest_frame: if cls.earliest_frame is None or product_frame["STARTED"] < cls.earliest_frame:
obj.color = (1.0, 1.0, 1.0, 1) obj.color = (1.0, 1.0, 1.0, 1)
obj.keyframe_insert(data_path="color", frame=start_frame) obj.keyframe_insert(data_path="color", frame=start_frame)
@@ -260,7 +260,7 @@ def schema_by_name(
return ifcopenshell_wrapper.schema_by_name(schema) return ifcopenshell_wrapper.schema_by_name(schema)
def guess_format(path: Path) -> Union[str, None]: def guess_format(path: Path) -> Literal[".ifc", ".ifcZIP", ".ifcXML", ".ifcJSON", ".ifcSQLite", None]:
"""Guesses the IFC format using file extension """Guesses the IFC format using file extension
IFCs may be serialised as different formats. The most common is a ``.ifc`` IFCs may be serialised as different formats. The most common is a ``.ifc``
@@ -273,8 +273,6 @@ def guess_format(path: Path) -> Union[str, None]:
Users generally won't call this function. The :func:`open` function uses Users generally won't call this function. The :func:`open` function uses
this internally to guess the file format. this internally to guess the file format.
:return: Either .ifc, .ifcZIP, .ifcXML, .ifcJSON, .ifcSQLite, or None.
""" """
suffix = path.suffix.lower() suffix = path.suffix.lower()
if suffix == ".ifc": if suffix == ".ifc":
@@ -127,7 +127,7 @@ class BRep(Representation):
class BRepElement(Element): class BRepElement(Element):
def calculate_projected_surface_area(self, along_x, along_y, along_z): ... def calculate_projected_surface_area(self, along_x, along_y, along_z): ...
@property @property
def geometry(self): ... def geometry(self) -> BRep: ...
def geometry_pointer(self): ... def geometry_pointer(self): ...
@property @property
def surface_area(self): ... def surface_area(self): ...
@@ -361,7 +361,7 @@ class Serialization(Representation):
class SerializedElement(Element): class SerializedElement(Element):
@property @property
def geometry(self): ... def geometry(self) -> Serialization: ...
class SerializerSettings: class SerializerSettings:
def get_(self, name): ... def get_(self, name): ...
@@ -744,7 +744,7 @@ class ellipse(curve):
def matrix(self): ... def matrix(self): ...
class entity(declaration): class entity(declaration):
def all_attributes(self): ... def all_attributes(self) -> tuple[attribute, ...]: ...
def all_inverse_attributes(self): ... def all_inverse_attributes(self): ...
def argument_types(self): ... def argument_types(self): ...
def as_entity(self): ... def as_entity(self): ...
@@ -1127,10 +1127,10 @@ class revolve(sweep):
class schema_definition: class schema_definition:
def declaration_by_name(self, *args: str) -> declaration: ... def declaration_by_name(self, *args: str) -> declaration: ...
def declarations(self) -> tuple[declaration, ...]: ... def declarations(self) -> tuple[declaration, ...]: ...
def entities(self): ... def entities(self) -> tuple[entity, ...]: ...
def enumeration_types(self): ... def enumeration_types(self): ...
def instantiate(self, decl, data): ... def instantiate(self, decl, data): ...
def name(self): ... def name(self) -> str: ...
def select_types(self): ... def select_types(self): ...
def type_declarations(self): ... def type_declarations(self): ...
@@ -1446,7 +1446,7 @@ def polygons_to_svg(*args): ...
def read(data): ... def read(data): ...
def register_schema(arg1): ... def register_schema(arg1): ...
def schema_by_name(arg1: str) -> schema_definition: ... def schema_by_name(arg1: str) -> schema_definition: ...
def schema_names(): ... def schema_names() -> tuple[str, ...]: ...
def serialise(schema_name, shape_str, advanced): ... def serialise(schema_name, shape_str, advanced): ...
def set_feature(x, v): ... def set_feature(x, v): ...
def set_log_format_json(): ... def set_log_format_json(): ...
@@ -1455,6 +1455,6 @@ def svg_to_line_segments(data, class_name): ...
def svg_to_polygons(data, class_name): ... def svg_to_polygons(data, class_name): ...
def taxonomy_item_repr(i): ... def taxonomy_item_repr(i): ...
def tesselate(schema_name, shape_str, d): ... def tesselate(schema_name, shape_str, d): ...
def turn_off_detailed_logging(): ... def turn_off_detailed_logging() -> None: ...
def turn_on_detailed_logging(): ... def turn_on_detailed_logging() -> None: ...
def version(): ... def version() -> str: ...
+2 -2
View File
@@ -96,9 +96,9 @@ class sqlite(file):
self.preprocess_schema() self.preprocess_schema()
def preprocess_schema(self) -> None: def preprocess_schema(self) -> None:
self.ifc_class_subtypes = {} self.ifc_class_subtypes: dict[str, Any] = {}
self.ifc_class_attributes: dict[str, dict[str, ifcopenshell_wrapper.attribute]] = {} self.ifc_class_attributes: dict[str, dict[str, ifcopenshell_wrapper.attribute]] = {}
self.ifc_class_inverse_attributes = {} self.ifc_class_inverse_attributes: dict[str, Any] = {}
self.ifc_class_references = {} self.ifc_class_references = {}
self.ifc_class_inverses = {} self.ifc_class_inverses = {}
@@ -27,9 +27,7 @@ def get_constraints(product: ifcopenshell.entity_instance) -> list[ifcopenshell.
Retrieves the constraints assigned to the `product`. Retrieves the constraints assigned to the `product`.
:param product: The IFC element. :param product: The IFC element.
:type product: ifcopenshell.entity_instance
:return: List of assigned constraints. :return: List of assigned constraints.
:rtype: list[ifcopenshell.entity_instance]
""" """
constraints = [] constraints = []
for rel in product.HasAssociations or []: for rel in product.HasAssociations or []:
@@ -43,9 +41,7 @@ def get_constrained_elements(constraint: ifcopenshell.entity_instance) -> set[if
Retrieves the elements constrained by a `constraint`. Retrieves the elements constrained by a `constraint`.
:param product: The IFC element. :param product: The IFC element.
:type product: ifcopenshell.entity_instance
:return: Set of elements constrained by a `constrant`. :return: Set of elements constrained by a `constrant`.
:rtype: set[ifcopenshell.entity_instance]
""" """
elements = set() elements = set()
for rel in constraint.file.get_inverse(constraint): for rel in constraint.file.get_inverse(constraint):
@@ -59,9 +55,7 @@ def get_metrics(constraint: ifcopenshell.entity_instance) -> list[ifcopenshell.e
Retrieves the list of nested constraints for a IfcObjective `constraint`. Retrieves the list of nested constraints for a IfcObjective `constraint`.
:param product: IfcObjective constraint. :param product: IfcObjective constraint.
:type product: ifcopenshell.entity_instance
:return: List of nested constraints. :return: List of nested constraints.
:rtype: list[ifcopenshell.entity_instance]
""" """
metrics = [] metrics = []