mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-18 06:21:40 +00:00
typing and small refactor
This commit is contained in:
@@ -216,39 +216,6 @@ def get_enum_items(data, prop_name, context=None):
|
|||||||
return items
|
return items
|
||||||
|
|
||||||
|
|
||||||
def get_obj_ifc_definition_id(context, obj, obj_type):
|
|
||||||
if obj_type == "Object":
|
|
||||||
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id
|
|
||||||
elif obj_type == "Material":
|
|
||||||
return bpy.data.materials.get(obj).BIMObjectProperties.ifc_definition_id
|
|
||||||
elif obj_type == "MaterialSet":
|
|
||||||
return ifcopenshell.util.element.get_material(
|
|
||||||
tool.Ifc.get_entity(bpy.data.objects.get(obj)), should_skip_usage=True
|
|
||||||
).id()
|
|
||||||
elif obj_type == "MaterialSetItem":
|
|
||||||
return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id
|
|
||||||
elif obj_type == "Task":
|
|
||||||
return context.scene.BIMTaskTreeProperties.tasks[
|
|
||||||
context.scene.BIMWorkScheduleProperties.active_task_index
|
|
||||||
].ifc_definition_id
|
|
||||||
elif obj_type == "Cost":
|
|
||||||
return context.scene.BIMCostProperties.cost_items[
|
|
||||||
context.scene.BIMCostProperties.active_cost_item_index
|
|
||||||
].ifc_definition_id
|
|
||||||
elif obj_type == "Resource":
|
|
||||||
return context.scene.BIMResourceTreeProperties.resources[
|
|
||||||
context.scene.BIMResourceProperties.active_resource_index
|
|
||||||
].ifc_definition_id
|
|
||||||
elif obj_type == "Profile":
|
|
||||||
return context.scene.BIMProfileProperties.profiles[
|
|
||||||
context.scene.BIMProfileProperties.active_profile_index
|
|
||||||
].ifc_definition_id
|
|
||||||
elif obj_type == "WorkSchedule":
|
|
||||||
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
|
|
||||||
elif obj_type == "Group":
|
|
||||||
prop = context.scene.BIMGroupProperties
|
|
||||||
return prop.groups[prop.active_group_index].ifc_definition_id
|
|
||||||
|
|
||||||
# hack to close popup
|
# hack to close popup
|
||||||
# https://blender.stackexchange.com/a/202576/130742
|
# https://blender.stackexchange.com/a/202576/130742
|
||||||
def close_operator_panel(event):
|
def close_operator_panel(event):
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ class RemoveClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
identification = active_reference[1]
|
identification = active_reference[1]
|
||||||
|
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||||
references = ifcopenshell.util.classification.get_references(element, should_inherit=False)
|
references = ifcopenshell.util.classification.get_references(element, should_inherit=False)
|
||||||
for reference in references:
|
for reference in references:
|
||||||
@@ -358,7 +358,7 @@ class AddClassificationReference(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
break
|
break
|
||||||
|
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||||
if not ifc_definition_id:
|
if not ifc_definition_id:
|
||||||
continue
|
continue
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
@@ -406,7 +406,7 @@ class AddClassificationReferenceFromBSDD(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
classification.Location = bsdd_classification.domain_namespace_uri
|
classification.Location = bsdd_classification.domain_namespace_uri
|
||||||
|
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||||
if not ifc_definition_id:
|
if not ifc_definition_id:
|
||||||
continue
|
continue
|
||||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import os
|
import os
|
||||||
import bpy
|
import bpy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
|
import ifcopenshell.util.doc
|
||||||
import ifcopenshell.util.schema
|
import ifcopenshell.util.schema
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ class EnablePsetEditing(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
self.props = get_pset_props(context, self.obj, self.obj_type)
|
self.props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
self.props.properties.clear()
|
self.props.properties.clear()
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
|
||||||
|
|
||||||
if self.pset_id:
|
if self.pset_id:
|
||||||
pset = tool.Ifc.get().by_id(self.pset_id)
|
pset = tool.Ifc.get().by_id(self.pset_id)
|
||||||
@@ -250,7 +249,7 @@ class DisablePsetEditing(bpy.types.Operator, Operator):
|
|||||||
props = get_pset_props(context, self.obj, self.obj_type)
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
if props.active_pset_id:
|
if props.active_pset_id:
|
||||||
pset = tool.Ifc.get().by_id(props.active_pset_id)
|
pset = tool.Ifc.get().by_id(props.active_pset_id)
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||||
if tool.Pset.is_pset_empty(pset):
|
if tool.Pset.is_pset_empty(pset):
|
||||||
ifcopenshell.api.run(
|
ifcopenshell.api.run(
|
||||||
"pset.remove_pset", tool.Ifc.get(), product=tool.Ifc.get().by_id(ifc_definition_id), pset=pset
|
"pset.remove_pset", tool.Ifc.get(), product=tool.Ifc.get().by_id(ifc_definition_id), pset=pset
|
||||||
@@ -272,7 +271,7 @@ class EditPset(bpy.types.Operator, Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
props = get_pset_props(context, self.obj, self.obj_type)
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||||
properties = {}
|
properties = {}
|
||||||
|
|
||||||
@@ -343,7 +342,7 @@ class RemovePset(bpy.types.Operator, Operator):
|
|||||||
pset_name = tool.Ifc.get().by_id(self.pset_id).Name
|
pset_name = tool.Ifc.get().by_id(self.pset_id).Name
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
props = get_pset_props(context, obj, self.obj_type)
|
props = get_pset_props(context, obj, self.obj_type)
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(obj, self.obj_type, context)
|
||||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||||
pset = ifcopenshell.util.element.get_psets(element, should_inherit=False).get(pset_name, None)
|
pset = ifcopenshell.util.element.get_psets(element, should_inherit=False).get(pset_name, None)
|
||||||
if pset:
|
if pset:
|
||||||
@@ -374,7 +373,7 @@ class AddQto(bpy.types.Operator, Operator):
|
|||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
self.file = IfcStore.get_file()
|
||||||
props = get_pset_props(context, self.obj, self.obj_type)
|
props = get_pset_props(context, self.obj, self.obj_type)
|
||||||
ifc_definition_id = blenderbim.bim.helper.get_obj_ifc_definition_id(context, self.obj, self.obj_type)
|
ifc_definition_id = tool.Blender.get_obj_ifc_definition_id(self.obj, self.obj_type, context)
|
||||||
element = tool.Ifc.get().by_id(ifc_definition_id)
|
element = tool.Ifc.get().by_id(ifc_definition_id)
|
||||||
bpy.ops.bim.enable_pset_editing(
|
bpy.ops.bim.enable_pset_editing(
|
||||||
pset_id=0, pset_name=props.qto_name, pset_type="QTO", obj=self.obj, obj_type=self.obj_type
|
pset_id=0, pset_name=props.qto_name, pset_type="QTO", obj=self.obj, obj_type=self.obj_type
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ class Blender:
|
|||||||
def get_name(cls, ifc_class, name): pass
|
def get_name(cls, ifc_class, name): pass
|
||||||
def get_selected_objects(cls): pass
|
def get_selected_objects(cls): pass
|
||||||
def create_ifc_object(cls, ifc_class: str, name: str = None, data=None): pass
|
def create_ifc_object(cls, ifc_class: str, name: str = None, data=None): pass
|
||||||
def get_obj_ifc_definition_id(cls, obj=None, obj_type=None): pass
|
def get_obj_ifc_definition_id(cls, obj=None, obj_type=None, context=None): pass
|
||||||
def is_ifc_object(cls, obj): pass
|
def is_ifc_object(cls, obj): pass
|
||||||
def is_ifc_class_active(cls, ifc_class): pass
|
def is_ifc_class_active(cls, ifc_class): pass
|
||||||
def get_viewport_context(cls): pass
|
def get_viewport_context(cls): pass
|
||||||
|
|||||||
@@ -17,13 +17,17 @@
|
|||||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import bmesh
|
||||||
import json
|
import json
|
||||||
import ifcopenshell.api
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
import blenderbim.bim
|
import blenderbim.bim
|
||||||
|
import addon_utils
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import addon_utils
|
from blenderbim.bim.ifc import IFC_CONNECTED_TYPE
|
||||||
|
from typing import Any, Optional, Union, Literal, Iterable, Callable
|
||||||
|
|
||||||
|
|
||||||
VIEWPORT_ATTRIBUTES = [
|
VIEWPORT_ATTRIBUTES = [
|
||||||
@@ -37,6 +41,8 @@ VIEWPORT_ATTRIBUTES = [
|
|||||||
"clip_planes",
|
"clip_planes",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
OBJECT_DATA_TYPE = Union[bpy.types.Mesh, bpy.types.Curve, bpy.types.Camera]
|
||||||
|
|
||||||
|
|
||||||
class Blender(blenderbim.core.tool.Blender):
|
class Blender(blenderbim.core.tool.Blender):
|
||||||
OBJECT_TYPES_THAT_SUPPORT_EDIT_MODE = ("MESH", "CURVE", "SURFACE", "META", "FONT", "LATTICE", "ARMATURE")
|
OBJECT_TYPES_THAT_SUPPORT_EDIT_MODE = ("MESH", "CURVE", "SURFACE", "META", "FONT", "LATTICE", "ARMATURE")
|
||||||
@@ -44,7 +50,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
TYPE_MANAGER_ICON = "LIGHTPROBE_VOLUME" if bpy.app.version >= (4, 1, 0) else "LIGHTPROBE_GRID"
|
TYPE_MANAGER_ICON = "LIGHTPROBE_VOLUME" if bpy.app.version >= (4, 1, 0) else "LIGHTPROBE_GRID"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_area_props(cls, context):
|
def get_area_props(cls, context: bpy.types.Context) -> Any:
|
||||||
try:
|
try:
|
||||||
if context.screen.name.endswith("-nonnormal"): # Ctrl-space temporary fullscreen
|
if context.screen.name.endswith("-nonnormal"): # Ctrl-space temporary fullscreen
|
||||||
screen = bpy.data.screens[context.screen.name[0 : -len("-nonnormal")]]
|
screen = bpy.data.screens[context.screen.name[0 : -len("-nonnormal")]]
|
||||||
@@ -56,12 +62,12 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return
|
return
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_active_object(cls, obj):
|
def set_active_object(cls, obj: bpy.types.Object) -> None:
|
||||||
bpy.context.view_layer.objects.active = obj
|
bpy.context.view_layer.objects.active = obj
|
||||||
obj.select_set(True)
|
obj.select_set(True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def setup_tabs(cls):
|
def setup_tabs(cls) -> None:
|
||||||
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
|
# https://blender.stackexchange.com/questions/140644/how-can-make-the-state-of-a-boolean-property-relative-to-the-3d-view-area
|
||||||
for screen in bpy.data.screens:
|
for screen in bpy.data.screens:
|
||||||
if len(screen.BIMAreaProperties) == 20:
|
if len(screen.BIMAreaProperties) == 20:
|
||||||
@@ -71,7 +77,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
screen.BIMAreaProperties.add()
|
screen.BIMAreaProperties.add()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_tab(cls, context, tab):
|
def is_tab(cls, context: bpy.types.Context, tab: str) -> bool:
|
||||||
aprops = cls.get_area_props(context)
|
aprops = cls.get_area_props(context)
|
||||||
if not aprops:
|
if not aprops:
|
||||||
return context.screen.BIMTabProperties.tab == tab
|
return context.screen.BIMTabProperties.tab == tab
|
||||||
@@ -80,7 +86,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return aprops.tab == tab
|
return aprops.tab == tab
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_default_scene(cls):
|
def is_default_scene(cls) -> bool:
|
||||||
if len(bpy.context.scene.objects) != 3:
|
if len(bpy.context.scene.objects) != 3:
|
||||||
return False
|
return False
|
||||||
if {obj.type for obj in bpy.context.scene.objects} == {"MESH", "LIGHT", "CAMERA"}:
|
if {obj.type for obj in bpy.context.scene.objects} == {"MESH", "LIGHT", "CAMERA"}:
|
||||||
@@ -88,7 +94,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_name(cls, ifc_class, name):
|
def get_name(cls, ifc_class: str, name: str) -> str:
|
||||||
if not bpy.data.objects.get(f"{ifc_class}/{name}"):
|
if not bpy.data.objects.get(f"{ifc_class}/{name}"):
|
||||||
return name
|
return name
|
||||||
i = 2
|
i = 2
|
||||||
@@ -97,13 +103,15 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return f"{name} {i}"
|
return f"{name} {i}"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_selected_objects(cls):
|
def get_selected_objects(cls) -> set[bpy.types.Object]:
|
||||||
if bpy.context.selected_objects:
|
if bpy.context.selected_objects:
|
||||||
return set(bpy.context.selected_objects + [bpy.context.active_object])
|
return set(bpy.context.selected_objects + [bpy.context.active_object])
|
||||||
return set([bpy.context.active_object])
|
return set([bpy.context.active_object])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_ifc_object(cls, ifc_class: str, name: str = None, data=None) -> bpy.types.Object:
|
def create_ifc_object(
|
||||||
|
cls, ifc_class: str, name: Optional[str] = None, data: Optional[OBJECT_DATA_TYPE] = None
|
||||||
|
) -> bpy.types.Object:
|
||||||
name = name or "My " + ifc_class
|
name = name or "My " + ifc_class
|
||||||
name = cls.get_name(ifc_class, name)
|
name = cls.get_name(ifc_class, name)
|
||||||
obj = bpy.data.objects.new(name, data)
|
obj = bpy.data.objects.new(name, data)
|
||||||
@@ -111,7 +119,11 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return obj
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_obj_ifc_definition_id(cls, obj=None, obj_type=None):
|
def get_obj_ifc_definition_id(
|
||||||
|
cls, obj: Optional[str] = None, obj_type: Optional[str] = None, context: Optional[bpy.types.Context] = None
|
||||||
|
) -> Union[int, None]:
|
||||||
|
if context is None:
|
||||||
|
context = bpy.context
|
||||||
if obj_type == "Object":
|
if obj_type == "Object":
|
||||||
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id
|
return bpy.data.objects.get(obj).BIMObjectProperties.ifc_definition_id
|
||||||
elif obj_type == "Material":
|
elif obj_type == "Material":
|
||||||
@@ -123,41 +135,42 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
elif obj_type == "MaterialSetItem":
|
elif obj_type == "MaterialSetItem":
|
||||||
return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id
|
return bpy.data.objects.get(obj).BIMObjectMaterialProperties.active_material_set_item_id
|
||||||
elif obj_type == "Task":
|
elif obj_type == "Task":
|
||||||
return bpy.context.scene.BIMTaskTreeProperties.tasks[
|
return context.scene.BIMTaskTreeProperties.tasks[
|
||||||
bpy.context.scene.BIMWorkScheduleProperties.active_task_index
|
context.scene.BIMWorkScheduleProperties.active_task_index
|
||||||
].ifc_definition_id
|
].ifc_definition_id
|
||||||
elif obj_type == "Cost":
|
elif obj_type == "Cost":
|
||||||
return bpy.context.scene.BIMCostProperties.cost_items[
|
return context.scene.BIMCostProperties.cost_items[
|
||||||
bpy.context.scene.BIMCostProperties.active_cost_item_index
|
context.scene.BIMCostProperties.active_cost_item_index
|
||||||
].ifc_definition_id
|
].ifc_definition_id
|
||||||
elif obj_type == "Resource":
|
elif obj_type == "Resource":
|
||||||
return bpy.context.scene.BIMResourceTreeProperties.resources[
|
return context.scene.BIMResourceTreeProperties.resources[
|
||||||
bpy.context.scene.BIMResourceProperties.active_resource_index
|
context.scene.BIMResourceProperties.active_resource_index
|
||||||
].ifc_definition_id
|
].ifc_definition_id
|
||||||
elif obj_type == "Profile":
|
elif obj_type == "Profile":
|
||||||
return bpy.context.scene.BIMProfileProperties.profiles[
|
return context.scene.BIMProfileProperties.profiles[
|
||||||
bpy.context.scene.BIMProfileProperties.active_profile_index
|
context.scene.BIMProfileProperties.active_profile_index
|
||||||
].ifc_definition_id
|
].ifc_definition_id
|
||||||
elif obj_type == "WorkSchedule":
|
elif obj_type == "WorkSchedule":
|
||||||
return bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id
|
return context.scene.BIMWorkScheduleProperties.active_work_schedule_id
|
||||||
elif obj_type == "Group":
|
elif obj_type == "Group":
|
||||||
prop = bpy.context.scene.BIMGroupProperties
|
prop = context.scene.BIMGroupProperties
|
||||||
return prop.groups[prop.active_group_index].ifc_definition_id
|
return prop.groups[prop.active_group_index].ifc_definition_id
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_ifc_object(cls, obj):
|
def is_ifc_object(cls, obj: bpy.types.Object) -> bool:
|
||||||
return bool(obj.BIMObjectProperties.ifc_definition_id)
|
return bool(obj.BIMObjectProperties.ifc_definition_id)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_ifc_class_active(cls, ifc_class):
|
def is_ifc_class_active(cls, ifc_class: str) -> bool:
|
||||||
if bpy.context.active_object:
|
obj = bpy.context.active_object
|
||||||
if cls.is_ifc_object(bpy.context.active_object):
|
if obj:
|
||||||
return tool.Ifc.get_entity(bpy.context.active_object).is_a(ifc_class)
|
if cls.is_ifc_object(obj):
|
||||||
|
return tool.Ifc.get_entity(obj).is_a(ifc_class)
|
||||||
return False
|
return False
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def show_info_message(cls, text, message_type="INFO"):
|
def show_info_message(cls, text: str, message_type: Literal["INFO", "ERROR"] = "INFO") -> None:
|
||||||
"""useful for showing error messages outside blender operators
|
"""useful for showing error messages outside blender operators
|
||||||
|
|
||||||
Possible `message_type`: `INFO` / `ERROR`"""
|
Possible `message_type`: `INFO` / `ERROR`"""
|
||||||
@@ -168,14 +181,14 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
bpy.context.window_manager.popup_menu(message_ui, title=message_type.capitalize(), icon=message_type)
|
bpy.context.window_manager.popup_menu(message_ui, title=message_type.capitalize(), icon=message_type)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_view3d_area(cls):
|
def get_view3d_area(cls) -> Union[bpy.types.Area, None]:
|
||||||
for window in bpy.context.window_manager.windows:
|
for window in bpy.context.window_manager.windows:
|
||||||
for area in window.screen.areas:
|
for area in window.screen.areas:
|
||||||
if area.type == "VIEW_3D":
|
if area.type == "VIEW_3D":
|
||||||
return area
|
return area
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_blender_prop_default_value(cls, props, prop_name):
|
def get_blender_prop_default_value(cls, props, prop_name: str) -> Any:
|
||||||
prop_bl_rna = props.bl_rna.properties[prop_name]
|
prop_bl_rna = props.bl_rna.properties[prop_name]
|
||||||
if getattr(prop_bl_rna, "array_length", 0) > 0:
|
if getattr(prop_bl_rna, "array_length", 0) > 0:
|
||||||
prop_value = prop_bl_rna.default_array
|
prop_value = prop_bl_rna.default_array
|
||||||
@@ -184,7 +197,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return prop_value
|
return prop_value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_viewport_context(cls):
|
def get_viewport_context(cls) -> dict:
|
||||||
"""Get viewport area context for context overriding.
|
"""Get viewport area context for context overriding.
|
||||||
|
|
||||||
Useful for calling operators outside viewport context.
|
Useful for calling operators outside viewport context.
|
||||||
@@ -199,25 +212,25 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return context_override
|
return context_override
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_viewport_position(cls):
|
def get_viewport_position(cls) -> dict:
|
||||||
region_3d = cls.get_viewport_context()["area"].spaces[0].region_3d
|
region_3d = cls.get_viewport_context()["area"].spaces[0].region_3d
|
||||||
copy_if_possible = lambda x: x.copy() if hasattr(x, "copy") else x
|
copy_if_possible = lambda x: x.copy() if hasattr(x, "copy") else x
|
||||||
viewport_data = {attr: copy_if_possible(getattr(region_3d, attr)) for attr in VIEWPORT_ATTRIBUTES}
|
viewport_data = {attr: copy_if_possible(getattr(region_3d, attr)) for attr in VIEWPORT_ATTRIBUTES}
|
||||||
return viewport_data
|
return viewport_data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_viewport_position(cls, data):
|
def set_viewport_position(cls, data: dict) -> None:
|
||||||
region_3d = cls.get_viewport_context()["area"].spaces[0].region_3d
|
region_3d = cls.get_viewport_context()["area"].spaces[0].region_3d
|
||||||
for attr in VIEWPORT_ATTRIBUTES:
|
for attr in VIEWPORT_ATTRIBUTES:
|
||||||
setattr(region_3d, attr, data[attr])
|
setattr(region_3d, attr, data[attr])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_viewport_tool(cls, tool_name):
|
def set_viewport_tool(cls, tool_name: str) -> None:
|
||||||
with bpy.context.temp_override(**tool.Blender.get_viewport_context()):
|
with bpy.context.temp_override(**tool.Blender.get_viewport_context()):
|
||||||
bpy.ops.wm.tool_set_by_id(name=tool_name)
|
bpy.ops.wm.tool_set_by_id(name=tool_name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_shader_editor_context(cls):
|
def get_shader_editor_context(cls) -> Union[dict, None]:
|
||||||
for screen in bpy.data.screens:
|
for screen in bpy.data.screens:
|
||||||
for area in screen.areas:
|
for area in screen.areas:
|
||||||
if area.type == "NODE_EDITOR":
|
if area.type == "NODE_EDITOR":
|
||||||
@@ -227,7 +240,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return context_override
|
return context_override
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def copy_node_graph(cls, material_to, material_from):
|
def copy_node_graph(cls, material_to: bpy.types.Material, material_from: bpy.types.Material) -> None:
|
||||||
# https://projects.blender.org/blender/blender/issues/108763
|
# https://projects.blender.org/blender/blender/issues/108763
|
||||||
if bpy.app.version[:2] == (4, 0):
|
if bpy.app.version[:2] == (4, 0):
|
||||||
print(
|
print(
|
||||||
@@ -271,7 +284,9 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
shader_editor.pin = previous_pin_setting
|
shader_editor.pin = previous_pin_setting
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_material_node(cls, blender_material, node_type, kwargs={}):
|
def get_material_node(
|
||||||
|
cls, blender_material: bpy.types.Material, node_type: str, kwargs: Optional[dict] = {}
|
||||||
|
) -> Union[bpy.types.ShaderNode, None]:
|
||||||
"""returns first node from the `blender_material` shader graph with type `node_type`"""
|
"""returns first node from the `blender_material` shader graph with type `node_type`"""
|
||||||
if not blender_material.use_nodes:
|
if not blender_material.use_nodes:
|
||||||
return
|
return
|
||||||
@@ -281,15 +296,15 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return node
|
return node
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_screen(cls):
|
def update_screen(cls) -> None:
|
||||||
bpy.ops.wm.redraw_timer(type="DRAW_WIN_SWAP", iterations=1)
|
bpy.ops.wm.redraw_timer(type="DRAW_WIN_SWAP", iterations=1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_viewport(cls):
|
def update_viewport(cls) -> None:
|
||||||
tool.Blender.get_viewport_context()["area"].tag_redraw()
|
tool.Blender.get_viewport_context()["area"].tag_redraw()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def force_depsgraph_update(cls):
|
def force_depsgraph_update(cls) -> None:
|
||||||
"""useful if you need to trigger callbacks like `depsgraph_update_pre`"""
|
"""useful if you need to trigger callbacks like `depsgraph_update_pre`"""
|
||||||
# blender is requiring some ID to be changed
|
# blender is requiring some ID to be changed
|
||||||
# to trigger depsgraph update
|
# to trigger depsgraph update
|
||||||
@@ -298,9 +313,11 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
bpy.context.view_layer.update()
|
bpy.context.view_layer.update()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def ensure_unique_name(cls, name, objects, iteration=0):
|
def ensure_unique_name(cls, name: str, objects: Iterable[str], iteration=0) -> str:
|
||||||
"""returns a unique name for the given name and dictionary of objects
|
"""returns a unique name for the given name and dictionary of objects
|
||||||
blender style name with .001, .002, etc. suffix
|
blender style name with .001, .002, etc. suffix.
|
||||||
|
|
||||||
|
objects can be `bpy.data.objects`.
|
||||||
"""
|
"""
|
||||||
current_iteration = name if not iteration else f"{name}.{iteration:03d}"
|
current_iteration = name if not iteration else f"{name}.{iteration:03d}"
|
||||||
if current_iteration not in objects:
|
if current_iteration not in objects:
|
||||||
@@ -308,7 +325,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return cls.ensure_unique_name(name, objects, iteration + 1)
|
return cls.ensure_unique_name(name, objects, iteration + 1)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def blender_path_to_posix(cls, blender_path):
|
def blender_path_to_posix(cls, blender_path: str) -> str:
|
||||||
"""Process blender path to be saved as posix.
|
"""Process blender path to be saved as posix.
|
||||||
|
|
||||||
If path is relative the method will keep it relative to .ifc file
|
If path is relative the method will keep it relative to .ifc file
|
||||||
@@ -323,7 +340,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return path.as_posix()
|
return path.as_posix()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default_selection_keypmap(cls):
|
def get_default_selection_keypmap(cls) -> tuple:
|
||||||
"""keymap to replicate default blender selection behaviour with click and box selection"""
|
"""keymap to replicate default blender selection behaviour with click and box selection"""
|
||||||
# code below comes from blender_default.py which is part of default blender scripts licensed under GPL v2
|
# code below comes from blender_default.py which is part of default blender scripts licensed under GPL v2
|
||||||
# https://github.com/blender/blender/blob/master/release/scripts/presets/keyconfig/keymap_data/blender_default.py
|
# https://github.com/blender/blender/blob/master/release/scripts/presets/keyconfig/keymap_data/blender_default.py
|
||||||
@@ -370,7 +387,9 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return keymap
|
return keymap
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_layout_hotkey_operator(cls, tool_name, layout, text, hotkey, description):
|
def add_layout_hotkey_operator(
|
||||||
|
cls, tool_name: str, layout: bpy.types.UILayout, text: str, hotkey: str, description: str
|
||||||
|
) -> tuple[bpy.types.OperatorProperties, bpy.types.UILayout]:
|
||||||
modifiers = {
|
modifiers = {
|
||||||
"A": "EVENT_ALT",
|
"A": "EVENT_ALT",
|
||||||
"S": "EVENT_SHIFT",
|
"S": "EVENT_SHIFT",
|
||||||
@@ -387,7 +406,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return op, row
|
return op, row
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object_bounding_box(cls, obj):
|
def get_object_bounding_box(cls, obj: bpy.types.Object) -> dict:
|
||||||
"""Returns dict with local min and max x, y, z values for the object.
|
"""Returns dict with local min and max x, y, z values for the object.
|
||||||
|
|
||||||
Careful with using this method for objects in EDIT mode because
|
Careful with using this method for objects in EDIT mode because
|
||||||
@@ -408,14 +427,20 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return bbox_dict
|
return bbox_dict
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def select_and_activate_single_object(cls, context, active_object):
|
def select_and_activate_single_object(cls, context: bpy.types.Context, active_object: bpy.types.Object) -> None:
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
context.view_layer.objects.active = active_object
|
context.view_layer.objects.active = active_object
|
||||||
active_object.select_set(True)
|
active_object.select_set(True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_objects_selection(cls, context, active_object, selected_objects, clear_previous_selection=True):
|
def set_objects_selection(
|
||||||
|
cls,
|
||||||
|
context: bpy.types.Context,
|
||||||
|
active_object: Optional[bpy.types.Object] = None,
|
||||||
|
selected_objects: list[bpy.types.Object] = list(),
|
||||||
|
clear_previous_selection=True,
|
||||||
|
) -> None:
|
||||||
if clear_previous_selection:
|
if clear_previous_selection:
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
@@ -426,7 +451,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
active_object.select_set(True)
|
active_object.select_set(True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def enum_property_has_valid_index(cls, props, prop_name, enum_items):
|
def enum_property_has_valid_index(cls, props, prop_name: str, enum_items: tuple) -> bool:
|
||||||
"""method created for readibility and to avoid console warnings like
|
"""method created for readibility and to avoid console warnings like
|
||||||
`pyrna_enum_to_py: current value '17' matches no enum in 'BIMModelProperties', '', 'relating_type_id'`
|
`pyrna_enum_to_py: current value '17' matches no enum in 'BIMModelProperties', '', 'relating_type_id'`
|
||||||
"""
|
"""
|
||||||
@@ -437,7 +462,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return current_value_index < len(enum_items)
|
return current_value_index < len(enum_items)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def append_data_block(cls, filepath, data_block_type, name, link=False, relative=False):
|
def append_data_block(cls, filepath: str, data_block_type: str, name: str, link=False, relative=False) -> dict:
|
||||||
if Path(filepath) == Path(bpy.data.filepath):
|
if Path(filepath) == Path(bpy.data.filepath):
|
||||||
data_block = getattr(bpy.data, data_block_type).get(name, None)
|
data_block = getattr(bpy.data, data_block_type).get(name, None)
|
||||||
if not data_block:
|
if not data_block:
|
||||||
@@ -451,7 +476,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return {"data_block": getattr(data_to, data_block_type)[0], "msg": ""}
|
return {"data_block": getattr(data_to, data_block_type)[0], "msg": ""}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def remove_data_block(cls, data_block, do_unlink=True):
|
def remove_data_block(cls, data_block: bpy.types.ID, do_unlink=True) -> None:
|
||||||
"""Removes a datablock (such as a mesh)
|
"""Removes a datablock (such as a mesh)
|
||||||
|
|
||||||
See https://projects.blender.org/blender/blender/issues/118787 for more
|
See https://projects.blender.org/blender/blender/issues/118787 for more
|
||||||
@@ -473,7 +498,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
|
|
||||||
## BMESH UTILS ##
|
## BMESH UTILS ##
|
||||||
@classmethod
|
@classmethod
|
||||||
def apply_bmesh(cls, mesh, bm, obj=None):
|
def apply_bmesh(cls, mesh: bpy.types.Mesh, bm: bmesh.types.BMesh, obj: Optional[bpy.types.Object] = None) -> None:
|
||||||
"""`obj` argument is not optional if you plan to update mesh in EDIT mode
|
"""`obj` argument is not optional if you plan to update mesh in EDIT mode
|
||||||
and it's possible that that mesh object won't be currenly active."""
|
and it's possible that that mesh object won't be currenly active."""
|
||||||
import bmesh
|
import bmesh
|
||||||
@@ -507,7 +532,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
mesh.update()
|
mesh.update()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_bmesh_for_mesh(cls, mesh, clean=False):
|
def get_bmesh_for_mesh(cls, mesh: bpy.types.Mesh, clean=False) -> bmesh.types.BMesh:
|
||||||
import bmesh
|
import bmesh
|
||||||
|
|
||||||
if mesh.is_editmode:
|
if mesh.is_editmode:
|
||||||
@@ -521,7 +546,16 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return bm
|
return bm
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def bmesh_join(cls, bm_a, bm_b, callback=None):
|
def bmesh_join(
|
||||||
|
cls,
|
||||||
|
bm_a: bmesh.types.BMesh,
|
||||||
|
bm_b: bmesh.types.BMesh,
|
||||||
|
callback: Optional[
|
||||||
|
Callable[
|
||||||
|
[bmesh.types.BMesh, list[bmesh.types.BMVert], list[bmesh.types.BMEdge], list[bmesh.types.BMFace]], None
|
||||||
|
]
|
||||||
|
] = None,
|
||||||
|
):
|
||||||
"""Join two meshes into single one, store it in `bm_a`"""
|
"""Join two meshes into single one, store it in `bm_a`"""
|
||||||
import bmesh
|
import bmesh
|
||||||
|
|
||||||
@@ -536,7 +570,7 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return bm_a
|
return bm_a
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def toggle_edit_mode(cls, context):
|
def toggle_edit_mode(cls, context: bpy.types.Context) -> set:
|
||||||
ao = context.active_object
|
ao = context.active_object
|
||||||
if not ao:
|
if not ao:
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
@@ -548,21 +582,21 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def is_object_an_ifc_class(cls, obj, classes):
|
def is_object_an_ifc_class(cls, obj: bpy.types.Object, classes: Iterable[str]) -> bool:
|
||||||
if not tool.Ifc.get():
|
if not tool.Ifc.get():
|
||||||
return False
|
return False
|
||||||
element = tool.Ifc.get_entity(obj)
|
element = tool.Ifc.get_entity(obj)
|
||||||
return element and element.is_a() in classes
|
return element and element.is_a() in classes
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object_from_guid(cls, guid):
|
def get_object_from_guid(cls, guid: str) -> Union[IFC_CONNECTED_TYPE, None]:
|
||||||
element = tool.Ifc.get().by_guid(guid)
|
element = tool.Ifc.get().by_guid(guid)
|
||||||
obj = tool.Ifc.get_object(element)
|
obj = tool.Ifc.get_object(element)
|
||||||
if obj:
|
if obj:
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def lock_transform(cls, obj, lock_state=True):
|
def lock_transform(cls, obj: bpy.types.ObjectBase, lock_state=True) -> None:
|
||||||
for prop in ("lock_location", "lock_rotation", "lock_scale"):
|
for prop in ("lock_location", "lock_rotation", "lock_scale"):
|
||||||
attr = getattr(obj, prop)
|
attr = getattr(obj, prop)
|
||||||
for axis_idx in range(3):
|
for axis_idx in range(3):
|
||||||
@@ -571,7 +605,9 @@ class Blender(blenderbim.core.tool.Blender):
|
|||||||
operator_invoke_filepath_hotkeys_description = "Hold Shift to open the file, Alt to browse containing directory"
|
operator_invoke_filepath_hotkeys_description = "Hold Shift to open the file, Alt to browse containing directory"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def operator_invoke_filepath_hotkeys(cls, operator, context, event, filepath: Path):
|
def operator_invoke_filepath_hotkeys(
|
||||||
|
cls, operator: bpy.types.Operator, context: bpy.types.Context, event: bpy.types.Event, filepath: Path
|
||||||
|
) -> Union[set, None]:
|
||||||
if not event.alt and not event.shift:
|
if not event.alt and not event.shift:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,11 @@
|
|||||||
# Note: it is the intent for you to override these with your own functions
|
# Note: it is the intent for you to override these with your own functions
|
||||||
|
|
||||||
|
|
||||||
def get_application(ifc):
|
import ifcopenshell
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
def get_application(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
"""Returns the application representing the authoring software
|
"""Returns the application representing the authoring software
|
||||||
|
|
||||||
It is expected for you to overload this function with your own
|
It is expected for you to overload this function with your own
|
||||||
@@ -39,7 +43,7 @@ def get_application(ifc):
|
|||||||
return (app or [None])[0]
|
return (app or [None])[0]
|
||||||
|
|
||||||
|
|
||||||
def get_user(ifc):
|
def get_user(ifc: ifcopenshell.file) -> Union[ifcopenshell.entity_instance, None]:
|
||||||
"""Returns the active authoring user
|
"""Returns the active authoring user
|
||||||
|
|
||||||
It is expected for you to overload this function with your own
|
It is expected for you to overload this function with your own
|
||||||
|
|||||||
@@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pprint import pprint
|
|
||||||
import copy
|
import copy
|
||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
import ifcopenshell.util.attribute
|
import ifcopenshell.util.attribute
|
||||||
|
import ifcopenshell.util.schema
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import glob
|
import glob
|
||||||
@@ -103,7 +103,7 @@ def get_db(version):
|
|||||||
return db.get(version)
|
return db.get(version)
|
||||||
|
|
||||||
|
|
||||||
def get_schema_by_name(version):
|
def get_schema_by_name(version: str):
|
||||||
global schema_by_name
|
global schema_by_name
|
||||||
version = ifcopenshell.util.schema.get_fallback_schema(version)
|
version = ifcopenshell.util.schema.get_fallback_schema(version)
|
||||||
if not schema_by_name[version]:
|
if not schema_by_name[version]:
|
||||||
|
|||||||
@@ -20,8 +20,10 @@ import numpy as np
|
|||||||
import ifcopenshell
|
import ifcopenshell
|
||||||
from typing import Literal, Iterable
|
from typing import Literal, Iterable
|
||||||
|
|
||||||
|
MatrixType = np.ndarray[np.ndarray[float]]
|
||||||
|
|
||||||
def a2p(o: Iterable[float], z: Iterable[float], x: Iterable[float]) -> np.array:
|
|
||||||
|
def a2p(o: Iterable[float], z: Iterable[float], x: Iterable[float]) -> MatrixType:
|
||||||
"""Converts a location, X, and Z axis vector to a 4x4 transformation matrix
|
"""Converts a location, X, and Z axis vector to a 4x4 transformation matrix
|
||||||
|
|
||||||
IFC uses a right-handed coordinate system, so it is not necessary to
|
IFC uses a right-handed coordinate system, so it is not necessary to
|
||||||
@@ -34,7 +36,7 @@ def a2p(o: Iterable[float], z: Iterable[float], x: Iterable[float]) -> np.array:
|
|||||||
:param x: The +X vector / axis of the matrix
|
:param x: The +X vector / axis of the matrix
|
||||||
:type x: iterable[float]
|
:type x: iterable[float]
|
||||||
:return: A 4x4 numpy matrix
|
:return: A 4x4 numpy matrix
|
||||||
:rtype: np.array[np.array[float]]
|
:rtype: np.ndarray[np.ndarray[float]]
|
||||||
"""
|
"""
|
||||||
x = x / np.linalg.norm(x)
|
x = x / np.linalg.norm(x)
|
||||||
z = z / np.linalg.norm(z)
|
z = z / np.linalg.norm(z)
|
||||||
@@ -46,7 +48,7 @@ def a2p(o: Iterable[float], z: Iterable[float], x: Iterable[float]) -> np.array:
|
|||||||
return r.T
|
return r.T
|
||||||
|
|
||||||
|
|
||||||
def get_axis2placement(placement: ifcopenshell.entity_instance) -> np.array:
|
def get_axis2placement(placement: ifcopenshell.entity_instance) -> MatrixType:
|
||||||
"""Parses an IfcAxis2Placement (2D or 3D) to a 4x4 transformation matrix
|
"""Parses an IfcAxis2Placement (2D or 3D) to a 4x4 transformation matrix
|
||||||
|
|
||||||
Note that this function only parses a single placement axis. If you want to
|
Note that this function only parses a single placement axis. If you want to
|
||||||
@@ -57,7 +59,7 @@ def get_axis2placement(placement: ifcopenshell.entity_instance) -> np.array:
|
|||||||
:param placement: The IfcLocalPlacement enitity
|
:param placement: The IfcLocalPlacement enitity
|
||||||
:type placement: ifcopenshell.entity_instance.entity_instance
|
:type placement: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: A 4x4 numpy matrix
|
:return: A 4x4 numpy matrix
|
||||||
:rtype: np.array[np.array[float]]
|
:rtype: np.ndarray[np.ndarray[float]]
|
||||||
"""
|
"""
|
||||||
if placement.is_a("IfcAxis2Placement3D"):
|
if placement.is_a("IfcAxis2Placement3D"):
|
||||||
z = np.array(placement.Axis.DirectionRatios if placement.Axis else (0, 0, 1))
|
z = np.array(placement.Axis.DirectionRatios if placement.Axis else (0, 0, 1))
|
||||||
@@ -74,7 +76,7 @@ def get_axis2placement(placement: ifcopenshell.entity_instance) -> np.array:
|
|||||||
return a2p(o, z, x)
|
return a2p(o, z, x)
|
||||||
|
|
||||||
|
|
||||||
def get_local_placement(placement: ifcopenshell.entity_instance) -> np.array:
|
def get_local_placement(placement: ifcopenshell.entity_instance) -> MatrixType:
|
||||||
"""Parse a local placement into a 4x4 transformation matrix
|
"""Parse a local placement into a 4x4 transformation matrix
|
||||||
|
|
||||||
This is typically used to find the location and rotation of an element. The
|
This is typically used to find the location and rotation of an element. The
|
||||||
@@ -97,7 +99,7 @@ def get_local_placement(placement: ifcopenshell.entity_instance) -> np.array:
|
|||||||
:param placement: The IfcLocalPlacement entity
|
:param placement: The IfcLocalPlacement entity
|
||||||
:type placement: ifcopenshell.entity_instance.entity_instance
|
:type placement: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: A 4x4 numpy matrix
|
:return: A 4x4 numpy matrix
|
||||||
:rtype: np.array[np.array[float]]
|
:rtype: np.ndarray[np.ndarray[float]]
|
||||||
"""
|
"""
|
||||||
if placement is None:
|
if placement is None:
|
||||||
return np.eye(4)
|
return np.eye(4)
|
||||||
@@ -108,7 +110,7 @@ def get_local_placement(placement: ifcopenshell.entity_instance) -> np.array:
|
|||||||
return np.dot(parent, get_axis2placement(placement.RelativePlacement))
|
return np.dot(parent, get_axis2placement(placement.RelativePlacement))
|
||||||
|
|
||||||
|
|
||||||
def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) -> np.array:
|
def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) -> MatrixType:
|
||||||
"""Parses an IfcCartesianTransformationOperator into a 4x4 transformation matrix
|
"""Parses an IfcCartesianTransformationOperator into a 4x4 transformation matrix
|
||||||
|
|
||||||
Note that in general you will not need to call this directly. See
|
Note that in general you will not need to call this directly. See
|
||||||
@@ -117,7 +119,7 @@ def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) ->
|
|||||||
:param item: The IfcCartesianTransformationOperator entity
|
:param item: The IfcCartesianTransformationOperator entity
|
||||||
:type item: ifcopenshell.entity_instance.entity_instance
|
:type item: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: A 4x4 numpy transformation matrix
|
:return: A 4x4 numpy transformation matrix
|
||||||
:rtype: np.array[np.array[float]]
|
:rtype: np.ndarray[np.ndarray[float]]
|
||||||
"""
|
"""
|
||||||
origin = np.array(inst.LocalOrigin.Coordinates)
|
origin = np.array(inst.LocalOrigin.Coordinates)
|
||||||
axis1 = np.array((1.0, 0.0, 0.0))
|
axis1 = np.array((1.0, 0.0, 0.0))
|
||||||
@@ -153,7 +155,7 @@ def get_cartesiantransformationoperator3d(inst: ifcopenshell.entity_instance) ->
|
|||||||
return m4
|
return m4
|
||||||
|
|
||||||
|
|
||||||
def get_mappeditem_transformation(item: ifcopenshell.entity_instance) -> np.array:
|
def get_mappeditem_transformation(item: ifcopenshell.entity_instance) -> MatrixType:
|
||||||
"""Parse an IfcMappedItem into a 4x4 transformation matrix
|
"""Parse an IfcMappedItem into a 4x4 transformation matrix
|
||||||
|
|
||||||
Mapped items take a representation with an origin and transform them with a
|
Mapped items take a representation with an origin and transform them with a
|
||||||
@@ -163,7 +165,7 @@ def get_mappeditem_transformation(item: ifcopenshell.entity_instance) -> np.arra
|
|||||||
:param item: The IfcMappedItem entity
|
:param item: The IfcMappedItem entity
|
||||||
:type item: ifcopenshell.entity_instance.entity_instance
|
:type item: ifcopenshell.entity_instance.entity_instance
|
||||||
:return: A 4x4 numpy transformation matrix
|
:return: A 4x4 numpy transformation matrix
|
||||||
:rtype: np.array[np.array[float]]
|
:rtype: np.ndarray[np.ndarray[float]]
|
||||||
"""
|
"""
|
||||||
m4 = get_axis2placement(item.MappingSource.MappingOrigin)
|
m4 = get_axis2placement(item.MappingSource.MappingOrigin)
|
||||||
# TODO 2d
|
# TODO 2d
|
||||||
@@ -188,7 +190,7 @@ def get_storey_elevation(storey: ifcopenshell.entity_instance) -> float:
|
|||||||
return getattr(storey, "Elevation", 0.0) or 0.0
|
return getattr(storey, "Elevation", 0.0) or 0.0
|
||||||
|
|
||||||
|
|
||||||
def rotation(angle: float, axis: Literal["X", "Y", "Z"], is_degrees=True) -> np.array:
|
def rotation(angle: float, axis: Literal["X", "Y", "Z"], is_degrees=True) -> MatrixType:
|
||||||
"""Create a 4x4 numpy matrix representing an euler rotation
|
"""Create a 4x4 numpy matrix representing an euler rotation
|
||||||
|
|
||||||
:param angle: The angle of rotation
|
:param angle: The angle of rotation
|
||||||
@@ -199,7 +201,7 @@ def rotation(angle: float, axis: Literal["X", "Y", "Z"], is_degrees=True) -> np.
|
|||||||
radians. Defaults to true (i.e. degrees).
|
radians. Defaults to true (i.e. degrees).
|
||||||
:type is_degrees: bool
|
:type is_degrees: bool
|
||||||
:return: A 4x4 numpy rotation matrix
|
:return: A 4x4 numpy rotation matrix
|
||||||
:rtype: np.array[np.array[float]]
|
:rtype: np.ndarray[np.ndarray[float]]
|
||||||
"""
|
"""
|
||||||
theta = np.radians(angle) if is_degrees else angle
|
theta = np.radians(angle) if is_degrees else angle
|
||||||
cos, sin = np.cos(theta), np.sin(theta)
|
cos, sin = np.cos(theta), np.sin(theta)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import ifcopenshell.util.representation
|
|||||||
from typing import Optional, Literal, Union, Iterable
|
from typing import Optional, Literal, Union, Iterable
|
||||||
|
|
||||||
tol = 1e-6
|
tol = 1e-6
|
||||||
AXIS_LITERAL = Union[Literal["X"], Literal["Y"], Literal["Z"]]
|
AXIS_LITERAL = Literal["X", "Y", "Z"]
|
||||||
VECTOR_3D = tuple[float, float, float]
|
VECTOR_3D = tuple[float, float, float]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -603,7 +603,7 @@ def format_length(
|
|||||||
precision: float,
|
precision: float,
|
||||||
decimal_places: int = 2,
|
decimal_places: int = 2,
|
||||||
suppress_zero_inches=True,
|
suppress_zero_inches=True,
|
||||||
unit_system: Union[Literal["metric"], Literal["imperial"]] = "imperial",
|
unit_system: Literal["metric", "imperial"] = "imperial",
|
||||||
input_unit="foot",
|
input_unit="foot",
|
||||||
output_unit="foot",
|
output_unit="foot",
|
||||||
) -> str:
|
) -> str:
|
||||||
|
|||||||
Reference in New Issue
Block a user