mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
typing
This commit is contained in:
@@ -23,11 +23,12 @@ import bmesh
|
||||
import bonsai.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
from mathutils import Vector, Matrix
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Annotator:
|
||||
@staticmethod
|
||||
def add_text(related_element=None):
|
||||
def add_text(related_element: Optional[ifcopenshell.entity_instance] = None) -> bpy.types.Object:
|
||||
curve = bpy.data.curves.new(type="FONT", name="Text")
|
||||
curve.body = "TEXT"
|
||||
obj = bpy.data.objects.new("Text", curve)
|
||||
@@ -53,7 +54,7 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def resize_text(text_obj):
|
||||
def resize_text(text_obj: bpy.types.Object) -> None:
|
||||
camera = None
|
||||
group = tool.Drawing.get_drawing_group(tool.Ifc.get_entity(text_obj))
|
||||
for element in tool.Drawing.get_drawing_elements(group):
|
||||
@@ -71,7 +72,9 @@ class Annotator:
|
||||
text_obj.data.size = font_size
|
||||
|
||||
@staticmethod
|
||||
def add_line_to_annotation(obj, co1=None, co2=None):
|
||||
def add_line_to_annotation(
|
||||
obj: bpy.types.Object, co1: Optional[Vector] = None, co2: Optional[Vector] = None
|
||||
) -> bpy.types.Object:
|
||||
if co1 is None:
|
||||
co1, co2, _, _ = Annotator.get_placeholder_coords()
|
||||
co1 = obj.matrix_world.inverted() @ co1
|
||||
@@ -93,7 +96,7 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_vertex_to_annotation(obj):
|
||||
def add_vertex_to_annotation(obj: bpy.types.Object) -> bpy.types.Object:
|
||||
verts_world_space = Annotator.get_placeholder_coords()
|
||||
vert_local = obj.matrix_world.inverted() @ verts_world_space[0]
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data, clean=True)
|
||||
@@ -102,7 +105,7 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def add_plane_to_annotation(obj, remove_face=False):
|
||||
def add_plane_to_annotation(obj: bpy.types.Object, remove_face: bool = False) -> bpy.types.Object:
|
||||
# default order = bot left, top left, bot right, top right
|
||||
# therefore we redefine the order
|
||||
face_verts = [0, 2, 3, 1]
|
||||
@@ -119,7 +122,7 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def get_annotation_obj(drawing, object_type, data_type):
|
||||
def get_annotation_obj(drawing: ifcopenshell.entity_instance, object_type: str, data_type: str) -> bpy.types.Object:
|
||||
camera = tool.Ifc.get_object(drawing)
|
||||
co1, _, _, _ = Annotator.get_placeholder_coords(camera)
|
||||
matrix_world = tool.Drawing.get_camera_matrix(camera)
|
||||
@@ -162,7 +165,7 @@ class Annotator:
|
||||
return obj
|
||||
|
||||
@staticmethod
|
||||
def get_placeholder_coords(camera=None):
|
||||
def get_placeholder_coords(camera: Optional[bpy.types.Object] = None) -> tuple[Vector, Vector, Vector, Vector]:
|
||||
if not camera:
|
||||
camera = bpy.context.scene.camera
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ import bonsai.core.root
|
||||
from bonsai.bim.module.model.window import create_bm_window, create_bm_box
|
||||
|
||||
from mathutils import Vector, Matrix
|
||||
from typing import Union, Any, Optional
|
||||
|
||||
import json
|
||||
import collections
|
||||
@@ -169,7 +170,9 @@ def update_door_modifier_representation(context: bpy.types.Context) -> None:
|
||||
|
||||
|
||||
# TODO: move it out to tools
|
||||
def bm_sort_out_geom(geom_data):
|
||||
def bm_sort_out_geom(
|
||||
geom_data: list[Union[bmesh.types.BMVert, bmesh.types.BMEdge, bmesh.types.BMFace]]
|
||||
) -> dict[str, Any]:
|
||||
geom_dict = {"verts": [], "edges": [], "faces": []}
|
||||
|
||||
for el in geom_data:
|
||||
@@ -183,8 +186,12 @@ def bm_sort_out_geom(geom_data):
|
||||
|
||||
|
||||
def bm_mirror(
|
||||
bm, verts, mirror_axes: Vector = V(1, 0, 0).freeze(), mirror_point: Vector = V(0, 0, 0).freeze(), create_copy=False
|
||||
):
|
||||
bm: bmesh.types.BMesh,
|
||||
verts: list[bmesh.types.BMVert],
|
||||
mirror_axes: Vector = V(1, 0, 0).freeze(),
|
||||
mirror_point: Vector = V(0, 0, 0).freeze(),
|
||||
create_copy: bool = False,
|
||||
) -> list[bmesh.types.BMVert]:
|
||||
matrix = Matrix.Translation(mirror_point)
|
||||
for i, v in enumerate(mirror_axes):
|
||||
if not v:
|
||||
@@ -208,14 +215,14 @@ def bm_mirror(
|
||||
|
||||
|
||||
def create_bm_extruded_profile(
|
||||
bm,
|
||||
points,
|
||||
edges=None,
|
||||
faces=None,
|
||||
bm: bmesh.types.BMesh,
|
||||
points: list[Vector],
|
||||
edges: Optional[list[tuple[int, int]]] = None,
|
||||
faces: Optional[list[list[int]]] = None,
|
||||
position: Vector = V(0, 0, 0).freeze(),
|
||||
magnitude=1.0,
|
||||
magnitude: float = 1.0,
|
||||
extrusion_vector: Vector = V(0, 0, 1).freeze(),
|
||||
):
|
||||
) -> list[bmesh.types.BMVert]:
|
||||
bm.verts.index_update()
|
||||
bm.edges.index_update()
|
||||
bm.faces.ensure_lookup_table()
|
||||
@@ -242,7 +249,9 @@ def create_bm_extruded_profile(
|
||||
return new_verts + extruded_verts
|
||||
|
||||
|
||||
def create_bm_door_lining(bm, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()):
|
||||
def create_bm_door_lining(
|
||||
bm: bmesh.types.BMesh, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
||||
) -> list[bmesh.types.BMVert]:
|
||||
"""`thickness` of the profile is defined as list in the following order: `(SIDE, TOP)`
|
||||
|
||||
`thickness` can be also defined just as 1 float value.
|
||||
@@ -303,7 +312,7 @@ def create_bm_door_lining(bm, size: Vector, thickness: list, position: Vector =
|
||||
return new_verts + translate_verts
|
||||
|
||||
|
||||
def update_door_modifier_bmesh(context):
|
||||
def update_door_modifier_bmesh(context: bpy.types.Context) -> None:
|
||||
obj = context.active_object
|
||||
props = obj.BIMDoorProperties
|
||||
|
||||
@@ -400,7 +409,9 @@ def update_door_modifier_bmesh(context):
|
||||
inner_casing_verts = create_bm_door_lining(bm, casing_size, inner_casing_thickness, inner_casing_position)
|
||||
casing_verts.extend(inner_casing_verts)
|
||||
|
||||
def create_bm_door_panel(panel_size, panel_position, door_swing_type):
|
||||
def create_bm_door_panel(
|
||||
panel_size: Vector, panel_position: Vector, door_swing_type: str
|
||||
) -> list[bmesh.types.BMVert]:
|
||||
door_verts = []
|
||||
# add door panel
|
||||
door_verts.extend(create_bm_box(bm, panel_size, panel_position))
|
||||
|
||||
@@ -33,13 +33,14 @@ from bonsai.bim.module.model.decorator import ProfileDecorator
|
||||
|
||||
from mathutils import Vector
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
# reference:
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRailing.htm
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRailingType.htm
|
||||
|
||||
|
||||
def bm_split_edge_at_offset(edge, offset):
|
||||
def bm_split_edge_at_offset(edge: bmesh.types.BMEdge, offset: float) -> dict[str, Any]:
|
||||
v0, v1 = edge.verts
|
||||
|
||||
offset = offset / 2
|
||||
@@ -51,7 +52,7 @@ def bm_split_edge_at_offset(edge, offset):
|
||||
return new_geometry
|
||||
|
||||
|
||||
def update_railing_modifier_ifc_data(context):
|
||||
def update_railing_modifier_ifc_data(context: bpy.types.Context) -> None:
|
||||
"""should be called after new geometry settled
|
||||
since it's going to update ifc representation
|
||||
"""
|
||||
@@ -113,7 +114,7 @@ def update_railing_modifier_ifc_data(context):
|
||||
tool.Ifc.edit(obj)
|
||||
|
||||
|
||||
def update_bbim_railing_pset(element, railing_data):
|
||||
def update_bbim_railing_pset(element: ifcopenshell.entity_instance, railing_data: dict[str, Any]) -> None:
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Railing")
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Railing")
|
||||
@@ -121,7 +122,7 @@ def update_bbim_railing_pset(element, railing_data):
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": railing_data})
|
||||
|
||||
|
||||
def update_railing_modifier_bmesh(context):
|
||||
def update_railing_modifier_bmesh(context: bpy.types.Context) -> None:
|
||||
"""before using should make sure that Data contains up-to-date information.
|
||||
If BBIM Pset just changed should call refresh() before updating bmesh
|
||||
"""
|
||||
@@ -153,7 +154,7 @@ def update_railing_modifier_bmesh(context):
|
||||
if props.railing_type != "FRAMELESS_PANEL":
|
||||
return
|
||||
|
||||
def generate_frameless_panel_railing():
|
||||
def generate_frameless_panel_railing() -> None:
|
||||
# generating FRAMELESS_PANEL railing
|
||||
height = props.height
|
||||
thickness = props.thickness
|
||||
@@ -214,7 +215,7 @@ def update_railing_modifier_bmesh(context):
|
||||
generate_frameless_panel_railing()
|
||||
|
||||
|
||||
def get_path_data(obj):
|
||||
def get_path_data(obj: bpy.types.Object) -> dict[str, Any]:
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
bm = tool.Blender.get_bmesh_for_mesh(obj.data)
|
||||
@@ -468,7 +469,7 @@ class EnableEditingRailingPath(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def cancel_editing_railing_path(context):
|
||||
def cancel_editing_railing_path(context: bpy.types.Context) -> set[str]:
|
||||
obj = context.active_object
|
||||
props = obj.BIMRailingProperties
|
||||
|
||||
|
||||
@@ -37,13 +37,14 @@ import mathutils.geometry
|
||||
from bpypolyskel import bpypolyskel
|
||||
import shapely
|
||||
from pprint import pprint
|
||||
from typing import Literal, Union, Any
|
||||
|
||||
# reference:
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoof.htm
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcRoofType.htm
|
||||
|
||||
|
||||
def bm_mesh_clean_up(bm):
|
||||
def bm_mesh_clean_up(bm: bmesh.types.BMesh) -> None:
|
||||
# remove internal edges and faces
|
||||
# adding missing faces so we could rely on `e.is_boundary` later
|
||||
bmesh.ops.contextual_create(bm, geom=bm.edges[:])
|
||||
@@ -94,7 +95,7 @@ class GenerateHippedRoof(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def is_valid_roof_footprint(bm):
|
||||
def is_valid_roof_footprint(bm: bmesh.types.BMesh) -> tuple[set[str], str]:
|
||||
# should be bmesh to support edit mode
|
||||
bm.verts.ensure_lookup_table()
|
||||
base_z = bm.verts[0].co.z
|
||||
@@ -108,8 +109,14 @@ def is_valid_roof_footprint(bm):
|
||||
|
||||
|
||||
def generate_hiped_roof_bmesh(
|
||||
bm, mode="ANGLE", height=1.0, roof_thickness=0.1, angle=pi / 18, rafter_edge_angle=pi / 2, mutate_current_bmesh=True
|
||||
):
|
||||
bm: bmesh.types.BMesh,
|
||||
mode: Literal["ANGLE", "HEIGHT"] = "ANGLE",
|
||||
height: float = 1.0,
|
||||
roof_thickness: float = 0.1,
|
||||
angle: float = pi / 18,
|
||||
rafter_edge_angle: float = pi / 2,
|
||||
mutate_current_bmesh: float = True,
|
||||
) -> bmesh.types.BMesh:
|
||||
"""return bmesh with gable roof geometry
|
||||
|
||||
`mutate_current_bmesh` is a flag to indicate whether the input bmesh
|
||||
@@ -199,16 +206,16 @@ def generate_hiped_roof_bmesh(
|
||||
new_edges = [bm.edges.new([new_verts[vi] for vi in edge]) for edge in edges]
|
||||
new_faces = [bm.faces.new([new_verts[vi] for vi in face]) for face in faces]
|
||||
|
||||
def find_identical_new_vert(co):
|
||||
def find_identical_new_vert(co: Vector) -> Union[bmesh.types.BMVert, None]:
|
||||
for v in bm.verts:
|
||||
if tool.Cad.is_x((co - v.co).length, 0):
|
||||
return v
|
||||
|
||||
def find_other_polygon_verts(edge):
|
||||
def find_other_polygon_verts(edge: bmesh.types.BMEdge) -> list[bmesh.types.BMVert]:
|
||||
polygon = edge.link_faces[0]
|
||||
return [v for v in polygon.verts if v not in edge.verts]
|
||||
|
||||
def change_angle(projected_vert_co, edge_verts, new_angle):
|
||||
def change_angle(projected_vert_co: Vector, edge_verts: list[bmesh.types.BMVert], new_angle: float) -> Vector:
|
||||
A, B = [v.co for v in edge_verts]
|
||||
P = projected_vert_co
|
||||
|
||||
@@ -232,10 +239,10 @@ def generate_hiped_roof_bmesh(
|
||||
verts_to_rip = []
|
||||
bottom_chords_to_remove = []
|
||||
|
||||
def is_footprint_vert(v):
|
||||
def is_footprint_vert(v: bmesh.types.BMVert) -> bool:
|
||||
return tool.Cad.is_x(v.co.z - footprint_z, 0)
|
||||
|
||||
def is_footprint_edge(edge):
|
||||
def is_footprint_edge(edge: bmesh.types.BMEdge) -> bool:
|
||||
return all(is_footprint_vert(v) for v in edge.verts)
|
||||
|
||||
# find footprint edges
|
||||
@@ -279,7 +286,9 @@ def generate_hiped_roof_bmesh(
|
||||
if defined_angle >= pi / 2:
|
||||
bottom_chords_to_remove.append(identical_edge)
|
||||
|
||||
def separate_vert(bm, vert, edge, new_co):
|
||||
def separate_vert(
|
||||
bm: bmesh.types.BMesh, vert: bmesh.types.BMVert, edge: bmesh.types.BMEdge, new_co: Vector
|
||||
) -> bmesh.types.BMVert:
|
||||
face = next(f for f in vert.link_faces if edge in f.edges)
|
||||
new_v = bmesh.utils.face_vert_separate(face, vert)
|
||||
new_v.co = new_co
|
||||
@@ -378,11 +387,11 @@ def generate_hiped_roof_bmesh(
|
||||
return bm
|
||||
|
||||
|
||||
def bm_get_indices(sequence):
|
||||
def bm_get_indices(sequence) -> list[int]:
|
||||
return [i.index for i in sequence]
|
||||
|
||||
|
||||
def update_roof_modifier_ifc_data(context):
|
||||
def update_roof_modifier_ifc_data(context: bpy.types.Context) -> None:
|
||||
"""should be called after new geometry settled
|
||||
since it's going to update ifc representation
|
||||
"""
|
||||
@@ -390,7 +399,7 @@ def update_roof_modifier_ifc_data(context):
|
||||
props = obj.BIMRoofProperties
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
|
||||
def roof_is_gabled():
|
||||
def roof_is_gabled() -> bool:
|
||||
pset_data = tool.Model.get_modeling_bbim_pset_data(obj, "BBIM_Roof")
|
||||
path_data = pset_data["data_dict"]["path_data"]
|
||||
angle_layer = path_data.get("gable_roof_angles", None)
|
||||
@@ -411,7 +420,7 @@ def update_roof_modifier_ifc_data(context):
|
||||
tool.Ifc.edit(obj)
|
||||
|
||||
|
||||
def update_bbim_roof_pset(element, roof_data):
|
||||
def update_bbim_roof_pset(element: ifcopenshell.entity_instance, roof_data: dict[str, Any]) -> None:
|
||||
pset = tool.Pset.get_element_pset(element, "BBIM_Roof")
|
||||
if not pset:
|
||||
pset = ifcopenshell.api.run("pset.add_pset", tool.Ifc.get(), product=element, name="BBIM_Roof")
|
||||
@@ -419,7 +428,7 @@ def update_bbim_roof_pset(element, roof_data):
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": roof_data})
|
||||
|
||||
|
||||
def update_roof_modifier_bmesh(context):
|
||||
def update_roof_modifier_bmesh(context: bpy.types.Context) -> None:
|
||||
"""before using should make sure that Data contains up-to-date information.
|
||||
If BBIM Pset just changed should call refresh() before updating bmesh
|
||||
"""
|
||||
@@ -466,7 +475,7 @@ def update_roof_modifier_bmesh(context):
|
||||
tool.Blender.apply_bmesh(obj.data, bm)
|
||||
|
||||
|
||||
def get_path_data(obj):
|
||||
def get_path_data(obj: bpy.types.Object) -> Union[dict[str, Any], None]:
|
||||
"""get path data for current mesh, path data is cleaned up"""
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
@@ -699,7 +708,7 @@ class EnableEditingRoofPath(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
def cancel_editing_roof_path(context):
|
||||
def cancel_editing_roof_path(context: bpy.types.Context) -> set[str]:
|
||||
obj = context.active_object
|
||||
props = obj.BIMRoofProperties
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ from bpy.props import FloatProperty, IntProperty
|
||||
from bpy_extras.object_utils import AddObjectHelper, object_data_add
|
||||
|
||||
|
||||
def regenerate_stair_mesh(context):
|
||||
def regenerate_stair_mesh(context: bpy.types.Context) -> None:
|
||||
obj = context.active_object
|
||||
props_kwargs = obj.BIMStairProperties.get_props_kwargs()
|
||||
vertices, edges, faces = tool.Model.generate_stair_2d_profile(**props_kwargs)
|
||||
@@ -65,7 +65,7 @@ def regenerate_stair_mesh(context):
|
||||
obj.data.update()
|
||||
|
||||
|
||||
def update_ifc_stair_props(obj):
|
||||
def update_ifc_stair_props(obj: bpy.types.Object) -> None:
|
||||
"""should be called after new geometry settled
|
||||
since it's going to update ifc representation
|
||||
"""
|
||||
@@ -114,7 +114,7 @@ def update_ifc_stair_props(obj):
|
||||
tool.Ifc.edit(obj)
|
||||
|
||||
# update related annotation objects
|
||||
def get_elements_from_product(product):
|
||||
def get_elements_from_product(product: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
elements = []
|
||||
for rel in product.ReferencedBy:
|
||||
if not rel.is_a("IfcRelAssignsToProduct"):
|
||||
|
||||
@@ -34,9 +34,10 @@ import ifcopenshell.util.unit
|
||||
from ifcopenshell.util.shape_builder import V
|
||||
from bmesh.types import BMVert
|
||||
from mathutils import Vector
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def update_window_modifier_representation(context):
|
||||
def update_window_modifier_representation(context: bpy.types.Context) -> None:
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
props = obj.BIMWindowProperties
|
||||
@@ -129,7 +130,9 @@ def update_window_modifier_representation(context):
|
||||
tool.Model.update_simple_openings(element)
|
||||
|
||||
|
||||
def create_bm_window_frame(bm, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()):
|
||||
def create_bm_window_frame(
|
||||
bm: bmesh.types.BMesh, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()
|
||||
) -> list[bmesh.types.BMVert]:
|
||||
"""`thickness` of the profile is defined as list in the following order:
|
||||
`(LEFT, TOP, RIGHT, BOTTOM)`
|
||||
|
||||
@@ -194,7 +197,9 @@ def create_bm_window_frame(bm, size: Vector, thickness: list, position: Vector =
|
||||
return new_verts + translate_verts
|
||||
|
||||
|
||||
def create_bm_box(bm, size: Vector = V(1, 1, 1).freeze(), position: Vector = V(0, 0, 0).freeze()):
|
||||
def create_bm_box(
|
||||
bm: bmesh.types.BMesh, size: Vector = V(1, 1, 1).freeze(), position: Vector = V(0, 0, 0).freeze()
|
||||
) -> list[bmesh.types.BMVert]:
|
||||
"""create a box of `size`, position box first vertex at `position`"""
|
||||
box_verts = bmesh.ops.create_cube(bm, size=1)["verts"]
|
||||
bmesh.ops.translate(bm, vec=-box_verts[0].co, verts=box_verts)
|
||||
@@ -204,17 +209,17 @@ def create_bm_box(bm, size: Vector = V(1, 1, 1).freeze(), position: Vector = V(0
|
||||
|
||||
|
||||
def create_bm_window(
|
||||
bm,
|
||||
bm: bmesh.types.BMesh,
|
||||
lining_size: Vector,
|
||||
lining_thickness: list,
|
||||
lining_to_panel_offset_x,
|
||||
lining_to_panel_offset_y_full,
|
||||
frame_size: Vector,
|
||||
frame_thickness,
|
||||
glass_thickness,
|
||||
frame_thickness: float,
|
||||
glass_thickness: float,
|
||||
position: Vector,
|
||||
x_offsets: list = None,
|
||||
):
|
||||
x_offsets: Optional[list] = None,
|
||||
) -> tuple[list[bmesh.types.BMVert], list[bmesh.types.BMVert], list[bmesh.types.BMVert]]:
|
||||
"""`lining_thickness` and `x_offsets` are expected to be defined as a list,
|
||||
similarly to `create_bm_window_frame` `thickness` argument"""
|
||||
|
||||
@@ -241,7 +246,7 @@ def create_bm_window(
|
||||
return (window_lining_verts, frame_verts, glass_verts)
|
||||
|
||||
|
||||
def update_window_modifier_bmesh(context):
|
||||
def update_window_modifier_bmesh(context: bpy.types.Context) -> None:
|
||||
obj = context.active_object
|
||||
props = obj.BIMWindowProperties
|
||||
panel_schema = DEFAULT_PANEL_SCHEMAS[props.window_type]
|
||||
|
||||
@@ -35,32 +35,32 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
def get_export_schema(self, context):
|
||||
def get_export_schema(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
if not ProjectData.is_loaded:
|
||||
ProjectData.load()
|
||||
return ProjectData.data["export_schema"]
|
||||
|
||||
|
||||
def get_template_file(self, context):
|
||||
def get_template_file(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
if not ProjectData.is_loaded:
|
||||
ProjectData.load()
|
||||
return ProjectData.data["template_file"]
|
||||
|
||||
|
||||
def get_library_file(self, context):
|
||||
def get_library_file(self: "BIMProjectProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
if not ProjectData.is_loaded:
|
||||
ProjectData.load()
|
||||
return ProjectData.data["library_file"]
|
||||
|
||||
|
||||
def update_library_file(self, context):
|
||||
def update_library_file(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
|
||||
if self.library_file != "0":
|
||||
bpy.ops.bim.select_library_file(
|
||||
filepath=os.path.join(bpy.context.scene.BIMProperties.data_dir, "libraries", self.library_file)
|
||||
)
|
||||
|
||||
|
||||
def update_filter_mode(self, context):
|
||||
def update_filter_mode(self: "BIMProjectProperties", context: bpy.types.Context) -> None:
|
||||
self.filter_categories.clear()
|
||||
if self.filter_mode == "NONE":
|
||||
return
|
||||
|
||||
@@ -248,7 +248,12 @@ class Model(bonsai.core.tool.Model):
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_profile(cls, profile, obj=None, position=None):
|
||||
def import_profile(
|
||||
cls,
|
||||
profile: ifcopenshell.entity_instance,
|
||||
obj: Optional[bpy.types.Object] = None,
|
||||
position: Optional[Matrix] = None,
|
||||
) -> bpy.types.Object:
|
||||
"""Creates new profile mesh and assigns it to `obj`,
|
||||
if `obj` is `None` then new "Profile" object will be created.
|
||||
|
||||
@@ -297,7 +302,9 @@ class Model(bonsai.core.tool.Model):
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_surface(cls, surface, obj=None):
|
||||
def import_surface(
|
||||
cls, surface: ifcopenshell.entity_instance, obj: Optional[bpy.types.Object] = None
|
||||
) -> bpy.types.Object:
|
||||
cls.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
|
||||
cls.vertices = []
|
||||
@@ -333,7 +340,7 @@ class Model(bonsai.core.tool.Model):
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def import_curve(cls, obj, position, curve):
|
||||
def import_curve(cls, obj: bpy.types.Object, position: Matrix, curve: ifcopenshell.entity_instance) -> None:
|
||||
offset = len(cls.vertices)
|
||||
|
||||
if curve.is_a("IfcPolyline"):
|
||||
@@ -402,7 +409,7 @@ class Model(bonsai.core.tool.Model):
|
||||
cls.edges.append((offset, offset + 1))
|
||||
|
||||
@classmethod
|
||||
def import_rectangle(cls, obj, position, profile):
|
||||
def import_rectangle(cls, obj: bpy.types.Object, position: Matrix, profile: ifcopenshell.entity_instance) -> None:
|
||||
if profile.Position:
|
||||
p_position = Matrix(ifcopenshell.util.placement.get_axis2placement(profile.Position).tolist())
|
||||
p_position.translation *= cls.unit_scale
|
||||
@@ -424,7 +431,7 @@ class Model(bonsai.core.tool.Model):
|
||||
cls.edges[-1] = (len(cls.vertices) - 1, 0) # Close the loop
|
||||
|
||||
@classmethod
|
||||
def load_openings(cls, openings):
|
||||
def load_openings(cls, openings: list[ifcopenshell.entity_instance]) -> Iterable[bpy.types.Object]:
|
||||
if not openings:
|
||||
return []
|
||||
ifc_import_settings = import_ifc.IfcImportSettings.factory()
|
||||
@@ -476,7 +483,11 @@ class Model(bonsai.core.tool.Model):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_booleans(cls, element=None, representation=None) -> list[ifcopenshell.entity_instance]:
|
||||
def get_booleans(
|
||||
cls,
|
||||
element: Optional[ifcopenshell.entity_instance] = None,
|
||||
representation: Optional[ifcopenshell.entity_instance] = None,
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
if representation is None:
|
||||
representation = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if not representation:
|
||||
@@ -491,7 +502,9 @@ class Model(bonsai.core.tool.Model):
|
||||
return booleans
|
||||
|
||||
@classmethod
|
||||
def get_manual_booleans(cls, element, representation=None) -> list[ifcopenshell.entity_instance]:
|
||||
def get_manual_booleans(
|
||||
cls, element: ifcopenshell.entity_instance, representation: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
|
||||
if not pset:
|
||||
return []
|
||||
@@ -521,7 +534,7 @@ class Model(bonsai.core.tool.Model):
|
||||
ifcopenshell.api.run("pset.edit_pset", tool.Ifc.get(), pset=pset, properties={"Data": data})
|
||||
|
||||
@classmethod
|
||||
def unmark_manual_booleans(cls, element, boolean_ids):
|
||||
def unmark_manual_booleans(cls, element: ifcopenshell.entity_instance, boolean_ids: list[int]) -> None:
|
||||
# NOTE: we use use boolean_ids instead of boolean entities
|
||||
# so it will be possible to unmark manual booleans after they already was deleted
|
||||
pset = ifcopenshell.util.element.get_pset(element, "BBIM_Boolean")
|
||||
@@ -538,12 +551,14 @@ class Model(bonsai.core.tool.Model):
|
||||
ifcopenshell.api.run("pset.remove_pset", tool.Ifc.get(), product=element, pset=pset)
|
||||
|
||||
@classmethod
|
||||
def get_flow_segment_axis(cls, obj):
|
||||
def get_flow_segment_axis(cls, obj: bpy.types.Object) -> tuple[Vector, Vector]:
|
||||
z_values = [v[2] for v in obj.bound_box]
|
||||
return (obj.matrix_world @ Vector((0, 0, min(z_values))), obj.matrix_world @ Vector((0, 0, max(z_values))))
|
||||
|
||||
@classmethod
|
||||
def get_flow_segment_profile(cls, element):
|
||||
def get_flow_segment_profile(
|
||||
cls, element: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
material = ifcopenshell.util.element.get_material(element, should_skip_usage=True)
|
||||
if material and material.is_a("IfcMaterialProfileSet") and len(material.MaterialProfiles) == 1:
|
||||
return material.MaterialProfiles[0].Profile
|
||||
@@ -579,7 +594,7 @@ class Model(bonsai.core.tool.Model):
|
||||
return "PROFILE"
|
||||
|
||||
@classmethod
|
||||
def get_wall_axis(cls, obj, layers=None):
|
||||
def get_wall_axis(cls, obj: bpy.types.Object, layers: Optional[dict[str, Any]] = None) -> dict[str, Any]:
|
||||
x_values = [v[0] for v in obj.bound_box]
|
||||
min_x = min(x_values)
|
||||
max_x = max(x_values)
|
||||
@@ -602,7 +617,9 @@ class Model(bonsai.core.tool.Model):
|
||||
return axes
|
||||
|
||||
@classmethod
|
||||
def handle_array_on_copied_element(cls, element, array_data=None):
|
||||
def handle_array_on_copied_element(
|
||||
cls, element: ifcopenshell.entity_instance, array_data: Optional[dict[str, Any]] = None
|
||||
) -> None:
|
||||
"""if no `array_data` is provided then an array will be removed from the element"""
|
||||
|
||||
if array_data is None:
|
||||
@@ -647,7 +664,9 @@ class Model(bonsai.core.tool.Model):
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
@classmethod
|
||||
def regenerate_array(cls, parent_obj, data, array_layers_to_apply=tuple()):
|
||||
def regenerate_array(
|
||||
cls, parent_obj: bpy.types.Object, data: list[dict[str, Any]], array_layers_to_apply: Iterable[int] = tuple()
|
||||
) -> None:
|
||||
"""`array_layers_to_apply` - list of array layer indices to apply"""
|
||||
tool.Blender.Modifier.Array.remove_constraints(tool.Ifc.get_entity(parent_obj))
|
||||
|
||||
@@ -744,7 +763,12 @@ class Model(bonsai.core.tool.Model):
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@classmethod
|
||||
def replace_object_ifc_representation(cls, ifc_context, obj, new_representation):
|
||||
def replace_object_ifc_representation(
|
||||
cls,
|
||||
ifc_context: ifcopenshell.entity_instance,
|
||||
obj: bpy.types.Object,
|
||||
new_representation: ifcopenshell.entity_instance,
|
||||
) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
ifc_element = tool.Ifc.get_entity(obj)
|
||||
old_representation = ifcopenshell.util.representation.get_representation(
|
||||
@@ -771,7 +795,7 @@ class Model(bonsai.core.tool.Model):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def update_thumbnail_for_element(cls, element, refresh=False):
|
||||
def update_thumbnail_for_element(cls, element: ifcopenshell.entity_instance, refresh: bool = False) -> None:
|
||||
if bpy.app.background:
|
||||
return
|
||||
|
||||
@@ -877,7 +901,7 @@ class Model(bonsai.core.tool.Model):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_modeling_bbim_pset_data(cls, object, pset_name):
|
||||
def get_modeling_bbim_pset_data(cls, object: bpy.types.Object, pset_name: str) -> Union[dict[str, Any], None]:
|
||||
"""get modelling BBIM pset data (eg, BBIM_Roof) and loads it's `Data` as json to `data_dict`"""
|
||||
element = tool.Ifc.get_entity(object)
|
||||
if not element:
|
||||
@@ -890,7 +914,7 @@ class Model(bonsai.core.tool.Model):
|
||||
return pset_data
|
||||
|
||||
@classmethod
|
||||
def edit_element_placement(cls, element, matrix):
|
||||
def edit_element_placement(cls, element: ifcopenshell.entity_instance, matrix: Matrix) -> None:
|
||||
"""Useful for moving objects like ports or openings -
|
||||
the method will ensure it will be moved in blender scene too if it exists"""
|
||||
obj = tool.Ifc.get_object(element)
|
||||
@@ -900,13 +924,13 @@ class Model(bonsai.core.tool.Model):
|
||||
tool.Ifc.run("geometry.edit_object_placement", product=element, matrix=matrix, is_si=True)
|
||||
|
||||
@classmethod
|
||||
def sync_object_ifc_position(cls, obj):
|
||||
def sync_object_ifc_position(cls, obj: bpy.types.Object) -> None:
|
||||
"""make sure IFC position will be in sync with the Blender object position, if object was moved in Blender"""
|
||||
if tool.Ifc.is_moved(obj):
|
||||
bonsai.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
|
||||
@classmethod
|
||||
def get_element_matrix(cls, element, keep_local=False):
|
||||
def get_element_matrix(cls, element: ifcopenshell.entity_instance, keep_local: bool = False) -> Matrix:
|
||||
placement = element.ObjectPlacement
|
||||
if keep_local:
|
||||
placement = ifcopenshell.util.placement.get_axis2placement(placement.RelativePlacement)
|
||||
@@ -947,23 +971,23 @@ class Model(bonsai.core.tool.Model):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def is_parametric_roof_active(cls):
|
||||
return (RoofData.is_loaded or not RoofData.load()) and RoofData.data["pset_data"]
|
||||
def is_parametric_roof_active(cls) -> bool:
|
||||
return bool((RoofData.is_loaded or not RoofData.load()) and RoofData.data["pset_data"])
|
||||
|
||||
@classmethod
|
||||
def is_parametric_railing_active(cls):
|
||||
return (RailingData.is_loaded or not RailingData.load()) and RailingData.data["pset_data"]
|
||||
def is_parametric_railing_active(cls) -> bool:
|
||||
return bool((RailingData.is_loaded or not RailingData.load()) and RailingData.data["pset_data"])
|
||||
|
||||
@classmethod
|
||||
def is_parametric_window_active(cls):
|
||||
return (WindowData.is_loaded or not WindowData.load()) and WindowData.data["pset_data"]
|
||||
def is_parametric_window_active(cls) -> bool:
|
||||
return bool((WindowData.is_loaded or not WindowData.load()) and WindowData.data["pset_data"])
|
||||
|
||||
@classmethod
|
||||
def is_parametric_door_active(cls):
|
||||
return (DoorData.is_loaded or not DoorData.load()) and DoorData.data["pset_data"]
|
||||
def is_parametric_door_active(cls) -> bool:
|
||||
return bool((DoorData.is_loaded or not DoorData.load()) and DoorData.data["pset_data"])
|
||||
|
||||
@classmethod
|
||||
def get_active_stair_calculated_params(cls, pset_data=None):
|
||||
def get_active_stair_calculated_params(cls, pset_data: Optional[dict[str, Any]] = None) -> dict[str, Any]:
|
||||
props = bpy.context.active_object.BIMStairProperties
|
||||
|
||||
if props.is_editing:
|
||||
|
||||
Reference in New Issue
Block a user