This commit is contained in:
Andrej730
2024-11-18 11:09:59 +05:00
parent ec521916da
commit b2e3a4dd82
10 changed files with 62 additions and 34 deletions
+1 -1
View File
@@ -262,7 +262,7 @@ def update_titleblocks(self, context):
SheetsData.data["titleblocks"] = SheetsData.titleblocks()
def update_should_draw_decorations(self, context):
def update_should_draw_decorations(self, context: bpy.types.Context) -> None:
if self.should_draw_decorations:
# TODO: design a proper text variable templating renderer
collection = context.scene.camera.BIMObjectProperties.collection
+37 -24
View File
@@ -27,7 +27,9 @@ import ifcopenshell.util.shape
import bonsai.tool as tool
from math import pi, pow
from mathutils import Vector, Matrix, geometry
from typing import Union
from typing import Union, Any, TypeVar, Optional
T = TypeVar("T")
class Helper:
@@ -39,7 +41,7 @@ class Helper:
# edge that shares a single vertex only with that face to find the extrusion
# edge. A face with the normal facing down is prioritised. A limited
# dissolve ensure that faces are quads and not tris.
def auto_detect_rectangle_profile_extruded_area_solid(self, mesh: bpy.types.Mesh) -> dict:
def auto_detect_rectangle_profile_extruded_area_solid(self, mesh: bpy.types.Mesh) -> dict[str, Any]:
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
@@ -63,7 +65,7 @@ class Helper:
# only ngon (this assumes the circle has at least a facetation of > 4 edges.
# The extrusion direction is any edge that only shares a single vertex with
# the profile. We prioritise the profile that has a downwards normal.
def auto_detect_circle_profile_extruded_area_solid(self, mesh):
def auto_detect_circle_profile_extruded_area_solid(self, mesh: bpy.types.Mesh) -> dict[str, Any]:
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
@@ -91,7 +93,7 @@ class Helper:
# Failing that, it is equivalent to a rectangular profile. The extrusion
# direction is any edge that only shares a single vertex with the profile.
# We prioritise the profile that has a downwards normal.
def auto_detect_arbitrary_closed_profile_extruded_area_solid(self, mesh):
def auto_detect_arbitrary_closed_profile_extruded_area_solid(self, mesh: bpy.types.Mesh) -> dict[str, Any]:
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
@@ -131,7 +133,7 @@ class Helper:
# set of edges. Then, connected edges (i.e. sharing a vertex) are joined to
# form distinct loops. Finally, the outer loop is distinguished by being
# the loop with the greatest area.
def auto_detect_arbitrary_profile_with_voids_extruded_area_solid(self, mesh):
def auto_detect_arbitrary_profile_with_voids_extruded_area_solid(self, mesh: bpy.types.Mesh) -> dict[str, Any]:
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
@@ -228,7 +230,7 @@ class Helper:
# Before we begin we check that all faces are coplanar instead of finding suitable face.
# Then we process the same way.
# Only 2 parameters are returned as there is no extrusion.
def auto_detect_curve_bounded_plane(self, mesh, tolerance=0.001):
def auto_detect_curve_bounded_plane(self, mesh: bpy.types.Mesh, tolerance: float = 0.001) -> dict[str, Any]:
bm = bmesh.new()
bm.from_mesh(mesh)
bmesh.ops.dissolve_limit(bm, angle_limit=pi / 180 * 1, verts=bm.verts, edges=bm.edges)
@@ -316,9 +318,8 @@ class Helper:
# An extrusion edge is an edge that shares a single vertex with a profile
# face and is not on the plane of the face.
def detect_extrusion_edge(self, bm, profile_face):
def detect_extrusion_edge(self, bm: bmesh.types.BMesh, profile_face: bmesh.types.BMFace) -> Union[list[int], None]:
bm.edges.ensure_lookup_table()
extrusion = None
face_verts_set = set(profile_face.verts)
for edge in bm.edges:
unshared_verts = set(edge.verts) - face_verts_set
@@ -336,7 +337,9 @@ class Helper:
return [edge.verts[0].index, edge.verts[1].index]
return [edge.verts[1].index, edge.verts[0].index]
def create_extruded_area_solid(self, mesh, extrusion_indices, profile_def):
def create_extruded_area_solid(
self, mesh: bpy.types.Mesh, extrusion_indices: list[int], profile_def: dict[str, Any]
) -> ifcopenshell.entity_instance:
position = self.create_ifc_axis_2_placement_3d(
profile_def["curve_ucs"]["center"], profile_def["curve_ucs"]["z_axis"], profile_def["curve_ucs"]["x_axis"]
)
@@ -349,13 +352,15 @@ class Helper:
self.convert_si_to_unit(direction.length),
)
def create_arbitrary_closed_profile_def(self, mesh, profile_indices):
def create_arbitrary_closed_profile_def(self, mesh: bpy.types.Mesh, profile_indices: list[int]) -> dict[str, Any]:
curve_ucs = self.get_curve_profile_coordinate_system(mesh, profile_indices)
outer_curve = self.create_polyline_from_loop(mesh, profile_indices, curve_ucs)
curve = self.file.createIfcArbitraryClosedProfileDef("AREA", None, outer_curve)
return {"curve_ucs": curve_ucs, "curve": curve}
def create_arbitrary_profile_def_with_voids(self, mesh, profile_indices, inner_curve_indices):
def create_arbitrary_profile_def_with_voids(
self, mesh: bpy.types.Mesh, profile_indices: list[int], inner_curve_indices: list[int]
) -> dict[str, Any]:
curve_ucs = self.get_curve_profile_coordinate_system(mesh, profile_indices)
outer_curve = self.create_polyline_from_loop(mesh, profile_indices, curve_ucs)
inner_curves = [
@@ -364,7 +369,7 @@ class Helper:
curve = self.file.createIfcArbitraryProfileDefWithVoids("AREA", None, outer_curve, inner_curves)
return {"curve_ucs": curve_ucs, "curve": curve}
def create_rectangle_profile_def(self, mesh, profile_indices):
def create_rectangle_profile_def(self, mesh: bpy.types.Mesh, profile_indices: list[int]) -> dict[str, Any]:
curve_ucs = self.get_curve_profile_coordinate_system(mesh, profile_indices)
xdim = self.convert_si_to_unit(
(mesh.vertices[profile_indices[0]].co - mesh.vertices[profile_indices[1]].co).length
@@ -378,7 +383,7 @@ class Helper:
curve = self.file.createIfcRectangleProfileDef("AREA", None, position, xdim, ydim)
return {"curve_ucs": curve_ucs, "curve": curve}
def create_circle_profile_def(self, mesh, profile_indices):
def create_circle_profile_def(self, mesh: bpy.types.Mesh, profile_indices: list[int]) -> dict[str, Any]:
curve_ucs = self.get_curve_profile_coordinate_system(mesh, profile_indices)
radius = self.convert_si_to_unit(
abs(
@@ -395,13 +400,13 @@ class Helper:
return {"curve_ucs": curve_ucs, "curve": curve}
# Not used anywhere, but probably useful in the future
def get_loop_from_v_indices(self, obj, indices):
def get_loop_from_v_indices(self, obj: bpy.types.Object, indices: list[int]) -> list[int]:
edges = self.get_edges_in_v_indices(obj, indices)
loop = self.get_loop_from_edges(edges)
loop.pop(-1)
return loop
def get_loop_from_edges(self, edges):
def get_loop_from_edges(self, edges: list[bpy.types.MeshEdge]) -> list[int]:
while edges:
currentEdge = edges.pop()
startVert = currentEdge.vertices[0]
@@ -436,10 +441,10 @@ class Helper:
del edges[i]
return polyLine
def get_edges_in_v_indices(self, obj, indices):
def get_edges_in_v_indices(self, obj: bpy.types.Object, indices: list[int]) -> list[bpy.types.MeshEdge]:
return [e for e in obj.data.edges if (e.vertices[0] in indices and e.vertices[1] in indices)]
def get_curve_profile_coordinate_system(self, mesh, loop):
def get_curve_profile_coordinate_system(self, mesh: bpy.types.Mesh, loop: list[int]) -> dict[str, Any]:
profile_face = bpy.data.meshes.new("profile_face")
profile_verts = [(mesh.vertices[p].co.x, mesh.vertices[p].co.y, mesh.vertices[p].co.z) for p in loop]
profile_faces = [tuple(range(0, len(profile_verts)))]
@@ -461,10 +466,12 @@ class Helper:
"matrix": matrix.to_4x4() @ Matrix.Translation(-center),
}
def convert_si_to_unit(self, co):
def convert_si_to_unit(self, co: T) -> T:
return co / self.unit_scale
def create_polyline_from_loop(self, mesh, loop, curve_ucs):
def create_polyline_from_loop(
self, mesh: bpy.types.Mesh, loop: list[int], curve_ucs: dict[str, Any]
) -> ifcopenshell.entity_instance:
points = []
for point in loop:
transformed_point = curve_ucs["matrix"] @ mesh.vertices[point].co
@@ -472,7 +479,7 @@ class Helper:
points.append(points[0])
return self.file.createIfcPolyline(points)
def create_cartesian_point(self, x, y, z=None):
def create_cartesian_point(self, x: float, y: float, z: Optional[float] = None) -> ifcopenshell.entity_instance:
x = self.convert_si_to_unit(x)
y = self.convert_si_to_unit(y)
if z is None:
@@ -480,22 +487,28 @@ class Helper:
z = self.convert_si_to_unit(z)
return self.file.createIfcCartesianPoint((x, y, z))
def get_extrusion_direction(self, mesh, extrusion_indices, curve_ucs):
def get_extrusion_direction(
self, mesh: bpy.types.Mesh, extrusion_indices: list[int], curve_ucs: dict[str, Any]
) -> Vector:
return curve_ucs["matrix"] @ (
curve_ucs["center"] + (mesh.vertices[extrusion_indices[1]].co - mesh.vertices[extrusion_indices[0]].co)
)
def get_start_and_end_of_extrusion(self, profile_points, extrusion_edge):
def get_start_and_end_of_extrusion(
self, profile_points: list[int], extrusion_edge: bpy.types.MeshEdge
) -> tuple[int, int]:
if extrusion_edge.vertices[0] in profile_points:
return (extrusion_edge.vertices[0], extrusion_edge.vertices[1])
return (extrusion_edge.vertices[1], extrusion_edge.vertices[0])
def create_ifc_axis_2_placement_2d(self, point, forward):
def create_ifc_axis_2_placement_2d(self, point: Vector, forward: Vector) -> ifcopenshell.entity_instance:
return self.file.createIfcAxis2Placement2D(
self.create_cartesian_point(point.x, point.y), self.file.createIfcDirection((forward.x, forward.y))
)
def create_ifc_axis_2_placement_3d(self, point, up, forward):
def create_ifc_axis_2_placement_3d(
self, point: Vector, up: Vector, forward: Vector
) -> ifcopenshell.entity_instance:
return self.file.createIfcAxis2Placement3D(
self.create_cartesian_point(point.x, point.y, point.z),
self.file.createIfcDirection((up.x, up.y, up.z)),
@@ -19,6 +19,7 @@
import os
import bpy
import ifcopenshell
import ifcopenshell.util.attribute
from ifcopenshell.util.doc import get_attribute_doc
from bonsai.bim.module.pset_template.data import PsetTemplatesData
from bonsai.bim.prop import StrProperty, Attribute
@@ -19,7 +19,8 @@
import isodate
from dateutil import parser
import ifcopenshell.util.date
from datetime import timedelta
from datetime import timedelta, datetime
from typing import Union
def parse_datetime(value):
@@ -36,7 +37,7 @@ def parse_duration(value):
return ifcopenshell.util.date.parse_duration(value)
def canonicalise_time(time):
def canonicalise_time(time: Union[datetime, None]) -> str:
if not time:
return "-"
return time.strftime("%d/%m/%y")
+2 -1
View File
@@ -28,6 +28,7 @@ from bonsai.bim.module.sequence.data import (
TaskICOMData,
AnimationColorSchemeData,
)
from typing import Any
class BIM_PT_status(Panel):
@@ -1007,7 +1008,7 @@ class BIM_PT_work_calendars(Panel):
if self.props.active_work_time_id == work_time["id"]:
self.draw_editable_work_time_ui(work_time)
def draw_editable_work_time_ui(self, work_time):
def draw_editable_work_time_ui(self, work_time: dict[str, Any]) -> None:
draw_attributes(self.props.work_time_attributes, self.layout)
if work_time["RecurrencePattern"]:
self.draw_editable_recurrence_pattern_ui(
+1
View File
@@ -57,6 +57,7 @@ def edit_assigned_product(
ifc: tool.Ifc, drawing: tool.Drawing, obj: bpy.types.Object, product: Optional[ifcopenshell.entity_instance] = None
) -> None:
element = ifc.get_entity(obj)
assert element
existing_product = drawing.get_assigned_product(element)
if existing_product != product:
if existing_product:
+2 -2
View File
@@ -48,7 +48,7 @@ from shapely.ops import unary_union
from lxml import etree
from mathutils import Vector, Matrix
from fractions import Fraction
from typing import Optional, Union, Iterable, Any, Literal
from typing import Optional, Union, Iterable, Any, Literal, Sequence
from pathlib import Path
@@ -212,7 +212,7 @@ class Drawing(bonsai.core.tool.Drawing):
@classmethod
def is_annotation_object_type(
cls, element: ifcopenshell.entity_instance, object_types: Union[str, list[str]]
cls, element: ifcopenshell.entity_instance, object_types: Union[str, Sequence[str]]
) -> bool:
if not isinstance(object_types, collections.abc.Iterable):
object_types = [object_types]
@@ -56,7 +56,9 @@ def get_total_quantity(root_element: ifcopenshell.entity_instance) -> Union[floa
def calculate_applied_value(
root_element: ifcopenshell.entity_instance, cost_value: ifcopenshell.entity_instance, category_filter=None
root_element: ifcopenshell.entity_instance,
cost_value: ifcopenshell.entity_instance,
category_filter: Optional[str] = None,
) -> float:
if cost_value.ArithmeticOperator and cost_value.Components:
component_values = []
@@ -211,7 +211,7 @@ def string_to_duration(duration_string):
return isodate.duration_isoformat(datetime.timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds))
def parse_duration(value):
def parse_duration(value: Union[str, None]) -> Union[datetime.timedelta, None]:
if not value:
return None
if isinstance(value, str):
@@ -246,6 +246,9 @@ class BatchReassignClass:
class Migrator:
migrated_ids: dict[int, int]
attribute_overrides: dict[int, dict[int, str]]
def __init__(self):
self.migrated_ids = {}
self.attribute_overrides = {}
@@ -308,7 +311,7 @@ class Migrator:
"User": None,
}
def preprocess(self, old_file: ifcopenshell.file, new_file: ifcopenshell.file):
def preprocess(self, old_file: ifcopenshell.file, new_file: ifcopenshell.file) -> None:
to_delete = set()
if old_file.schema == "IFC2X3" and new_file.schema == "IFC4":
@@ -381,7 +384,13 @@ class Migrator:
new_element = new_file.create_entity(self.class_2x3_to_4[ifc_class])
return new_element
def migrate_attributes(self, element, new_file, new_element, new_element_schema):
def migrate_attributes(
self,
element: ifcopenshell.entity_instance,
new_file: ifcopenshell.file,
new_element: ifcopenshell.entity_instance,
new_element_schema: ifcopenshell_wrapper.declaration,
) -> ifcopenshell.entity_instance:
for attribute_index, value in self.attribute_overrides.get(element.id(), {}).items():
new_element[attribute_index] = value
for i, attribute in enumerate(new_element_schema.all_attributes()):