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)
@staticmethod
def get_schema():
def get_schema() -> ifcopenshell.ifcopenshell_wrapper.schema_definition:
if IfcStore.file is None:
IfcStore.schema = ifcopenshell.ifcopenshell_wrapper.schema_by_name(
bpy.context.scene.BIMProjectProperties.export_schema
@@ -19,6 +19,7 @@
from collections import defaultdict
import bpy
import ifcopenshell.util.element
import ifcopenshell.util.schema
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc, get_class_suggestions
import blenderbim.tool as tool
from blenderbim.bim.ifc import IfcStore
+28 -19
View File
@@ -54,6 +54,7 @@ def assign_container(
) -> Union[ifcopenshell.entity_instance, None]:
if not spatial.can_contain(structure_obj, element_obj):
return
assert element_obj and structure_obj # Type checker.
rel = ifc.run(
"spatial.assign_container",
products=[ifc.get_entity(element_obj)],
@@ -64,25 +65,31 @@ def assign_container(
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.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)
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)
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)])
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)
if not element:
return
@@ -102,54 +109,56 @@ def copy_to_container(ifc, collector, spatial, obj=None, containers=None):
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)
if not element:
return
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)
if 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])
def import_spatial_decomposition(spatial):
def import_spatial_decomposition(spatial: tool.Spatial) -> None:
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.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.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.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))
spatial.import_spatial_decomposition()
def select_decomposed_elements(spatial):
def select_decomposed_elements(spatial: tool.Spatial) -> None:
container = spatial.get_active_container()
if 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():
raise NoDefaultContainer()
@@ -201,7 +210,7 @@ def generate_space(ifc, model, root, spatial, type):
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()
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)
def toggle_space_visibility(ifc, spatial):
def toggle_space_visibility(ifc: tool.Ifc, spatial: tool.Spatial) -> None:
model = ifc.get()
spaces = model.by_type("IfcSpace")
if not spaces:
@@ -229,7 +238,7 @@ def toggle_space_visibility(ifc, spatial):
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()
spaces = model.by_type("IfcSpace")
if not spaces:
@@ -237,7 +246,7 @@ def toggle_hide_spaces(ifc, spatial):
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)
+10 -5
View File
@@ -20,6 +20,7 @@ import os
import bpy
import numpy as np
import ifcopenshell.api
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
import ifcopenshell.util.element
import blenderbim.core.tool
import blenderbim.bim.handler
@@ -75,21 +76,25 @@ class Ifc(blenderbim.core.tool.Ifc):
return True
@classmethod
def schema(cls):
def schema(cls) -> ifcopenshell_wrapper.schema_definition:
return IfcStore.get_schema()
@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()
props = getattr(obj, "BIMObjectProperties", None)
if ifc and props and props.ifc_definition_id:
try:
return IfcStore.get_file().by_id(props.ifc_definition_id)
except:
return ifc.by_id(props.ifc_definition_id)
except RuntimeError:
pass
@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"""
ifc_file = tool.Ifc.get()
try:
+111 -92
View File
@@ -16,11 +16,19 @@
# 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/>.
from __future__ import annotations
import bpy
import bmesh
import shapely
import shapely.ops
import ifcopenshell
import ifcopenshell.geom
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.tool
import blenderbim.core.root
@@ -31,12 +39,14 @@ import json
from math import pi
from mathutils import Vector, Matrix
from shapely import Polygon
from typing import Generator, Optional
from typing import Generator, Optional, Union, Literal, Any
class Spatial(blenderbim.core.tool.Spatial):
@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)
element = tool.Ifc.get_entity(element_obj)
if not structure or not element:
@@ -54,7 +64,9 @@ class Spatial(blenderbim.core.tool.Spatial):
return True
@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:
return False
if tool.Ifc.get_schema() == "IFC2X3":
@@ -68,39 +80,39 @@ class Spatial(blenderbim.core.tool.Spatial):
return True
@classmethod
def disable_editing(cls, obj):
def disable_editing(cls, obj: bpy.types.Object) -> None:
obj.BIMObjectSpatialProperties.is_editing = False
@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()
if obj.data:
new_obj.data = obj.data.copy()
return new_obj
@classmethod
def enable_editing(cls, obj):
def enable_editing(cls, obj: bpy.types.Object) -> None:
obj.BIMObjectSpatialProperties.is_editing = True
obj.BIMObjectSpatialProperties.relating_container_object = None
@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)
@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)
@classmethod
def get_object_matrix(cls, obj):
def get_object_matrix(cls, obj: bpy.types.Object) -> Matrix:
return obj.matrix_world
@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
@classmethod
def import_containers(cls, parent=None):
def import_containers(cls, parent: Optional[ifcopenshell.entity_instance] = None) -> None:
props = bpy.context.scene.BIMSpatialProperties
props.containers.clear()
@@ -125,30 +137,33 @@ class Spatial(blenderbim.core.tool.Spatial):
new.ifc_definition_id = element.id()
@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)
@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(
tool.Ifc, tool.Collector, tool.Spatial, structure_obj=structure_obj, element_obj=element_obj
)
@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)
@classmethod
def select_object(cls, obj):
obj.select_set(True)
def select_object(cls, obj: bpy.types.Object) -> None:
tool.Blender.select_object(obj)
@classmethod
def set_active_object(cls, obj):
bpy.context.view_layer.objects.active = obj
cls.select_object(obj)
def set_active_object(cls, obj: bpy.types.Object) -> None:
tool.Blender.set_active_object(obj)
@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
@classmethod
@@ -162,8 +177,10 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.select_set(True)
@classmethod
def filter_products(cls, products, action):
objects = [tool.Ifc.get_object(product) for product in products if tool.Ifc.get_object(product)]
def filter_products(
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":
[obj.select_set(True) for obj in objects]
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)]
@classmethod
def deselect_objects(cls):
def deselect_objects(cls) -> None:
[obj.select_set(False) for obj in bpy.context.selected_objects]
# bpy.ops.object.select_all(action='DESELECT')
@classmethod
def show_scene_objects(cls):
def show_scene_objects(cls) -> None:
[
obj.hide_set(False)
for obj in bpy.data.scenes["Scene"].objects
@@ -206,12 +222,11 @@ class Spatial(blenderbim.core.tool.Spatial):
yield entity
@classmethod
def copy_xy(cls, src_obj, destination_obj):
z = src_obj.location[2]
src_obj.location = (destination_obj.location[0], destination_obj.location[1], z)
def copy_xy(cls, src_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
src_obj.location.xy = destination_obj.location.xy
@classmethod
def load_contained_elements(cls):
def load_contained_elements(cls) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties
props.elements.clear()
if not (container := props.active_container):
@@ -245,7 +260,7 @@ class Spatial(blenderbim.core.tool.Spatial):
props.total_elements = total_elements
@classmethod
def import_spatial_decomposition(cls):
def import_spatial_decomposition(cls) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties
previous_container_index = props.active_container_index
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)
@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):
return
props = bpy.context.scene.BIMSpatialDecompositionProperties
@@ -275,7 +290,7 @@ class Spatial(blenderbim.core.tool.Spatial):
cls.import_spatial_element(child, level_index + 1)
@classmethod
def edit_container_attributes(cls, entity):
def edit_container_attributes(cls, entity: ifcopenshell.entity_instance) -> None:
# TODO
obj = tool.Ifc.get_object(entity)
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)
@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})
@classmethod
def get_active_container(cls):
def get_active_container(cls) -> Union[ifcopenshell.entity_instance, None]:
props = bpy.context.scene.BIMSpatialDecompositionProperties
if props.active_container_index < len(props.containers):
container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id)
return container
@classmethod
def contract_container(cls, container):
def contract_container(cls, container: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties
contracted_containers = json.loads(props.contracted_containers)
contracted_containers.append(container.id())
props.contracted_containers = json.dumps(contracted_containers)
@classmethod
def expand_container(cls, container):
def expand_container(cls, container: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMSpatialDecompositionProperties
contracted_containers = json.loads(props.contracted_containers)
contracted_containers.remove(container.id())
@@ -311,14 +326,14 @@ class Spatial(blenderbim.core.tool.Spatial):
# HERE STARTS SPATIAL TOOL
@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"]:
if visible_element.is_a(ifc_class):
return True
return False
@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()
unioned_boundaries = shapely.union_all(shapely.GeometryCollection(boundary_lines))
closed_polygons = shapely.polygonize(unioned_boundaries.geoms)
@@ -329,7 +344,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return space_polygon
@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]
mesh = bpy.data.meshes.new(name="NewMesh")
bm = bmesh.new()
@@ -344,7 +359,7 @@ class Spatial(blenderbim.core.tool.Spatial):
bpy.context.view_layer.update()
@classmethod
def debug_line(cls, start, end):
def debug_line(cls, start: Vector, end: Vector) -> None:
coords = [start, end]
mesh = bpy.data.meshes.new(name="NewMesh")
bm = bmesh.new()
@@ -359,7 +374,7 @@ class Spatial(blenderbim.core.tool.Spatial):
bpy.context.view_layer.update()
@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
container = tool.Root.get_default_container()
container_obj = tool.Ifc.get_object(container)
@@ -419,14 +434,14 @@ class Spatial(blenderbim.core.tool.Spatial):
return boundary_lines
@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.set("disable-opening-subtractions", True)
new_mesh = cls.create_mesh_from_shape(ifcopenshell.geom.create_shape(gross_settings, visible_element))
return new_mesh
@classmethod
def create_mesh_from_shape(cls, shape):
def create_mesh_from_shape(cls, shape: ifcopenshell.geom.ShapeElementType) -> bpy.types.Mesh:
geometry = shape.geometry
mesh = bpy.data.meshes.new("tmp")
verts = geometry.verts
@@ -450,7 +465,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return mesh
@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
local_bbox_center = 0.125 * sum((Vector(b) for b in active_obj.bound_box), Vector())
global_bbox_center = mat @ local_bbox_center
@@ -462,7 +477,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return x, y, z, h, mat
@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
if container := tool.Root.get_default_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
@classmethod
def get_union_shape_from_selected_objects(cls):
def get_union_shape_from_selected_objects(cls) -> Polygon:
selected_objects = bpy.context.selected_objects
boundary_elements = cls.get_boundary_elements(selected_objects)
polys = cls.get_polygons(boundary_elements)
@@ -482,7 +497,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return union
@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 = []
for obj in selected_objects:
subelement = tool.Ifc.get_entity(obj)
@@ -491,7 +506,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return boundary_elements
@classmethod
def get_polygons(cls, boundary_elements):
def get_polygons(cls, boundary_elements: list[ifcopenshell.entity_instance]) -> list[Polygon]:
polys = []
for boundary_element in boundary_elements:
obj = tool.Ifc.get_object(boundary_element)
@@ -507,7 +522,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return polys
@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]
y_values = [(obj.matrix_world @ Vector(v)).y for v in obj.bound_box]
return {
@@ -518,7 +533,7 @@ class Spatial(blenderbim.core.tool.Spatial):
}
@classmethod
def get_converted_tolerance(cls, tolerance):
def get_converted_tolerance(cls, tolerance: float) -> float:
model = tool.Ifc.get()
project_unit = ifcopenshell.util.unit.get_project_unit(model, "LENGTHUNIT")
prefix = getattr(project_unit, "Prefix", None)
@@ -532,7 +547,7 @@ class Spatial(blenderbim.core.tool.Spatial):
)
@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 = []
if union_geom.geom_type == "MultiPolygon":
@@ -552,7 +567,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return new_poly
@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:
p = Polygon(interior)
if p.area >= min_area:
@@ -560,14 +575,14 @@ class Spatial(blenderbim.core.tool.Spatial):
return interiors_list
@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)
converted_tolerance = cls.get_converted_tolerance(tolerance=0.03)
poly = poly.buffer(converted_tolerance, single_sided=True, cap_style=2, join_style=2)
return poly
@classmethod
def get_bmesh_from_polygon(cls, poly, h):
def get_bmesh_from_polygon(cls, poly: Polygon, h: float) -> bmesh.types.BMesh:
mat = Matrix()
bm = bmesh.new()
bm.verts.index_update()
@@ -596,25 +611,25 @@ class Spatial(blenderbim.core.tool.Spatial):
return bm
@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)
obj = cls.get_named_obj_from_mesh(name, mesh)
return obj
@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)
return obj
@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)
bmesh.to_mesh(mesh)
bmesh.free()
return mesh
@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()
mat = active_obj.matrix_world
mesh.transform(mat.inverted())
@@ -622,7 +637,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return mesh
@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
mesh.name = active_obj.data.name
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)
@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
inverted = mat.inverted()
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
@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
inverted = mat.inverted()
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
@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
inverted = mat.inverted()
@@ -681,59 +696,57 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.location = newLoc
@classmethod
def get_selected_objects(cls):
def get_selected_objects(cls) -> list[bpy.types.Object]:
return bpy.context.selected_objects
@classmethod
def get_active_obj(cls):
def get_active_obj(cls) -> Union[bpy.types.Object, None]:
return bpy.context.active_object
@classmethod
def get_active_obj_z(cls):
x, y, z = bpy.context.active_object.matrix_world.translation.xyz
return z
def get_active_obj_z(cls) -> float:
return bpy.context.active_object.matrix_world.translation.z
@classmethod
def get_active_obj_height(cls):
def get_active_obj_height(cls) -> float:
height = bpy.context.active_object.dimensions.z
return height
@classmethod
def get_relating_type_id(cls):
def get_relating_type_id(cls) -> int:
props = bpy.context.scene.BIMModelProperties
relating_type_id = props.relating_type_id
return relating_type_id
@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:
obj.location = obj.location + Vector((0, 0, z))
@classmethod
def get_2d_vertices_from_obj(cls, obj):
def get_2d_vertices_from_obj(cls, obj: bpy.types.Object) -> list[tuple]:
points = []
vectors = [v.co for v in obj.data.vertices.values()]
for vector in vectors:
point = (vector[0], vector[1])
points.append(point)
points.append(vector.xy)
points.append((vectors[0][0], vectors[0][1]))
points.append(vectors[0].xy)
return points
@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()
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(model)
for i in range(len(points)):
a = list(points[i])
a[0] /= unit_scale
a[1] /= unit_scale
points[i] = tuple(a)
return points
_points = []
for p in points:
_p = list(p)
_p[0] /= unit_scale
_p[1] /= unit_scale
_points.append(_p)
return _points
@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)
model = tool.Ifc.get()
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)
@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)
return ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
@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")
@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 = tool.Ifc.get().by_id(int(relating_type_id))
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)
@classmethod
def assign_relating_type_to_element(cls, ifc, Type, element, relating_type):
blenderbim.core.type.assign_type(ifc, Type, element=element, type=relating_type)
def assign_relating_type_to_element(
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
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(
tool.Ifc,
tool.Geometry,
@@ -782,7 +801,7 @@ class Spatial(blenderbim.core.tool.Spatial):
)
@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])
if bpy.data.objects[first_obj.name].display_type == "TEXTURED":
for space in spaces:
@@ -799,7 +818,7 @@ class Spatial(blenderbim.core.tool.Spatial):
return
@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])
if first_obj.hide_get() == False:
for space in spaces:
@@ -813,7 +832,7 @@ class Spatial(blenderbim.core.tool.Spatial):
obj.hide_set(False)
@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()
project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = project.BIMObjectProperties.collection
+4
View File
@@ -109,6 +109,10 @@ class Style(blenderbim.core.tool.Style):
@classmethod
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:
try:
return tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)