This commit is contained in:
Andrej730
2024-06-25 12:15:25 +05:00
parent a65c6b6d3f
commit 0a06d58c42
6 changed files with 155 additions and 117 deletions
+1 -1
View File
@@ -167,7 +167,7 @@ class IfcStore:
IfcStore.file = ifcopenshell.open(path) IfcStore.file = ifcopenshell.open(path)
@staticmethod @staticmethod
def get_schema(): def get_schema() -> ifcopenshell.ifcopenshell_wrapper.schema_definition:
if IfcStore.file is None: if IfcStore.file is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name( IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
bpy.context.scene.BIMProjectProperties.export_schema bpy.context.scene.BIMProjectProperties.export_schema
@@ -19,6 +19,7 @@
from collections import defaultdict from collections import defaultdict
import bpy import bpy
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.schema
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc, get_class_suggestions from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc, get_class_suggestions
import blenderbim.tool as tool import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
+28 -19
View File
@@ -54,6 +54,7 @@ def assign_container(
) -> Union[ifcopenshell.entity_instance, None]: ) -> Union[ifcopenshell.entity_instance, None]:
if not spatial.can_contain(structure_obj, element_obj): if not spatial.can_contain(structure_obj, element_obj):
return return
assert element_obj and structure_obj # Type checker.
rel = ifc.run( rel = ifc.run(
"spatial.assign_container", "spatial.assign_container",
products=[ifc.get_entity(element_obj)], products=[ifc.get_entity(element_obj)],
@@ -64,25 +65,31 @@ def assign_container(
return rel return rel
def enable_editing_container(spatial, obj=None): def enable_editing_container(spatial: tool.Spatial, obj: bpy.types.Object) -> None:
spatial.enable_editing(obj) spatial.enable_editing(obj)
spatial.import_containers() spatial.import_containers()
def disable_editing_container(spatial, obj=None): def disable_editing_container(spatial: tool.Spatial, obj: bpy.types.Object) -> None:
spatial.disable_editing(obj) spatial.disable_editing(obj)
def change_spatial_level(spatial, parent=None): def change_spatial_level(spatial: tool.Spatial, parent: Union[ifcopenshell.entity_instance, None] = None) -> None:
spatial.import_containers(parent=parent) spatial.import_containers(parent=parent)
def remove_container(ifc, collector, obj=None): def remove_container(ifc: tool.Ifc, collector: tool.Collector, obj: bpy.types.Object) -> None:
ifc.run("spatial.unassign_container", products=[ifc.get_entity(obj)]) ifc.run("spatial.unassign_container", products=[ifc.get_entity(obj)])
collector.assign(obj) collector.assign(obj)
def copy_to_container(ifc, collector, spatial, obj=None, containers=None): def copy_to_container(
ifc: tool.Ifc,
collector: tool.Collector,
spatial: tool.Spatial,
obj: bpy.types.Object,
containers: list[ifcopenshell.entity_instance],
) -> list[ifcopenshell.entity_instance]:
element = ifc.get_entity(obj) element = ifc.get_entity(obj)
if not element: if not element:
return return
@@ -102,54 +109,56 @@ def copy_to_container(ifc, collector, spatial, obj=None, containers=None):
return result_objs return result_objs
def select_container(ifc, spatial, obj=None): def select_container(ifc: tool.Ifc, spatial: tool.Spatial, obj: bpy.types.Object) -> None:
element = ifc.get_entity(obj) element = ifc.get_entity(obj)
if not element: if not element:
return return
spatial.set_active_object(ifc.get_object(spatial.get_container(element))) spatial.set_active_object(ifc.get_object(spatial.get_container(element)))
def select_similar_container(ifc, spatial, obj=None): def select_similar_container(ifc: tool.Ifc, spatial: tool.Spatial, obj: bpy.types.Object) -> None:
element = ifc.get_entity(obj) element = ifc.get_entity(obj)
if element: if element:
spatial.select_products(spatial.get_decomposed_elements(spatial.get_container(element))) spatial.select_products(spatial.get_decomposed_elements(spatial.get_container(element)))
def select_product(spatial, product): def select_product(spatial: tool.Spatial, product: ifcopenshell.entity_instance) -> None:
spatial.select_products([product]) spatial.select_products([product])
def import_spatial_decomposition(spatial): def import_spatial_decomposition(spatial: tool.Spatial) -> None:
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def edit_container_attributes(spatial, entity=None): def edit_container_attributes(spatial: tool.Spatial, entity: ifcopenshell.entity_instance) -> None:
spatial.edit_container_attributes(entity) spatial.edit_container_attributes(entity)
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def contract_container(spatial, container=None): def contract_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
spatial.contract_container(container) spatial.contract_container(container)
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def expand_container(spatial, container=None): def expand_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
spatial.expand_container(container) spatial.expand_container(container)
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def delete_container(ifc, spatial, geometry, container=None): def delete_container(
ifc: tool.Ifc, spatial: tool.Spatial, geometry: tool.Geometry, container: ifcopenshell.entity_instance
) -> None:
geometry.delete_ifc_object(ifc.get_object(container)) geometry.delete_ifc_object(ifc.get_object(container))
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def select_decomposed_elements(spatial): def select_decomposed_elements(spatial: tool.Spatial) -> None:
container = spatial.get_active_container() container = spatial.get_active_container()
if container: if container:
spatial.select_products(spatial.get_decomposed_elements(container)) spatial.select_products(spatial.get_decomposed_elements(container))
def generate_space(ifc, model, root, spatial, type): def generate_space(ifc: tool.Ifc, model: tool.Model, root: tool.Root, spatial: tool.Spatial, type: tool.Type) -> None:
if not root.get_default_container(): if not root.get_default_container():
raise NoDefaultContainer() raise NoDefaultContainer()
@@ -201,7 +210,7 @@ def generate_space(ifc, model, root, spatial, type):
spatial.import_spatial_decomposition() spatial.import_spatial_decomposition()
def generate_spaces_from_walls(ifc, spatial, collector): def generate_spaces_from_walls(ifc: tool.Ifc, spatial: tool.Spatial, collector: tool.Collector) -> None:
z = spatial.get_active_obj_z() z = spatial.get_active_obj_z()
h = spatial.get_active_obj_height() h = spatial.get_active_obj_height()
@@ -221,7 +230,7 @@ def generate_spaces_from_walls(ifc, spatial, collector):
spatial.assign_ifcspace_class_to_obj(obj) spatial.assign_ifcspace_class_to_obj(obj)
def toggle_space_visibility(ifc, spatial): def toggle_space_visibility(ifc: tool.Ifc, spatial: tool.Spatial) -> None:
model = ifc.get() model = ifc.get()
spaces = model.by_type("IfcSpace") spaces = model.by_type("IfcSpace")
if not spaces: if not spaces:
@@ -229,7 +238,7 @@ def toggle_space_visibility(ifc, spatial):
spatial.toggle_spaces_visibility_wired_and_textured(spaces) spatial.toggle_spaces_visibility_wired_and_textured(spaces)
def toggle_hide_spaces(ifc, spatial): def toggle_hide_spaces(ifc: tool.Ifc, spatial: tool.Spatial) -> None:
model = ifc.get() model = ifc.get()
spaces = model.by_type("IfcSpace") spaces = model.by_type("IfcSpace")
if not spaces: if not spaces:
@@ -237,7 +246,7 @@ def toggle_hide_spaces(ifc, spatial):
spatial.toggle_hide_spaces(spaces) spatial.toggle_hide_spaces(spaces)
def set_default_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance): def set_default_container(spatial: tool.Spatial, container: ifcopenshell.entity_instance) -> None:
spatial.set_default_container(container) spatial.set_default_container(container)
+10 -5
View File
@@ -20,6 +20,7 @@ import os
import bpy import bpy
import numpy as np import numpy as np
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
import ifcopenshell.util.element import ifcopenshell.util.element
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.bim.handler import blenderbim.bim.handler
@@ -75,21 +76,25 @@ class Ifc(blenderbim.core.tool.Ifc):
return True return True
@classmethod @classmethod
def schema(cls): def schema(cls) -> ifcopenshell_wrapper.schema_definition:
return IfcStore.get_schema() return IfcStore.get_schema()
@classmethod @classmethod
def get_entity(cls, obj: IFC_CONNECTED_TYPE) -> ifcopenshell.entity_instance: def get_entity(cls, obj: IFC_CONNECTED_TYPE) -> Union[ifcopenshell.entity_instance, None]:
"""Get linked IFC entity based on obj's BIMObjectProperties.ifc_definition_id.
Return None if object is not linked to IFC or it's linked to non-existent element.
"""
ifc = IfcStore.get_file() ifc = IfcStore.get_file()
props = getattr(obj, "BIMObjectProperties", None) props = getattr(obj, "BIMObjectProperties", None)
if ifc and props and props.ifc_definition_id: if ifc and props and props.ifc_definition_id:
try: try:
return IfcStore.get_file().by_id(props.ifc_definition_id) return ifc.by_id(props.ifc_definition_id)
except: except RuntimeError:
pass pass
@classmethod @classmethod
def get_entity_by_id(cls, entity_id) -> Union[ifcopenshell.entity_instance, None]: def get_entity_by_id(cls, entity_id: int) -> Union[ifcopenshell.entity_instance, None]:
"""useful to check whether entity_id is still exists in IFC""" """useful to check whether entity_id is still exists in IFC"""
ifc_file = tool.Ifc.get() ifc_file = tool.Ifc.get()
try: try:
+111 -92
View File
@@ -16,11 +16,19 @@
# 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 BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>. # along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations
import bpy import bpy
import bmesh import bmesh
import shapely import shapely
import shapely.ops
import ifcopenshell import ifcopenshell
import ifcopenshell.geom
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.placement
import ifcopenshell.util.representation
import ifcopenshell.util.shape_builder
import ifcopenshell.util.type
import ifcopenshell.util.unit
import blenderbim.core.type import blenderbim.core.type
import blenderbim.core.tool import blenderbim.core.tool
import blenderbim.core.root import blenderbim.core.root
@@ -31,12 +39,14 @@ import json
from math import pi from math import pi
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from shapely import Polygon from shapely import Polygon
from typing import Generator, Optional from typing import Generator, Optional, Union, Literal, Any
class Spatial(blenderbim.core.tool.Spatial): class Spatial(blenderbim.core.tool.Spatial):
@classmethod @classmethod
def can_contain(cls, structure_obj: bpy.types.Object, element_obj: bpy.types.Object) -> bool: def can_contain(
cls, structure_obj: Union[bpy.types.Object, None], element_obj: Union[bpy.types.Object, None]
) -> bool:
structure = tool.Ifc.get_entity(structure_obj) structure = tool.Ifc.get_entity(structure_obj)
element = tool.Ifc.get_entity(element_obj) element = tool.Ifc.get_entity(element_obj)
if not structure or not element: if not structure or not element:
@@ -54,7 +64,9 @@ class Spatial(blenderbim.core.tool.Spatial):
return True return True
@classmethod @classmethod
def can_reference(cls, structure: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance) -> bool: def can_reference(
cls, structure: Union[ifcopenshell.entity_instance, None], element: Union[ifcopenshell.entity_instance, None]
) -> bool:
if not structure or not element: if not structure or not element:
return False return False
if tool.Ifc.get_schema() == "IFC2X3": if tool.Ifc.get_schema() == "IFC2X3":
@@ -68,39 +80,39 @@ class Spatial(blenderbim.core.tool.Spatial):
return True return True
@classmethod @classmethod
def disable_editing(cls, obj): def disable_editing(cls, obj: bpy.types.Object) -> None:
obj.BIMObjectSpatialProperties.is_editing = False obj.BIMObjectSpatialProperties.is_editing = False
@classmethod @classmethod
def duplicate_object_and_data(cls, obj): def duplicate_object_and_data(cls, obj: bpy.types.Object) -> bpy.types.Object:
new_obj = obj.copy() new_obj = obj.copy()
if obj.data: if obj.data:
new_obj.data = obj.data.copy() new_obj.data = obj.data.copy()
return new_obj return new_obj
@classmethod @classmethod
def enable_editing(cls, obj): def enable_editing(cls, obj: bpy.types.Object) -> None:
obj.BIMObjectSpatialProperties.is_editing = True obj.BIMObjectSpatialProperties.is_editing = True
obj.BIMObjectSpatialProperties.relating_container_object = None obj.BIMObjectSpatialProperties.relating_container_object = None
@classmethod @classmethod
def get_container(cls, element): def get_container(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
return ifcopenshell.util.element.get_container(element) return ifcopenshell.util.element.get_container(element)
@classmethod @classmethod
def get_decomposed_elements(cls, container): def get_decomposed_elements(cls, container: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
return ifcopenshell.util.element.get_decomposition(container) return ifcopenshell.util.element.get_decomposition(container)
@classmethod @classmethod
def get_object_matrix(cls, obj): def get_object_matrix(cls, obj: bpy.types.Object) -> Matrix:
return obj.matrix_world return obj.matrix_world
@classmethod @classmethod
def get_relative_object_matrix(cls, target_obj, relative_to_obj): def get_relative_object_matrix(cls, target_obj: bpy.types.Object, relative_to_obj: bpy.types.Object) -> Matrix:
return relative_to_obj.matrix_world.inverted() @ target_obj.matrix_world return relative_to_obj.matrix_world.inverted() @ target_obj.matrix_world
@classmethod @classmethod
def import_containers(cls, parent=None): def import_containers(cls, parent: Optional[ifcopenshell.entity_instance] = None) -> None:
props = bpy.context.scene.BIMSpatialProperties props = bpy.context.scene.BIMSpatialProperties
props.containers.clear() props.containers.clear()
@@ -125,30 +137,33 @@ class Spatial(blenderbim.core.tool.Spatial):
new.ifc_definition_id = element.id() new.ifc_definition_id = element.id()
@classmethod @classmethod
def run_root_copy_class(cls, obj=None): def run_root_copy_class(cls, obj: bpy.types.Object) -> ifcopenshell.entity_instance:
return blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj) return blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=obj)
@classmethod @classmethod
def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): def run_spatial_assign_container(
cls, structure_obj: bpy.types.Object, element_obj: bpy.types.Object
) -> Union[ifcopenshell.entity_instance, None]:
return blenderbim.core.spatial.assign_container( return blenderbim.core.spatial.assign_container(
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=structure_obj, element_obj=element_obj tool.Ifc, tool.Collector, tool.Spatial, structure_obj=structure_obj, element_obj=element_obj
) )
@classmethod @classmethod
def run_spatial_import_spatial_decomposition(cls): def run_spatial_import_spatial_decomposition(cls) -> None:
return blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial) return blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
@classmethod @classmethod
def select_object(cls, obj): def select_object(cls, obj: bpy.types.Object) -> None:
obj.select_set(True) tool.Blender.select_object(obj)
@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 tool.Blender.set_active_object(obj)
cls.select_object(obj)
@classmethod @classmethod
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): def set_relative_object_matrix(
cls, target_obj: bpy.types.Object, relative_to_obj: bpy.types.Object, matrix: Matrix
) -> None:
target_obj.matrix_world = relative_to_obj.matrix_world @ matrix target_obj.matrix_world = relative_to_obj.matrix_world @ matrix
@classmethod @classmethod
@@ -162,8 +177,10 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.select_set(True) obj.select_set(True)
@classmethod @classmethod
def filter_products(cls, products, action): def filter_products(
objects = [tool.Ifc.get_object(product) for product in products if tool.Ifc.get_object(product)] cls, products: list[ifcopenshell.entity_instance], action: Literal["select", "isolate", "unhide", "hide"]
) -> None:
objects = [obj for product in products if (obj := tool.Ifc.get_object(product))]
if action == "select": if action == "select":
[obj.select_set(True) for obj in objects] [obj.select_set(True) for obj in objects]
elif action == "isolate": elif action == "isolate":
@@ -179,12 +196,11 @@ class Spatial(blenderbim.core.tool.Spatial):
[obj.hide_set(True) for obj in objects if bpy.context.view_layer.objects.get(obj.name)] [obj.hide_set(True) for obj in objects if bpy.context.view_layer.objects.get(obj.name)]
@classmethod @classmethod
def deselect_objects(cls): def deselect_objects(cls) -> None:
[obj.select_set(False) for obj in bpy.context.selected_objects] [obj.select_set(False) for obj in bpy.context.selected_objects]
# bpy.ops.object.select_all(action='DESELECT')
@classmethod @classmethod
def show_scene_objects(cls): def show_scene_objects(cls) -> None:
[ [
obj.hide_set(False) obj.hide_set(False)
for obj in bpy.data.scenes["Scene"].objects for obj in bpy.data.scenes["Scene"].objects
@@ -206,12 +222,11 @@ class Spatial(blenderbim.core.tool.Spatial):
yield entity yield entity
@classmethod @classmethod
def copy_xy(cls, src_obj, destination_obj): def copy_xy(cls, src_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
z = src_obj.location[2] src_obj.location.xy = destination_obj.location.xy
src_obj.location = (destination_obj.location[0], destination_obj.location[1], z)
@classmethod @classmethod
def load_contained_elements(cls): def load_contained_elements(cls) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
props.elements.clear() props.elements.clear()
if not (container := props.active_container): if not (container := props.active_container):
@@ -245,7 +260,7 @@ class Spatial(blenderbim.core.tool.Spatial):
props.total_elements = total_elements props.total_elements = total_elements
@classmethod @classmethod
def import_spatial_decomposition(cls): def import_spatial_decomposition(cls) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
previous_container_index = props.active_container_index previous_container_index = props.active_container_index
props.containers.clear() props.containers.clear()
@@ -254,7 +269,7 @@ class Spatial(blenderbim.core.tool.Spatial):
props.active_container_index = min(previous_container_index, len(props.containers) - 1) props.active_container_index = min(previous_container_index, len(props.containers) - 1)
@classmethod @classmethod
def import_spatial_element(cls, element, level_index): def import_spatial_element(cls, element: ifcopenshell.entity_instance, level_index: int) -> None:
if not element.is_a("IfcProject") and not tool.Root.is_spatial_element(element): if not element.is_a("IfcProject") and not tool.Root.is_spatial_element(element):
return return
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
@@ -275,7 +290,7 @@ class Spatial(blenderbim.core.tool.Spatial):
cls.import_spatial_element(child, level_index + 1) cls.import_spatial_element(child, level_index + 1)
@classmethod @classmethod
def edit_container_attributes(cls, entity): def edit_container_attributes(cls, entity: ifcopenshell.entity_instance) -> None:
# TODO # TODO
obj = tool.Ifc.get_object(entity) obj = tool.Ifc.get_object(entity)
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj) blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
@@ -284,25 +299,25 @@ class Spatial(blenderbim.core.tool.Spatial):
cls.edit_container_name(entity, name) cls.edit_container_name(entity, name)
@classmethod @classmethod
def edit_container_name(cls, container, name): def edit_container_name(cls, container: ifcopenshell.entity_instance, name: str) -> None:
tool.Ifc.run("attribute.edit_attributes", product=container, attributes={"Name": name}) tool.Ifc.run("attribute.edit_attributes", product=container, attributes={"Name": name})
@classmethod @classmethod
def get_active_container(cls): def get_active_container(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
if props.active_container_index < len(props.containers): if props.active_container_index < len(props.containers):
container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id) container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id)
return container return container
@classmethod @classmethod
def contract_container(cls, container): def contract_container(cls, container: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
contracted_containers = json.loads(props.contracted_containers) contracted_containers = json.loads(props.contracted_containers)
contracted_containers.append(container.id()) contracted_containers.append(container.id())
props.contracted_containers = json.dumps(contracted_containers) props.contracted_containers = json.dumps(contracted_containers)
@classmethod @classmethod
def expand_container(cls, container): def expand_container(cls, container: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties props = bpy.context.scene.BIMSpatialDecompositionProperties
contracted_containers = json.loads(props.contracted_containers) contracted_containers = json.loads(props.contracted_containers)
contracted_containers.remove(container.id()) contracted_containers.remove(container.id())
@@ -311,14 +326,14 @@ class Spatial(blenderbim.core.tool.Spatial):
# HERE STARTS SPATIAL TOOL # HERE STARTS SPATIAL TOOL
@classmethod @classmethod
def is_bounding_class(cls, visible_element): def is_bounding_class(cls, visible_element: ifcopenshell.entity_instance) -> bool:
for ifc_class in ["IfcWall", "IfcColumn", "IfcMember", "IfcVirtualElement", "IfcPlate"]: for ifc_class in ["IfcWall", "IfcColumn", "IfcMember", "IfcVirtualElement", "IfcPlate"]:
if visible_element.is_a(ifc_class): if visible_element.is_a(ifc_class):
return True return True
return False return False
@classmethod @classmethod
def get_space_polygon_from_context_visible_objects(cls, x, y): def get_space_polygon_from_context_visible_objects(cls, x: float, y: float) -> Union[shapely.Polygon, None]:
boundary_lines = cls.get_boundary_lines_from_context_visible_objects() boundary_lines = cls.get_boundary_lines_from_context_visible_objects()
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines)) unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
closed_polygons = shapely.polygonize(unioned_boundaries.geoms) closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
@@ -329,7 +344,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return space_polygon return space_polygon
@classmethod @classmethod
def debug_shape(cls, foo): def debug_shape(cls, foo: shapely.Polygon) -> None:
coords = [(p[0], p[1], 0) for p in foo.exterior.coords] coords = [(p[0], p[1], 0) for p in foo.exterior.coords]
mesh = bpy.data.meshes.new(name="NewMesh") mesh = bpy.data.meshes.new(name="NewMesh")
bm = bmesh.new() bm = bmesh.new()
@@ -344,7 +359,7 @@ class Spatial(blenderbim.core.tool.Spatial):
bpy.context.view_layer.update() bpy.context.view_layer.update()
@classmethod @classmethod
def debug_line(cls, start, end): def debug_line(cls, start: Vector, end: Vector) -> None:
coords = [start, end] coords = [start, end]
mesh = bpy.data.meshes.new(name="NewMesh") mesh = bpy.data.meshes.new(name="NewMesh")
bm = bmesh.new() bm = bmesh.new()
@@ -359,7 +374,7 @@ class Spatial(blenderbim.core.tool.Spatial):
bpy.context.view_layer.update() bpy.context.view_layer.update()
@classmethod @classmethod
def get_boundary_lines_from_context_visible_objects(cls): def get_boundary_lines_from_context_visible_objects(cls) -> list[shapely.LineString]:
calculation_rl = bpy.context.scene.BIMModelProperties.rl3 calculation_rl = bpy.context.scene.BIMModelProperties.rl3
container = tool.Root.get_default_container() container = tool.Root.get_default_container()
container_obj = tool.Ifc.get_object(container) container_obj = tool.Ifc.get_object(container)
@@ -419,14 +434,14 @@ class Spatial(blenderbim.core.tool.Spatial):
return boundary_lines return boundary_lines
@classmethod @classmethod
def get_gross_mesh_from_element(cls, visible_element): def get_gross_mesh_from_element(cls, visible_element: ifcopenshell.entity_instance) -> bpy.types.Mesh:
gross_settings = ifcopenshell.geom.settings() gross_settings = ifcopenshell.geom.settings()
gross_settings.set("disable-opening-subtractions", True) gross_settings.set("disable-opening-subtractions", True)
new_mesh = cls.create_mesh_from_shape(ifcopenshell.geom.create_shape(gross_settings, visible_element)) new_mesh = cls.create_mesh_from_shape(ifcopenshell.geom.create_shape(gross_settings, visible_element))
return new_mesh return new_mesh
@classmethod @classmethod
def create_mesh_from_shape(cls, shape): def create_mesh_from_shape(cls, shape: ifcopenshell.geom.ShapeElementType) -> bpy.types.Mesh:
geometry = shape.geometry geometry = shape.geometry
mesh = bpy.data.meshes.new("tmp") mesh = bpy.data.meshes.new("tmp")
verts = geometry.verts verts = geometry.verts
@@ -450,7 +465,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return mesh return mesh
@classmethod @classmethod
def get_x_y_z_h_mat_from_active_obj(cls, active_obj): def get_x_y_z_h_mat_from_active_obj(cls, active_obj: bpy.types.Object) -> tuple[float, float, float, float, Matrix]:
mat = active_obj.matrix_world mat = active_obj.matrix_world
local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector()) local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector())
global_bbox_center = mat @ local_bbox_center global_bbox_center = mat @ local_bbox_center
@@ -462,7 +477,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return x, y, z, h, mat return x, y, z, h, mat
@classmethod @classmethod
def get_x_y_z_h_mat_from_cursor(cls): def get_x_y_z_h_mat_from_cursor(cls) -> tuple[float, float, float, float, Matrix]:
x, y, z = bpy.context.scene.cursor.location.xyz x, y, z = bpy.context.scene.cursor.location.xyz
if container := tool.Root.get_default_container(): if container := tool.Root.get_default_container():
if container_obj := tool.Ifc.get_object(container): if container_obj := tool.Ifc.get_object(container):
@@ -472,7 +487,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return x, y, z, h, mat return x, y, z, h, mat
@classmethod @classmethod
def get_union_shape_from_selected_objects(cls): def get_union_shape_from_selected_objects(cls) -> Polygon:
selected_objects = bpy.context.selected_objects selected_objects = bpy.context.selected_objects
boundary_elements = cls.get_boundary_elements(selected_objects) boundary_elements = cls.get_boundary_elements(selected_objects)
polys = cls.get_polygons(boundary_elements) polys = cls.get_polygons(boundary_elements)
@@ -482,7 +497,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return union return union
@classmethod @classmethod
def get_boundary_elements(cls, selected_objects): def get_boundary_elements(cls, selected_objects: list[bpy.types.Object]) -> list[ifcopenshell.entity_instance]:
boundary_elements = [] boundary_elements = []
for obj in selected_objects: for obj in selected_objects:
subelement = tool.Ifc.get_entity(obj) subelement = tool.Ifc.get_entity(obj)
@@ -491,7 +506,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return boundary_elements return boundary_elements
@classmethod @classmethod
def get_polygons(cls, boundary_elements): def get_polygons(cls, boundary_elements: list[ifcopenshell.entity_instance]) -> list[Polygon]:
polys = [] polys = []
for boundary_element in boundary_elements: for boundary_element in boundary_elements:
obj = tool.Ifc.get_object(boundary_element) obj = tool.Ifc.get_object(boundary_element)
@@ -507,7 +522,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return polys return polys
@classmethod @classmethod
def get_obj_base_points(cls, obj): def get_obj_base_points(cls, obj: bpy.types.Object) -> dict[str, tuple[float, float]]:
x_values = [(obj.matrix_world @ Vector(v)).x for v in obj.bound_box] x_values = [(obj.matrix_world @ Vector(v)).x for v in obj.bound_box]
y_values = [(obj.matrix_world @ Vector(v)).y for v in obj.bound_box] y_values = [(obj.matrix_world @ Vector(v)).y for v in obj.bound_box]
return { return {
@@ -518,7 +533,7 @@ class Spatial(blenderbim.core.tool.Spatial):
} }
@classmethod @classmethod
def get_converted_tolerance(cls, tolerance): def get_converted_tolerance(cls, tolerance: float) -> float:
model = tool.Ifc.get() model = tool.Ifc.get()
project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT") project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT")
prefix = getattr(project_unit, "Prefix", None) prefix = getattr(project_unit, "Prefix", None)
@@ -532,7 +547,7 @@ class Spatial(blenderbim.core.tool.Spatial):
) )
@classmethod @classmethod
def get_purged_inner_holes_poly(cls, union_geom, min_area): def get_purged_inner_holes_poly(cls, union_geom: Polygon, min_area: float) -> Polygon:
interiors_list = [] interiors_list = []
if union_geom.geom_type == "MultiPolygon": if union_geom.geom_type == "MultiPolygon":
@@ -552,7 +567,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return new_poly return new_poly
@classmethod @classmethod
def get_poly_valid_interior_list(cls, poly, min_area, interiors_list): def get_poly_valid_interior_list(cls, poly: Polygon, min_area: float, interiors_list: list[shapely.LinearRing]):
for interior in poly.interiors: for interior in poly.interiors:
p = Polygon(interior) p = Polygon(interior)
if p.area >= min_area: if p.area >= min_area:
@@ -560,14 +575,14 @@ class Spatial(blenderbim.core.tool.Spatial):
return interiors_list return interiors_list
@classmethod @classmethod
def get_buffered_poly_from_linear_ring(cls, linear_ring): def get_buffered_poly_from_linear_ring(cls, linear_ring: shapely.LinearRing) -> Polygon:
poly = Polygon(linear_ring) poly = Polygon(linear_ring)
converted_tolerance = cls.get_converted_tolerance(tolerance=0.03) converted_tolerance = cls.get_converted_tolerance(tolerance=0.03)
poly = poly.buffer(converted_tolerance, single_sided=True, cap_style=2, join_style=2) poly = poly.buffer(converted_tolerance, single_sided=True, cap_style=2, join_style=2)
return poly return poly
@classmethod @classmethod
def get_bmesh_from_polygon(cls, poly, h): def get_bmesh_from_polygon(cls, poly: Polygon, h: float) -> bmesh.types.BMesh:
mat = Matrix() mat = Matrix()
bm = bmesh.new() bm = bmesh.new()
bm.verts.index_update() bm.verts.index_update()
@@ -596,25 +611,25 @@ class Spatial(blenderbim.core.tool.Spatial):
return bm return bm
@classmethod @classmethod
def get_named_obj_from_bmesh(cls, name, bmesh): def get_named_obj_from_bmesh(cls, name: str, bmesh: bmesh.types.BMesh) -> bpy.types.Object:
mesh = cls.get_named_mesh_from_bmesh(name, bmesh) mesh = cls.get_named_mesh_from_bmesh(name, bmesh)
obj = cls.get_named_obj_from_mesh(name, mesh) obj = cls.get_named_obj_from_mesh(name, mesh)
return obj return obj
@classmethod @classmethod
def get_named_obj_from_mesh(cls, name, mesh): def get_named_obj_from_mesh(cls, name: str, mesh: bpy.types.Mesh) -> bpy.types.Object:
obj = bpy.data.objects.new(name, mesh) obj = bpy.data.objects.new(name, mesh)
return obj return obj
@classmethod @classmethod
def get_named_mesh_from_bmesh(cls, name, bmesh): def get_named_mesh_from_bmesh(cls, name: str, bmesh: bmesh.types.BMesh) -> bpy.types.Mesh:
mesh = bpy.data.meshes.new(name=name) mesh = bpy.data.meshes.new(name=name)
bmesh.to_mesh(mesh) bmesh.to_mesh(mesh)
bmesh.free() bmesh.free()
return mesh return mesh
@classmethod @classmethod
def get_transformed_mesh_from_local_to_global(cls, mesh): def get_transformed_mesh_from_local_to_global(cls, mesh: bpy.types.Mesh) -> bpy.types.Mesh:
active_obj = cls.get_active_obj() active_obj = cls.get_active_obj()
mat = active_obj.matrix_world mat = active_obj.matrix_world
mesh.transform(mat.inverted()) mesh.transform(mat.inverted())
@@ -622,7 +637,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return mesh return mesh
@classmethod @classmethod
def edit_active_space_obj_from_mesh(cls, mesh): def edit_active_space_obj_from_mesh(cls, mesh: bpy.types.Mesh) -> None:
active_obj = bpy.context.active_object active_obj = bpy.context.active_object
mesh.name = active_obj.data.name mesh.name = active_obj.data.name
mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id mesh.BIMMeshProperties.ifc_definition_id = active_obj.data.BIMMeshProperties.ifc_definition_id
@@ -630,7 +645,7 @@ class Spatial(blenderbim.core.tool.Spatial):
tool.Ifc.edit(active_obj) tool.Ifc.edit(active_obj)
@classmethod @classmethod
def set_obj_origin_to_bboxcenter(cls, obj): def set_obj_origin_to_bboxcenter(cls, obj: bpy.types.Object) -> None:
mat = obj.matrix_world mat = obj.matrix_world
inverted = mat.inverted() inverted = mat.inverted()
local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector()) local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector())
@@ -646,7 +661,7 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.location = newLoc obj.location = newLoc
@classmethod @classmethod
def set_obj_origin_to_bboxcenter_and_zero_elevation(cls, obj): def set_obj_origin_to_bboxcenter_and_zero_elevation(cls, obj: bpy.types.Object) -> None:
mat = obj.matrix_world mat = obj.matrix_world
inverted = mat.inverted() inverted = mat.inverted()
local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector()) local_bbox_center = 0.125 * sum((Vector(b) for b in obj.bound_box), Vector())
@@ -664,7 +679,7 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.location = newLoc obj.location = newLoc
@classmethod @classmethod
def set_obj_origin_to_cursor_position_and_zero_elevation(cls, obj): def set_obj_origin_to_cursor_position_and_zero_elevation(cls, obj: bpy.types.Object) -> None:
mat = obj.matrix_world mat = obj.matrix_world
inverted = mat.inverted() inverted = mat.inverted()
@@ -681,59 +696,57 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.location = newLoc obj.location = newLoc
@classmethod @classmethod
def get_selected_objects(cls): def get_selected_objects(cls) -> list[bpy.types.Object]:
return bpy.context.selected_objects return bpy.context.selected_objects
@classmethod @classmethod
def get_active_obj(cls): def get_active_obj(cls) -> Union[bpy.types.Object, None]:
return bpy.context.active_object return bpy.context.active_object
@classmethod @classmethod
def get_active_obj_z(cls): def get_active_obj_z(cls) -> float:
x, y, z = bpy.context.active_object.matrix_world.translation.xyz return bpy.context.active_object.matrix_world.translation.z
return z
@classmethod @classmethod
def get_active_obj_height(cls): def get_active_obj_height(cls) -> float:
height = bpy.context.active_object.dimensions.z height = bpy.context.active_object.dimensions.z
return height return height
@classmethod @classmethod
def get_relating_type_id(cls): def get_relating_type_id(cls) -> int:
props = bpy.context.scene.BIMModelProperties props = bpy.context.scene.BIMModelProperties
relating_type_id = props.relating_type_id relating_type_id = props.relating_type_id
return relating_type_id return relating_type_id
@classmethod @classmethod
def translate_obj_to_z_location(cls, obj, z): def translate_obj_to_z_location(cls, obj: bpy.types.Object, z: float) -> None:
if z != 0: if z != 0:
obj.location = obj.location + Vector((0, 0, z)) obj.location = obj.location + Vector((0, 0, z))
@classmethod @classmethod
def get_2d_vertices_from_obj(cls, obj): def get_2d_vertices_from_obj(cls, obj: bpy.types.Object) -> list[tuple]:
points = [] points = []
vectors = [v.co for v in obj.data.vertices.values()] vectors = [v.co for v in obj.data.vertices.values()]
for vector in vectors: for vector in vectors:
point = (vector[0], vector[1]) points.append(vector.xy)
points.append(point)
points.append((vectors[0][0], vectors[0][1])) points.append(vectors[0].xy)
return points return points
@classmethod @classmethod
def get_scaled_2d_vertices(cls, points): def get_scaled_2d_vertices(cls, points: list[Vector]) -> list[tuple[float, float]]:
model = tool.Ifc.get() model = tool.Ifc.get()
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(model) unit_scale = ifcopenshell.util.unit.calculate_unit_scale(model)
for i in range(len(points)): _points = []
a = list(points[i]) for p in points:
a[0] /= unit_scale _p = list(p)
a[1] /= unit_scale _p[0] /= unit_scale
points[i] = tuple(a) _p[1] /= unit_scale
_points.append(_p)
return points return _points
@classmethod @classmethod
def assign_swept_area_outer_curve_from_2d_vertices(cls, obj, vertices): def assign_swept_area_outer_curve_from_2d_vertices(cls, obj: bpy.types.Object, vertices: list[Vector]) -> None:
body = cls.get_body_representation(obj) body = cls.get_body_representation(obj)
model = tool.Ifc.get() model = tool.Ifc.get()
extrusion = tool.Model.get_extrusion(body) extrusion = tool.Model.get_extrusion(body)
@@ -747,16 +760,16 @@ class Spatial(blenderbim.core.tool.Spatial):
ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_area) ifcopenshell.util.element.remove_deep2(tool.Ifc.get(), old_area)
@classmethod @classmethod
def get_body_representation(cls, obj): def get_body_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
element = tool.Ifc.get_entity(obj) element = tool.Ifc.get_entity(obj)
return ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") return ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
@classmethod @classmethod
def assign_ifcspace_class_to_obj(cls, obj): def assign_ifcspace_class_to_obj(cls, obj: bpy.types.Object) -> None:
bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace") bpy.ops.bim.assign_class(obj=obj.name, ifc_class="IfcSpace")
@classmethod @classmethod
def assign_type_to_obj(cls, obj): def assign_type_to_obj(cls, obj: bpy.types.Object) -> None:
relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id relating_type_id = bpy.context.scene.BIMModelProperties.relating_type_id
relating_type = tool.Ifc.get().by_id(int(relating_type_id)) relating_type = tool.Ifc.get().by_id(int(relating_type_id))
ifc_class = relating_type.is_a() ifc_class = relating_type.is_a()
@@ -766,11 +779,17 @@ class Spatial(blenderbim.core.tool.Spatial):
tool.Ifc.run("type.assign_type", related_objects=[element], relating_type=relating_type) tool.Ifc.run("type.assign_type", related_objects=[element], relating_type=relating_type)
@classmethod @classmethod
def assign_relating_type_to_element(cls, ifc, Type, element, relating_type): def assign_relating_type_to_element(
blenderbim.core.type.assign_type(ifc, Type, element=element, type=relating_type) cls,
ifc: tool.Ifc,
type: tool.Type,
element: ifcopenshell.entity_instance,
relating_type: ifcopenshell.entity_instance,
) -> None:
blenderbim.core.type.assign_type(ifc, type, element=element, type=relating_type)
@classmethod @classmethod
def regen_obj_representation(cls, obj, body): def regen_obj_representation(cls, obj: bpy.types.Object, body: ifcopenshell.entity_instance) -> None:
blenderbim.core.geometry.switch_representation( blenderbim.core.geometry.switch_representation(
tool.Ifc, tool.Ifc,
tool.Geometry, tool.Geometry,
@@ -782,7 +801,7 @@ class Spatial(blenderbim.core.tool.Spatial):
) )
@classmethod @classmethod
def toggle_spaces_visibility_wired_and_textured(cls, spaces): def toggle_spaces_visibility_wired_and_textured(cls, spaces: list[ifcopenshell.entity_instance]) -> None:
first_obj = tool.Ifc.get_object(spaces[0]) first_obj = tool.Ifc.get_object(spaces[0])
if bpy.data.objects[first_obj.name].display_type == "TEXTURED": if bpy.data.objects[first_obj.name].display_type == "TEXTURED":
for space in spaces: for space in spaces:
@@ -799,7 +818,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return return
@classmethod @classmethod
def toggle_hide_spaces(cls, spaces): def toggle_hide_spaces(cls, spaces: list[ifcopenshell.entity_instance]) -> None:
first_obj = tool.Ifc.get_object(spaces[0]) first_obj = tool.Ifc.get_object(spaces[0])
if first_obj.hide_get() == False: if first_obj.hide_get() == False:
for space in spaces: for space in spaces:
@@ -813,7 +832,7 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.hide_set(False) obj.hide_set(False)
@classmethod @classmethod
def set_default_container(cls, container): def set_default_container(cls, container: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMSpatialDecompositionProperties.default_container = container.id() bpy.context.scene.BIMSpatialDecompositionProperties.default_container = container.id()
project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = project.BIMObjectProperties.collection project_collection = project.BIMObjectProperties.collection
+4
View File
@@ -109,6 +109,10 @@ class Style(blenderbim.core.tool.Style):
@classmethod @classmethod
def get_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]: def get_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
"""Get linked IFC style based on material's BIMMaterialProperties.ifc_style_id.
Return None if material is not linked to IFC or it's linked to non-existent element.
"""
if obj.BIMMaterialProperties.ifc_style_id: if obj.BIMMaterialProperties.ifc_style_id:
try: try:
return tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id) return tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)