This commit is contained in:
Andrej730
2025-07-24 19:14:26 +05:00
parent cfd5d2a4eb
commit 05a8824019
22 changed files with 258 additions and 139 deletions
+21 -13
View File
@@ -381,7 +381,7 @@ class PolylineDecorator:
start = self.polyline_points[0] start = self.polyline_points[0]
if len(self.polyline_points) == 1: if len(self.polyline_points) == 1:
end = context.scene.BIMPolylineProperties.snap_mouse_point[0] end = tool.Model.get_polyline_props().snap_mouse_point[0]
else: else:
end = self.polyline_points[1] end = self.polyline_points[1]
@@ -632,14 +632,16 @@ class PolylineDecorator:
self.draw_batch("LINES", polyline_verts, self.decorator_color_special, polyline_edges) self.draw_batch("LINES", polyline_verts, self.decorator_color_special, polyline_edges)
def get_polylines_data(self, context): def get_polylines_data(self, context):
self.measure_type = context.scene.MeasureToolSettings.measurement_type self.measure_type = tool.Project.get_measure_tool_settings().measurement_type
self.polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
self.measure_data = context.scene.BIMPolylineProperties.measurement_polyline self.polyline_data = polyline_props.insertion_polyline
self.measure_data = polyline_props.measurement_polyline
def select_and_draw_measurements_text(self, context): def select_and_draw_measurements_text(self, context):
self.get_polylines_data(context) self.get_polylines_data(context)
if self.polyline_data: if self.polyline_data:
self.polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_props = tool.Model.get_polyline_props()
self.polyline_data = polyline_props.insertion_polyline[0]
self.polyline_points = self.polyline_data.polyline_points self.polyline_points = self.polyline_data.polyline_points
self.draw_measurements_text(context) self.draw_measurements_text(context)
@@ -653,7 +655,8 @@ class PolylineDecorator:
def select_and_draw_measurements_poly(self, context): def select_and_draw_measurements_poly(self, context):
self.get_polylines_data(context) self.get_polylines_data(context)
if self.polyline_data: if self.polyline_data:
self.polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_props = tool.Model.get_polyline_props()
self.polyline_data = polyline_props.insertion_polyline[0]
self.polyline_points = self.polyline_data.polyline_points self.polyline_points = self.polyline_data.polyline_points
self.draw_measurements_poly(context) self.draw_measurements_poly(context)
@@ -676,11 +679,12 @@ class PolylineDecorator:
region = context.region region = context.region
rv3d = region.data rv3d = region.data
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z)) mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
try: try:
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_ref[0] snap_prop = polyline_props.snap_mouse_ref[0]
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z)) mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
except: except:
pass pass
@@ -742,7 +746,8 @@ class PolylineDecorator:
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
gpu.state.point_size_set(6) gpu.state.point_size_set(6)
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
# Point related to the mouse # Point related to the mouse
mouse_point = [Vector((snap_prop.x, snap_prop.y, snap_prop.z))] mouse_point = [Vector((snap_prop.x, snap_prop.y, snap_prop.z))]
@@ -815,9 +820,10 @@ class PolylineDecorator:
self.draw_batch("LINES", axis2, highlight_color(axis_color2), [(0, 1)]) self.draw_batch("LINES", axis2, highlight_color(axis_color2), [(0, 1)])
# Create polyline with selected points # Create polyline with selected points
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
if polyline_data: if polyline_data:
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_data = polyline_props.insertion_polyline[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
else: else:
polyline_points = [] polyline_points = []
@@ -927,7 +933,8 @@ class ProductDecorator:
self.shader = gpu.shader.from_builtin("UNIFORM_COLOR") self.shader = gpu.shader.from_builtin("UNIFORM_COLOR")
self.line_shader.uniform_float("lineWidth", 2.0) self.line_shader.uniform_float("lineWidth", 2.0)
decorator_color = self.addon_prefs.decorator_color_special decorator_color = self.addon_prefs.decorator_color_special
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
self.relating_type = None self.relating_type = None
@@ -1114,7 +1121,8 @@ class FaceAreaDecorator:
gpu.state.blend_set("ALPHA") gpu.state.blend_set("ALPHA")
decorator_color = self.addon_prefs.decorator_color_special decorator_color = self.addon_prefs.decorator_color_special
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
for i, polyline in enumerate(polyline_data): for i, polyline in enumerate(polyline_data):
vertices = [] vertices = []
for point in polyline.polyline_points: for point in polyline.polyline_points:
+15 -8
View File
@@ -92,7 +92,8 @@ def get_wall_preview_data(context, relating_type):
# Verts # Verts
polyline_vertices = [] polyline_vertices = []
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if len(polyline_points) < 2: if len(polyline_points) < 2:
data = [] data = []
@@ -212,7 +213,8 @@ def get_slab_preview_data(context, relating_type):
data["verts"] = [] data["verts"] = []
# Verts # Verts
polyline_vertices = [] polyline_vertices = []
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if len(polyline_points) < 3: if len(polyline_points) < 3:
data = [] data = []
@@ -340,7 +342,8 @@ def get_vertical_profile_preview_data(
tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()] tris = [[loop.vert.index for loop in triangles] for triangles in bm.calc_loop_triangles()]
# Calculate rotation, mouse position, angle and cardinal point # Calculate rotation, mouse position, angle and cardinal point
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z)) mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
data = {} data = {}
@@ -384,7 +387,8 @@ def get_horizontal_profile_preview_data(
cardinal_point = model_props.cardinal_point cardinal_point = model_props.cardinal_point
polyline_verts = [] polyline_verts = []
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if len(polyline_points) < 2: if len(polyline_points) < 2:
return {} return {}
@@ -530,7 +534,8 @@ def get_generic_product_preview_data(context, relating_type):
rl = float(model_props.rl2) rl = float(model_props.rl2)
else: else:
rl = 0 rl = 0
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
default_container_elevation = tool.Root.get_default_container_elevation() default_container_elevation = tool.Root.get_default_container_elevation()
mouse_point = Vector((snap_prop.x, snap_prop.y, default_container_elevation)) mouse_point = Vector((snap_prop.x, snap_prop.y, default_container_elevation))
snap_obj = bpy.data.objects.get(snap_prop.snap_object) snap_obj = bpy.data.objects.get(snap_prop.snap_object)
@@ -646,7 +651,8 @@ class PolylineOperator:
def choose_plane(self, event: bpy.types.Event, x: bool = True, y: bool = True, z: bool = True) -> None: def choose_plane(self, event: bpy.types.Event, x: bool = True, y: bool = True, z: bool = True) -> None:
def get_plane_origin(): def get_plane_origin():
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if len(polyline_points) > 0: if len(polyline_points) > 0:
reference_point = polyline_points[-1] reference_point = polyline_points[-1]
@@ -824,7 +830,8 @@ class PolylineOperator:
if event.value == "PRESS" and event.type == "C": if event.value == "PRESS" and event.type == "C":
# Get the first point coordinates to close the polyline # Get the first point coordinates to close the polyline
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if len(polyline_points) > 2: if len(polyline_points) > 2:
first_point = polyline_points[0] first_point = polyline_points[0]
@@ -832,7 +839,7 @@ class PolylineOperator:
if not ( if not (
first_point.x == last_point.x and first_point.y == last_point.y and first_point.z == last_point.z first_point.x == last_point.x and first_point.y == last_point.y and first_point.z == last_point.z
): ):
mouse_point = context.scene.BIMPolylineProperties.snap_mouse_point[0] mouse_point = polyline_props.snap_mouse_point[0]
mouse_point.x = first_point.x mouse_point.x = first_point.x
mouse_point.y = first_point.y mouse_point.y = first_point.y
if self.input_ui.get_number_value("Z") is not None: if self.input_ui.get_number_value("Z") is not None:
@@ -180,7 +180,8 @@ class DrawOccurrence(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
# TODO: when this workflow matures a bit, recode it so it doesn't rely on selection and cursor # TODO: when this workflow matures a bit, recode it so it doesn't rely on selection and cursor
# Select snapped object so we can insert doors and windows # Select snapped object so we can insert doors and windows
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
snap_obj = bpy.data.objects.get(snap_prop.snap_object) snap_obj = bpy.data.objects.get(snap_prop.snap_object)
if snap_obj: if snap_obj:
try: try:
@@ -193,7 +194,7 @@ class DrawOccurrence(bpy.types.Operator, PolylineOperator, tool.Ifc.Operator):
except: except:
pass pass
point = context.scene.BIMPolylineProperties.insertion_polyline[0].polyline_points[0] point = polyline_props.insertion_polyline[0].polyline_points[0]
context.scene.cursor.location = Vector((point.x, point.y, point.z)) context.scene.cursor.location = Vector((point.x, point.y, point.z))
tool.Polyline.clear_polyline() tool.Polyline.clear_polyline()
@@ -545,7 +546,10 @@ class ChangeTypePage(bpy.types.Operator, tool.Ifc.Operator):
bl_idname = "bim.change_type_page" bl_idname = "bim.change_type_page"
bl_label = "Change Type Page" bl_label = "Change Type Page"
bl_options = {"REGISTER"} bl_options = {"REGISTER"}
page: bpy.props.IntProperty() page: bpy.props.IntProperty() # pyright: ignore[reportRedeclaration]
if TYPE_CHECKING:
page: int
def _execute(self, context): def _execute(self, context):
props = tool.Model.get_model_props() props = tool.Model.get_model_props()
+20 -11
View File
@@ -40,7 +40,10 @@ from math import pi, degrees, atan2
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator from bonsai.bim.module.model.decorator import ProfileDecorator, PolylineDecorator, ProductDecorator
from bonsai.bim.module.model.polyline import PolylineOperator from bonsai.bim.module.model.polyline import PolylineOperator
from typing import Union, Any, Optional from typing import Union, Any, Optional, Literal
ProfileFrom2PointsReturn = Union[dict[str, Any], None]
class DumbProfileGenerator: class DumbProfileGenerator:
@@ -48,7 +51,9 @@ class DumbProfileGenerator:
self.relating_type = relating_type self.relating_type = relating_type
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
def generate(self, insertion_type="CURSOR"): def generate(
self, insertion_type: Literal["CURSOR", "POLYLINE"] = "CURSOR"
) -> Union[tuple[list[ProfileFrom2PointsReturn], bool], bpy.types.Object, None]:
self.insertion_type = insertion_type self.insertion_type = insertion_type
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) self.unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
@@ -77,8 +82,9 @@ class DumbProfileGenerator:
elif self.insertion_type == "CURSOR": elif self.insertion_type == "CURSOR":
return self.derive_from_cursor() return self.derive_from_cursor()
def derive_from_polyline(self) -> tuple[list[Union[dict[str, Any], None]], bool]: def derive_from_polyline(self) -> tuple[list[ProfileFrom2PointsReturn], bool]:
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
is_polyline_closed = False is_polyline_closed = False
if len(polyline_points) > 3: if len(polyline_points) > 3:
@@ -87,7 +93,7 @@ class DumbProfileGenerator:
if first_vec == last_vec: if first_vec == last_vec:
is_polyline_closed = True is_polyline_closed = True
profiles = [] profiles: list[ProfileFrom2PointsReturn] = []
for i in range(len(polyline_points) - 1): for i in range(len(polyline_points) - 1):
vec1 = Vector((polyline_points[i].x, polyline_points[i].y, polyline_points[i].z)) vec1 = Vector((polyline_points[i].x, polyline_points[i].y, polyline_points[i].z))
vec2 = Vector((polyline_points[i + 1].x, polyline_points[i + 1].y, polyline_points[i + 1].z)) vec2 = Vector((polyline_points[i + 1].x, polyline_points[i + 1].y, polyline_points[i + 1].z))
@@ -95,11 +101,12 @@ class DumbProfileGenerator:
profiles.append(self.create_profile_from_2_points(coords)) profiles.append(self.create_profile_from_2_points(coords))
return profiles, is_polyline_closed return profiles, is_polyline_closed
def derive_from_cursor(self): def derive_from_cursor(self) -> bpy.types.Object:
assert bpy.context.scene
self.location = bpy.context.scene.cursor.location self.location = bpy.context.scene.cursor.location
return self.create_profile() return self.create_profile()
def create_profile(self): def create_profile(self) -> bpy.types.Object:
ifc_classes = ifcopenshell.util.type.get_applicable_entities(self.relating_type.is_a(), self.file.schema) ifc_classes = ifcopenshell.util.type.get_applicable_entities(self.relating_type.is_a(), self.file.schema)
# Standard cases are deprecated, so let's cull them # Standard cases are deprecated, so let's cull them
ifc_class = next(c for c in ifc_classes if "StandardCase" not in c) ifc_class = next(c for c in ifc_classes if "StandardCase" not in c)
@@ -109,7 +116,7 @@ class DumbProfileGenerator:
matrix_world = Matrix() matrix_world = Matrix()
if self.relating_type.is_a() not in ("IfcColumnType", "IfcPileType"): if self.relating_type.is_a() not in ("IfcColumnType", "IfcPileType"):
if self.insertion_type not in {"POLYLINE"}: if self.insertion_type != "POLYLINE":
matrix_world = Matrix.Rotation(pi / 2, 4, "Z") @ Matrix.Rotation(pi / 2, 4, "X") @ matrix_world matrix_world = Matrix.Rotation(pi / 2, 4, "Z") @ Matrix.Rotation(pi / 2, 4, "X") @ matrix_world
matrix_world = Matrix.Rotation(self.rotation, 4, "Z") @ matrix_world matrix_world = Matrix.Rotation(self.rotation, 4, "Z") @ matrix_world
else: else:
@@ -117,7 +124,7 @@ class DumbProfileGenerator:
matrix_world = rotation_matrix.to_matrix().to_4x4() @ matrix_world matrix_world = rotation_matrix.to_matrix().to_4x4() @ matrix_world
matrix_world.translation = self.location matrix_world.translation = self.location
if self.insertion_type not in {"POLYLINE"} and self.container_obj: if self.insertion_type != "POLYLINE" and self.container_obj:
matrix_world.translation.z = self.container_obj.location.z matrix_world.translation.z = self.container_obj.location.z
element = bonsai.core.root.assign_class( element = bonsai.core.root.assign_class(
tool.Ifc, tool.Ifc,
@@ -171,12 +178,14 @@ class DumbProfileGenerator:
return obj return obj
def create_profile_from_2_points(self, coords, should_round=False) -> Union[dict[str, Any], None]: def create_profile_from_2_points(
self, coords: tuple[Vector, Vector], should_round: bool = False
) -> ProfileFrom2PointsReturn:
self.direction = coords[1] - coords[0] self.direction = coords[1] - coords[0]
length = self.direction.length length = self.direction.length
if round(length, 4) < 0.1: if round(length, 4) < 0.1:
return return
data = {"coords": coords} data: dict[str, Any] = {"coords": coords}
self.depth = length self.depth = length
self.rotation = atan2(self.direction[1], self.direction[0]) self.rotation = atan2(self.direction[1], self.direction[0])
@@ -323,6 +323,13 @@ class BIMModelProperties(PropertyGroup):
show_wall_axis: bool show_wall_axis: bool
show_slab_direction: bool show_slab_direction: bool
prev_transform_orientation_slot_type: str
prev_show_gizmo_object_translate: bool
show_bounding_box: bool
show_cut_decorator: bool
show_cut_decorator_fill: bool
class BIMArrayProperties(PropertyGroup): class BIMArrayProperties(PropertyGroup):
is_editing: bpy.props.IntProperty( is_editing: bpy.props.IntProperty(
@@ -1157,6 +1164,13 @@ class SnapMousePoint(PropertyGroup):
snap_type: bpy.props.StringProperty(name="Snap Type") snap_type: bpy.props.StringProperty(name="Snap Type")
snap_object: bpy.props.StringProperty(name="Object Name") snap_object: bpy.props.StringProperty(name="Object Name")
if TYPE_CHECKING:
x: float
y: float
z: float
snap_type: str
snap_object: str
class PolylinePoint(PropertyGroup): class PolylinePoint(PropertyGroup):
x: bpy.props.FloatProperty(name="X") x: bpy.props.FloatProperty(name="X")
@@ -1166,6 +1180,14 @@ class PolylinePoint(PropertyGroup):
angle: bpy.props.StringProperty(name="Angle") angle: bpy.props.StringProperty(name="Angle")
position: bpy.props.FloatVectorProperty(name="Decorator Position", size=3) position: bpy.props.FloatVectorProperty(name="Decorator Position", size=3)
if TYPE_CHECKING:
x: float
y: float
z: float
dim: str
angle: str
position: tuple[float, float, float]
class Polyline(PropertyGroup): class Polyline(PropertyGroup):
id: bpy.props.StringProperty(name="Id") id: bpy.props.StringProperty(name="Id")
@@ -1174,6 +1196,13 @@ class Polyline(PropertyGroup):
area: bpy.props.StringProperty(name="Measured Area") area: bpy.props.StringProperty(name="Measured Area")
total_length: bpy.props.StringProperty(name="Total Length") total_length: bpy.props.StringProperty(name="Total Length")
if TYPE_CHECKING:
id: str
polyline_points: bpy.types.bpy_prop_collection_idprop[PolylinePoint]
measurement_type: str
area: str
total_length: str
class BIMPolylineProperties(PropertyGroup): class BIMPolylineProperties(PropertyGroup):
snap_mouse_point: bpy.props.CollectionProperty(type=SnapMousePoint) snap_mouse_point: bpy.props.CollectionProperty(type=SnapMousePoint)
@@ -1181,17 +1210,32 @@ class BIMPolylineProperties(PropertyGroup):
insertion_polyline: bpy.props.CollectionProperty(type=Polyline) insertion_polyline: bpy.props.CollectionProperty(type=Polyline)
measurement_polyline: bpy.props.CollectionProperty(type=Polyline) measurement_polyline: bpy.props.CollectionProperty(type=Polyline)
if TYPE_CHECKING:
snap_mouse_point: bpy.types.bpy_prop_collection_idprop[SnapMousePoint]
snap_mouse_ref: bpy.types.bpy_prop_collection_idprop[SnapMousePoint]
insertion_polyline: bpy.types.bpy_prop_collection_idprop[Polyline]
measurement_polyline: bpy.types.bpy_prop_collection_idprop[Polyline]
class ProductPreviewItem(PropertyGroup): class ProductPreviewItem(PropertyGroup):
value_3d: bpy.props.FloatVectorProperty() value_3d: bpy.props.FloatVectorProperty()
value_2d: bpy.props.FloatVectorProperty(size=2) value_2d: bpy.props.FloatVectorProperty(size=2)
if TYPE_CHECKING:
value_3d: tuple[float, float, float]
value_2d: tuple[float, float]
class BIMProductPreviewProperties(PropertyGroup): class BIMProductPreviewProperties(PropertyGroup):
verts: bpy.props.CollectionProperty(type=ProductPreviewItem) verts: bpy.props.CollectionProperty(type=ProductPreviewItem)
edges: bpy.props.CollectionProperty(type=ProductPreviewItem) edges: bpy.props.CollectionProperty(type=ProductPreviewItem)
tris: bpy.props.CollectionProperty(type=ProductPreviewItem) tris: bpy.props.CollectionProperty(type=ProductPreviewItem)
if TYPE_CHECKING:
verts: bpy.types.bpy_prop_collection_idprop[ProductPreviewItem]
edges: bpy.types.bpy_prop_collection_idprop[ProductPreviewItem]
tris: bpy.types.bpy_prop_collection_idprop[ProductPreviewItem]
def update_is_editing(self: "BIMExternalParametricGeometryProperties", context: bpy.types.Context) -> None: def update_is_editing(self: "BIMExternalParametricGeometryProperties", context: bpy.types.Context) -> None:
if self.is_editing: if self.is_editing:
+2 -1
View File
@@ -88,7 +88,8 @@ class DumbSlabGenerator:
return self.derive_from_cursor() return self.derive_from_cursor()
def derive_from_polyline(self): def derive_from_polyline(self):
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
self.location = Vector((polyline_points[0].x, polyline_points[0].y, self.container_obj.location.z)) self.location = Vector((polyline_points[0].x, polyline_points[0].y, self.container_obj.location.z))
self.polyline = [tuple(Vector((p.x, p.y, 0.0)) - self.location) for p in polyline_points] self.polyline = [tuple(Vector((p.x, p.y, 0.0)) - self.location) for p in polyline_points]
+7 -4
View File
@@ -163,7 +163,8 @@ class ExtendWallsToPolylinePoint(bpy.types.Operator, PolylineOperator, tool.Ifc.
PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0]) PolylineDecorator.update(event, self.tool_state, self.input_ui, self.snapping_points[0])
tool.Blender.update_viewport() tool.Blender.update_viewport()
# Point related to the mouse # Point related to the mouse
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z)) mouse_point = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
angle = atan2(direcion.y, direcion.x) angle = atan2(direcion.y, direcion.x)
@@ -214,13 +215,14 @@ class ExtendWallsToPolylinePoint(bpy.types.Operator, PolylineOperator, tool.Ifc.
if result: if result:
self.report({"WARNING"}, result) self.report({"WARNING"}, result)
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_prop = polyline_props.snap_mouse_point[0]
snap_obj = bpy.data.objects.get(snap_prop.snap_object) snap_obj = bpy.data.objects.get(snap_prop.snap_object)
if snap_obj and tool.Ifc.get_entity(snap_obj).is_a("IfcWall"): if snap_obj and tool.Ifc.get_entity(snap_obj).is_a("IfcWall"):
tool.Blender.set_active_object(snap_obj) tool.Blender.set_active_object(snap_obj)
ExtendWallsToWall._execute(self, context) ExtendWallsToWall._execute(self, context)
else: else:
point = context.scene.BIMPolylineProperties.insertion_polyline[0].polyline_points[1] point = polyline_props.insertion_polyline[0].polyline_points[1]
core.extend_walls( core.extend_walls(
tool.Ifc, tool.Ifc,
tool.Blender, tool.Blender,
@@ -867,7 +869,8 @@ class DumbWallGenerator:
return self.derive_from_cursor() return self.derive_from_cursor()
def derive_from_polyline(self) -> tuple[list[Union[dict[str, Any], None]], bool]: def derive_from_polyline(self) -> tuple[list[Union[dict[str, Any], None]], bool]:
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
is_polyline_closed = False is_polyline_closed = False
if len(polyline_points) > 3: if len(polyline_points) > 3:
@@ -2625,11 +2625,11 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
self.handle_snap_selection(context, event) self.handle_snap_selection(context, event)
single_mode = False single_mode = False
polyline_props = tool.Model.get_polyline_props()
if ( if (
self.measure_type == "SINGLE" self.measure_type == "SINGLE"
and context.scene.BIMPolylineProperties.insertion_polyline and polyline_props.insertion_polyline
and len(context.scene.BIMPolylineProperties.insertion_polyline[0].polyline_points) >= 2 and len(polyline_props.insertion_polyline[0].polyline_points) >= 2
): ):
single_mode = True single_mode = True
@@ -2652,19 +2652,19 @@ class MeasureTool(bpy.types.Operator, PolylineOperator):
self.handle_inserting_polyline(context, event) self.handle_inserting_polyline(context, event)
# Add measurement type to the insertion polyline # Add measurement type to the insertion polyline
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_data = polyline_props.insertion_polyline
if not polyline_data: if not polyline_data:
pass pass
else: else:
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_data = polyline_props.insertion_polyline[0]
measurement_type = bpy.context.scene.MeasureToolSettings.measurement_type measurement_type = tool.Project.get_measure_tool_settings().measurement_type
if not polyline_data.measurement_type: if not polyline_data.measurement_type:
polyline_data.measurement_type = measurement_type polyline_data.measurement_type = measurement_type
tool.Polyline.calculate_area(context, self.input_ui) tool.Polyline.calculate_area(context, self.input_ui)
if event.type == "E": if event.type == "E":
context.scene.BIMPolylineProperties.measurement_polyline.clear() polyline_props.measurement_polyline.clear()
MeasureDecorator.uninstall() MeasureDecorator.uninstall()
tool.Blender.update_viewport() tool.Blender.update_viewport()
@@ -2727,6 +2727,7 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
return {"PASS_THROUGH"} return {"PASS_THROUGH"}
self.handle_mouse_move(context, event) self.handle_mouse_move(context, event)
polyline_props = tool.Model.get_polyline_props()
if event.value == "PRESS" and event.type == "LEFTMOUSE": if event.value == "PRESS" and event.type == "LEFTMOUSE":
tool.Blender.update_viewport() tool.Blender.update_viewport()
@@ -2737,7 +2738,7 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
self.clicked_faces.append(obj.data.polygons[face_index]) self.clicked_faces.append(obj.data.polygons[face_index])
self.total_area += obj.data.polygons[face_index].area self.total_area += obj.data.polygons[face_index].area
self.input_ui.set_value("AREA", self.total_area) self.input_ui.set_value("AREA", self.total_area)
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline.add() polyline_data = polyline_props.insertion_polyline.add()
polyline_data.id = obj.name + str(face_index) polyline_data.id = obj.name + str(face_index)
for v_id in obj.data.polygons[face_index].vertices: for v_id in obj.data.polygons[face_index].vertices:
vertex = obj.matrix_world @ obj.data.vertices[v_id].co vertex = obj.matrix_world @ obj.data.vertices[v_id].co
@@ -2755,14 +2756,14 @@ class MeasureFaceAreaTool(bpy.types.Operator, PolylineOperator):
self.clicked_faces.remove(obj.data.polygons[face_index]) self.clicked_faces.remove(obj.data.polygons[face_index])
self.total_area -= obj.data.polygons[face_index].area self.total_area -= obj.data.polygons[face_index].area
self.input_ui.set_value("AREA", self.total_area) self.input_ui.set_value("AREA", self.total_area)
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_data = polyline_props.insertion_polyline
for i, polyline in enumerate(polyline_data): for i, polyline in enumerate(polyline_data):
if polyline.id == obj.name + str(face_index): if polyline.id == obj.name + str(face_index):
polyline_data.remove(i) polyline_data.remove(i)
tool.Blender.update_viewport() tool.Blender.update_viewport()
if event.value == "RELEASE" and event.type in {"ESC", "RIGHTMOUSE"}: if event.value == "RELEASE" and event.type in {"ESC", "RIGHTMOUSE"}:
bpy.context.scene.BIMPolylineProperties.insertion_polyline.clear() polyline_props.insertion_polyline.clear()
context.workspace.status_text_set(text=None) context.workspace.status_text_set(text=None)
PolylineDecorator.uninstall() PolylineDecorator.uninstall()
FaceAreaDecorator.uninstall() FaceAreaDecorator.uninstall()
@@ -2786,10 +2787,12 @@ class ClearMeasurement(bpy.types.Operator):
@classmethod @classmethod
def poll(cls, context): def poll(cls, context):
return len(context.scene.BIMPolylineProperties.measurement_polyline) > 0 polyline_props = tool.Model.get_polyline_props()
return len(polyline_props.measurement_polyline) > 0
def execute(self, context): def execute(self, context):
context.scene.BIMPolylineProperties.measurement_polyline.clear() polyline_props = tool.Model.get_polyline_props()
polyline_props.measurement_polyline.clear()
MeasureDecorator.uninstall() MeasureDecorator.uninstall()
tool.Blender.update_viewport() tool.Blender.update_viewport()
return {"FINISHED"} return {"FINISHED"}
@@ -59,7 +59,7 @@ class ExploreTool(bpy.types.WorkSpaceTool):
row.label(text="", icon="EVENT_ALT") row.label(text="", icon="EVENT_ALT")
row.label(text="Disable Culling" if LinksData.enable_culling else "Enable Culling", icon="EVENT_C") row.label(text="Disable Culling" if LinksData.enable_culling else "Enable Culling", icon="EVENT_C")
prop = context.scene.MeasureToolSettings prop = tool.Project.get_measure_tool_settings()
row = layout.row(align=True) row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT") row.label(text="", icon="EVENT_SHIFT")
row.label(text="", icon="EVENT_M") row.label(text="", icon="EVENT_M")
@@ -105,7 +105,7 @@ class ExploreHotkey(bpy.types.Operator):
def hotkey_S_M(self): def hotkey_S_M(self):
for obj in tool.Blender.get_selected_objects(): for obj in tool.Blender.get_selected_objects():
obj.select_set(False) obj.select_set(False)
measure_type = bpy.context.scene.MeasureToolSettings.measurement_type measure_type = tool.Project.get_measure_tool_settings().measurement_type
if measure_type == "FACE_AREA": if measure_type == "FACE_AREA":
bpy.ops.bim.measure_face_area_tool("INVOKE_DEFAULT") bpy.ops.bim.measure_face_area_tool("INVOKE_DEFAULT")
else: else:
@@ -646,10 +646,11 @@ class EditWorkCalendar(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def _execute(self, context): def _execute(self, context):
props = tool.Sequence.get_work_calendar_props()
core.edit_work_calendar( core.edit_work_calendar(
tool.Ifc, tool.Ifc,
tool.Sequence, tool.Sequence,
work_calendar=tool.Ifc.get().by_id(context.scene.BIMWorkCalendarProperties.active_work_calendar_id), work_calendar=tool.Ifc.get().by_id(props.active_work_calendar_id),
) )
+3 -1
View File
@@ -1021,11 +1021,13 @@ class BIM_PT_work_calendars(Panel):
file = tool.Ifc.get() file = tool.Ifc.get()
return file and hasattr(file, "schema") and file.schema != "IFC2X3" return file and hasattr(file, "schema") and file.schema != "IFC2X3"
layout: bpy.types.UILayout
def draw(self, context): def draw(self, context):
if not SequenceData.is_loaded: if not SequenceData.is_loaded:
SequenceData.load() SequenceData.load()
self.props = context.scene.BIMWorkCalendarProperties self.props = tool.Sequence.get_work_calendar_props()
row = self.layout.row() row = self.layout.row()
if SequenceData.data["has_work_calendars"]: if SequenceData.data["has_work_calendars"]:
row.label( row.label(
+6
View File
@@ -65,6 +65,7 @@ if TYPE_CHECKING:
BIMStairProperties, BIMStairProperties,
BIMRailingProperties, BIMRailingProperties,
BIMExternalParametricGeometryProperties, BIMExternalParametricGeometryProperties,
BIMPolylineProperties,
) )
@@ -101,6 +102,11 @@ class Model(bonsai.core.tool.Model):
def get_epg_props(cls, obj: bpy.types.Object) -> BIMExternalParametricGeometryProperties: def get_epg_props(cls, obj: bpy.types.Object) -> BIMExternalParametricGeometryProperties:
return obj.BIMExternalParametricGeometryProperties return obj.BIMExternalParametricGeometryProperties
@classmethod
def get_polyline_props(cls) -> BIMPolylineProperties:
assert (scene := bpy.context.scene)
return scene.BIMPolylineProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod @classmethod
def convert_si_to_unit(cls, value: T) -> T: def convert_si_to_unit(cls, value: T) -> T:
if isinstance(value, (tuple, list)): if isinstance(value, (tuple, list)):
+22 -14
View File
@@ -106,7 +106,8 @@ class Polyline(bonsai.core.tool.Polyline):
cls, context: bpy.types.Context, input_ui: PolylineUI, tool_state: ToolState, should_round: bool = False cls, context: bpy.types.Context, input_ui: PolylineUI, tool_state: ToolState, should_round: bool = False
) -> None: ) -> None:
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
if len(polyline_data) > 0: if len(polyline_data) > 0:
polyline_data = polyline_data[0] polyline_data = polyline_data[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
@@ -123,7 +124,7 @@ class Polyline(bonsai.core.tool.Polyline):
else: else:
default_container_elevation = 0 default_container_elevation = 0
mouse_point = context.scene.BIMPolylineProperties.snap_mouse_point[0] mouse_point = polyline_props.snap_mouse_point[0]
if last_point_data: if last_point_data:
last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z)) last_point = Vector((last_point_data.x, last_point_data.y, last_point_data.z))
@@ -202,8 +203,9 @@ class Polyline(bonsai.core.tool.Polyline):
@classmethod @classmethod
def calculate_area(cls, context: bpy.types.Context, input_ui: PolylineUI) -> Union[PolylineUI, None]: def calculate_area(cls, context: bpy.types.Context, input_ui: PolylineUI) -> Union[PolylineUI, None]:
polyline_props = tool.Model.get_polyline_props()
try: try:
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_data = polyline_props.insertion_polyline[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
except: except:
return input_ui return input_ui
@@ -251,9 +253,10 @@ class Polyline(bonsai.core.tool.Polyline):
@classmethod @classmethod
def calculate_x_y_and_z(cls, context: bpy.types.Context, input_ui: PolylineUI, tool_state: ToolState) -> None: def calculate_x_y_and_z(cls, context: bpy.types.Context, input_ui: PolylineUI, tool_state: ToolState) -> None:
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
if len(polyline_data) > 0: if len(polyline_data) > 0:
polyline_data = context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_data = polyline_props.insertion_polyline[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
if len(polyline_points) > 0: if len(polyline_points) > 0:
last_point_data = polyline_points[len(polyline_points) - 1] last_point_data = polyline_points[len(polyline_points) - 1]
@@ -269,7 +272,7 @@ class Polyline(bonsai.core.tool.Polyline):
else: else:
default_container_elevation = 0 default_container_elevation = 0
snap_prop = context.scene.BIMPolylineProperties.snap_mouse_point[0] snap_prop = polyline_props.snap_mouse_point[0]
snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z)) snap_vector = Vector((snap_prop.x, snap_prop.y, snap_prop.z))
if tool_state.is_input_on: if tool_state.is_input_on:
@@ -501,11 +504,13 @@ class Polyline(bonsai.core.tool.Polyline):
d = input_ui.get_formatted_value("D") d = input_ui.get_formatted_value("D")
a = input_ui.get_formatted_value("A") a = input_ui.get_formatted_value("A")
snap_vertex = bpy.context.scene.BIMPolylineProperties.snap_mouse_point[0] polyline_props = tool.Model.get_polyline_props()
snap_vertex = polyline_props.snap_mouse_point[0]
if tool_state and tool_state.use_default_container: if tool_state and tool_state.use_default_container:
z = tool.Root.get_default_container_elevation() z = tool.Root.get_default_container_elevation()
# Lock one dimension when in plane method # Lock one dimension when in plane method
assert tool_state
if tool_state.plane_origin: if tool_state.plane_origin:
if tool_state.plane_method == "XY": if tool_state.plane_method == "XY":
z = tool_state.plane_origin.z z = tool_state.plane_origin.z
@@ -519,9 +524,9 @@ class Polyline(bonsai.core.tool.Polyline):
y = snap_vertex.y y = snap_vertex.y
z = snap_vertex.z z = snap_vertex.z
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_data = polyline_props.insertion_polyline
if not polyline_data: if not polyline_data:
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline.add() polyline_data = polyline_props.insertion_polyline.add()
else: else:
polyline_data = polyline_data[0] polyline_data = polyline_data[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
@@ -560,20 +565,23 @@ class Polyline(bonsai.core.tool.Polyline):
@classmethod @classmethod
def clear_polyline(cls) -> None: def clear_polyline(cls) -> None:
bpy.context.scene.BIMPolylineProperties.insertion_polyline.clear() polyline_props = tool.Model.get_polyline_props()
polyline_props.insertion_polyline.clear()
@classmethod @classmethod
def remove_last_polyline_point(cls) -> None: def remove_last_polyline_point(cls) -> None:
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
polyline_points.remove(len(polyline_points) - 1) polyline_points.remove(len(polyline_points) - 1)
@classmethod @classmethod
def move_polyline_to_measure(cls, context: bpy.types.Context, input_ui: PolylineUI) -> None: def move_polyline_to_measure(cls, context: bpy.types.Context, input_ui: PolylineUI) -> None:
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
measurement_data = bpy.context.scene.BIMPolylineProperties.measurement_polyline.add() measurement_data = polyline_props.measurement_polyline.add()
measurement_type = bpy.context.scene.MeasureToolSettings.measurement_type measurement_type = tool.Project.get_measure_tool_settings().measurement_type
measurement_data.measurement_type = measurement_type measurement_data.measurement_type = measurement_type
if measurement_type == "AREA" and len(polyline_points) < 3: if measurement_type == "AREA" and len(polyline_points) < 3:
return return
+6 -1
View File
@@ -38,7 +38,7 @@ from pathlib import Path
from typing import Optional, Union, TYPE_CHECKING from typing import Optional, Union, TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from bonsai.bim.module.project.prop import BIMProjectProperties from bonsai.bim.module.project.prop import BIMProjectProperties, MeasureToolSettings
HiearchyDict = dict[ifcopenshell.entity_instance, "HiearchyDict"] HiearchyDict = dict[ifcopenshell.entity_instance, "HiearchyDict"]
@@ -48,6 +48,11 @@ class Project(bonsai.core.tool.Project):
def get_project_props(cls) -> BIMProjectProperties: def get_project_props(cls) -> BIMProjectProperties:
return bpy.context.scene.BIMProjectProperties return bpy.context.scene.BIMProjectProperties
@classmethod
def get_measure_tool_settings(cls) -> MeasureToolSettings:
assert (scene := bpy.context.scene)
return scene.MeasureToolSettings # pyright: ignore[reportAttributeAccessIssue]
@classmethod @classmethod
def append_all_types_from_template(cls, template: str) -> None: def append_all_types_from_template(cls, template: str) -> None:
# TODO refactor # TODO refactor
+2 -1
View File
@@ -316,7 +316,8 @@ class Raycast(bonsai.core.tool.Raycast):
except: except:
loc = Vector((0, 0, 0)) loc = Vector((0, 0, 0))
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
polyline_points = polyline_points[ polyline_points = polyline_points[
: len(polyline_points) - 1 : len(polyline_points) - 1
+27 -15
View File
@@ -49,6 +49,7 @@ if TYPE_CHECKING:
BIMAnimationProperties, BIMAnimationProperties,
BIMStatusProperties, BIMStatusProperties,
BIMTaskTreeProperties, BIMTaskTreeProperties,
BIMWorkCalendarProperties,
BIMWorkPlanProperties, BIMWorkPlanProperties,
BIMWorkScheduleProperties, BIMWorkScheduleProperties,
) )
@@ -83,6 +84,11 @@ class Sequence(bonsai.core.tool.Sequence):
assert (scene := bpy.context.scene) assert (scene := bpy.context.scene)
return scene.BIMWorkPlanProperties # pyright: ignore[reportAttributeAccessIssue] return scene.BIMWorkPlanProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod
def get_work_calendar_props(cls) -> BIMWorkCalendarProperties:
assert (scene := bpy.context.scene)
return scene.BIMWorkCalendarProperties # pyright: ignore[reportAttributeAccessIssue]
@classmethod @classmethod
def get_work_plan_attributes(cls) -> dict[str, Any]: def get_work_plan_attributes(cls) -> dict[str, Any]:
import bonsai.bim.module.sequence.helper as helper import bonsai.bim.module.sequence.helper as helper
@@ -411,7 +417,8 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def get_active_work_time(cls) -> ifcopenshell.entity_instance: def get_active_work_time(cls) -> ifcopenshell.entity_instance:
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkCalendarProperties.active_work_time_id) props = cls.get_work_calendar_props()
return tool.Ifc.get().by_id(props.active_work_time_id)
@classmethod @classmethod
def get_task_time(cls, task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]: def get_task_time(cls, task: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
@@ -571,32 +578,35 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def enable_editing_work_calendar_times(cls, work_calendar: ifcopenshell.entity_instance) -> None: def enable_editing_work_calendar_times(cls, work_calendar: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkCalendarProperties props = cls.get_work_calendar_props()
props.active_work_calendar_id = work_calendar.id() props.active_work_calendar_id = work_calendar.id()
props.editing_type = "WORKTIMES" props.editing_type = "WORKTIMES"
@classmethod @classmethod
def load_work_calendar_attributes(cls, work_calendar: ifcopenshell.entity_instance) -> dict[str, Any]: def load_work_calendar_attributes(cls, work_calendar: ifcopenshell.entity_instance) -> dict[str, Any]:
props = bpy.context.scene.BIMWorkCalendarProperties props = cls.get_work_calendar_props()
props.work_calendar_attributes.clear() props.work_calendar_attributes.clear()
return bonsai.bim.helper.import_attributes(work_calendar, props.work_calendar_attributes) return bonsai.bim.helper.import_attributes(work_calendar, props.work_calendar_attributes)
@classmethod @classmethod
def enable_editing_work_calendar(cls, work_calendar: ifcopenshell.entity_instance) -> None: def enable_editing_work_calendar(cls, work_calendar: ifcopenshell.entity_instance) -> None:
bpy.context.scene.BIMWorkCalendarProperties.active_work_calendar_id = work_calendar.id() props = cls.get_work_calendar_props()
bpy.context.scene.BIMWorkCalendarProperties.editing_type = "ATTRIBUTES" props.active_work_calendar_id = work_calendar.id()
props.editing_type = "ATTRIBUTES"
@classmethod @classmethod
def disable_editing_work_calendar(cls) -> None: def disable_editing_work_calendar(cls) -> None:
bpy.context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0 props = cls.get_work_calendar_props()
props.active_work_calendar_id = 0
@classmethod @classmethod
def get_work_calendar_attributes(cls) -> dict[str, Any]: def get_work_calendar_attributes(cls) -> dict[str, Any]:
return bonsai.bim.helper.export_attributes(bpy.context.scene.BIMWorkCalendarProperties.work_calendar_attributes) props = cls.get_work_calendar_props()
return bonsai.bim.helper.export_attributes(props.work_calendar_attributes)
@classmethod @classmethod
def load_work_time_attributes(cls, work_time: ifcopenshell.entity_instance) -> None: def load_work_time_attributes(cls, work_time: ifcopenshell.entity_instance) -> None:
props = bpy.context.scene.BIMWorkCalendarProperties props = cls.get_work_calendar_props()
props.work_time_attributes.clear() props.work_time_attributes.clear()
bonsai.bim.helper.import_attributes(work_time, props.work_time_attributes) bonsai.bim.helper.import_attributes(work_time, props.work_time_attributes)
@@ -642,7 +652,7 @@ class Sequence(bonsai.core.tool.Sequence):
for component in recurrence_pattern.MonthComponent or []: for component in recurrence_pattern.MonthComponent or []:
props.month_components[component - 1].is_specified = True props.month_components[component - 1].is_specified = True
props = bpy.context.scene.BIMWorkCalendarProperties props = cls.get_work_calendar_props()
initialise_recurrence_components(props) initialise_recurrence_components(props)
load_recurrence_pattern_data(work_time, props) load_recurrence_pattern_data(work_time, props)
props.active_work_time_id = work_time.id() props.active_work_time_id = work_time.id()
@@ -661,12 +671,12 @@ class Sequence(bonsai.core.tool.Sequence):
return True return True
return False return False
props = bpy.context.scene.BIMWorkCalendarProperties props = cls.get_work_calendar_props()
return bonsai.bim.helper.export_attributes(props.work_time_attributes, callback) return bonsai.bim.helper.export_attributes(props.work_time_attributes, callback)
@classmethod @classmethod
def get_recurrence_pattern_attributes(cls, recurrence_pattern): def get_recurrence_pattern_attributes(cls, recurrence_pattern):
props = bpy.context.scene.BIMWorkCalendarProperties props = props = cls.get_work_calendar_props()
attributes = { attributes = {
"Interval": props.interval if props.interval > 0 else None, "Interval": props.interval if props.interval > 0 else None,
"Occurrences": props.occurrences if props.occurrences > 0 else None, "Occurrences": props.occurrences if props.occurrences > 0 else None,
@@ -693,11 +703,12 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def disable_editing_work_time(cls) -> None: def disable_editing_work_time(cls) -> None:
bpy.context.scene.BIMWorkCalendarProperties.active_work_time_id = 0 props = cls.get_work_calendar_props()
props.active_work_time_id = 0
@classmethod @classmethod
def get_recurrence_pattern_times(cls) -> Union[tuple[datetime, datetime], None]: def get_recurrence_pattern_times(cls) -> Union[tuple[datetime, datetime], None]:
props = bpy.context.scene.BIMWorkCalendarProperties props = props = cls.get_work_calendar_props()
try: try:
start_time = parser.parse(props.start_time) start_time = parser.parse(props.start_time)
end_time = parser.parse(props.end_time) end_time = parser.parse(props.end_time)
@@ -707,8 +718,9 @@ class Sequence(bonsai.core.tool.Sequence):
@classmethod @classmethod
def reset_time_period(cls) -> None: def reset_time_period(cls) -> None:
bpy.context.scene.BIMWorkCalendarProperties.start_time = "" props = cls.get_work_calendar_props()
bpy.context.scene.BIMWorkCalendarProperties.end_time = "" props.start_time = ""
props.end_time = ""
@classmethod @classmethod
def enable_editing_task_calendar(cls, task: ifcopenshell.entity_instance) -> None: def enable_editing_task_calendar(cls, task: ifcopenshell.entity_instance) -> None:
+17 -10
View File
@@ -111,10 +111,11 @@ class Snap(bonsai.core.tool.Snap):
@classmethod @classmethod
def update_snapping_point(cls, snap_point, snap_type, snap_obj=None): def update_snapping_point(cls, snap_point, snap_type, snap_obj=None):
polyline_props = tool.Model.get_polyline_props()
try: try:
snap_vertex = bpy.context.scene.BIMPolylineProperties.snap_mouse_point[0] snap_vertex = polyline_props.snap_mouse_point[0]
except: except:
snap_vertex = bpy.context.scene.BIMPolylineProperties.snap_mouse_point.add() snap_vertex = polyline_props.snap_mouse_point.add()
snap_vertex.x = snap_point[0] snap_vertex.x = snap_point[0]
snap_vertex.y = snap_point[1] snap_vertex.y = snap_point[1]
@@ -127,14 +128,16 @@ class Snap(bonsai.core.tool.Snap):
@classmethod @classmethod
def clear_snapping_point(cls): def clear_snapping_point(cls):
bpy.context.scene.BIMPolylineProperties.snap_mouse_point.clear() polyline_props = tool.Model.get_polyline_props()
polyline_props.snap_mouse_point.clear()
@classmethod @classmethod
def update_snapping_ref(cls, snap_point, snap_type): def update_snapping_ref(cls, snap_point, snap_type):
polyline_props = tool.Model.get_polyline_props()
try: try:
snap_vertex = bpy.context.scene.BIMPolylineProperties.snap_mouse_ref[0] snap_vertex = polyline_props.snap_mouse_ref[0]
except: except:
snap_vertex = bpy.context.scene.BIMPolylineProperties.snap_mouse_ref.add() snap_vertex = polyline_props.snap_mouse_ref.add()
snap_vertex.x = snap_point[0] snap_vertex.x = snap_point[0]
snap_vertex.y = snap_point[1] snap_vertex.y = snap_point[1]
@@ -143,7 +146,8 @@ class Snap(bonsai.core.tool.Snap):
@classmethod @classmethod
def clear_snapping_ref(cls): def clear_snapping_ref(cls):
bpy.context.scene.BIMPolylineProperties.snap_mouse_ref.clear() polyline_props = tool.Model.get_polyline_props()
polyline_props.snap_mouse_ref.clear()
@classmethod @classmethod
def snap_on_axis(cls, intersection, tool_state): def snap_on_axis(cls, intersection, tool_state):
@@ -167,7 +171,8 @@ class Snap(bonsai.core.tool.Snap):
default_container_elevation = tool.Root.get_default_container_elevation() default_container_elevation = tool.Root.get_default_container_elevation()
else: else:
default_container_elevation = 0.0 default_container_elevation = 0.0
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if polyline_points: if polyline_points:
last_point_data = polyline_points[-1] last_point_data = polyline_points[-1]
@@ -263,7 +268,8 @@ class Snap(bonsai.core.tool.Snap):
intersections.append(tool.Cad.intersect_edge_plane(axis_start, axis_end, snap_point["point"], y_axis)) intersections.append(tool.Cad.intersect_edge_plane(axis_start, axis_end, snap_point["point"], y_axis))
intersections.append(tool.Cad.intersect_edge_plane(axis_start, axis_end, snap_point["point"], z_axis)) intersections.append(tool.Cad.intersect_edge_plane(axis_start, axis_end, snap_point["point"], z_axis))
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline polyline_props = tool.Model.get_polyline_props()
polyline_data = polyline_props.insertion_polyline
polyline_points = polyline_data[0].polyline_points if polyline_data else [] polyline_points = polyline_data[0].polyline_points if polyline_data else []
if polyline_points: if polyline_points:
last_point_data = polyline_points[-1] last_point_data = polyline_points[-1]
@@ -329,8 +335,9 @@ class Snap(bonsai.core.tool.Snap):
return plane_origin, plane_normal return plane_origin, plane_normal
# Polyline # Polyline
polyline_props = tool.Model.get_polyline_props()
try: try:
polyline_data = bpy.context.scene.BIMPolylineProperties.insertion_polyline[0] polyline_data = polyline_props.insertion_polyline[0]
polyline_points = polyline_data.polyline_points polyline_points = polyline_data.polyline_points
last_polyline_point = polyline_points[len(polyline_points) - 1] last_polyline_point = polyline_points[len(polyline_points) - 1]
except: except:
@@ -344,7 +351,7 @@ class Snap(bonsai.core.tool.Snap):
detected_snaps.append(point) detected_snaps.append(point)
# Measure # Measure
measure_data = context.scene.BIMPolylineProperties.measurement_polyline measure_data = polyline_props.measurement_polyline
for measure in measure_data: for measure in measure_data:
measure_points = measure.polyline_points measure_points = measure.polyline_points
snap_points = tool.Raycast.ray_cast_to_measure(context, event, measure_points) snap_points = tool.Raycast.ray_cast_to_measure(context, event, measure_points)
@@ -27,29 +27,28 @@ def assign_representation(
) -> None: ) -> None:
usecase = Usecase() usecase = Usecase()
usecase.file = file usecase.file = file
usecase.settings = {"product": product, "representation": representation} return usecase.execute(product, representation)
return usecase.execute()
class Usecase: class Usecase:
file: ifcopenshell.file file: ifcopenshell.file
settings: dict[str, Any] settings: dict[str, Any]
def execute(self) -> None: def execute(self, product: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance) -> None:
if self.settings["product"].is_a("IfcProduct"): if product.is_a("IfcProduct"):
product_type = ifcopenshell.util.element.get_type(self.settings["product"]) product_type = ifcopenshell.util.element.get_type(product)
if ( if (
product_type product_type
and product_type.RepresentationMaps and product_type.RepresentationMaps
and self.settings["representation"].RepresentationType != "MappedRepresentation" and representation.RepresentationType != "MappedRepresentation"
): ):
self.settings["product"] = product_type product = product_type
if self.settings["product"].is_a("IfcProduct"): if product.is_a("IfcProduct"):
self.assign_product_representation(self.settings["product"], self.settings["representation"]) self.assign_product_representation(product, representation)
elif self.settings["product"].is_a("IfcTypeProduct"): elif product.is_a("IfcTypeProduct"):
if self.settings["product"].RepresentationMaps: if product.RepresentationMaps:
maps = list(self.settings["product"].RepresentationMaps) maps = list(product.RepresentationMaps)
else: else:
maps = [] maps = []
self.zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0)) self.zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
@@ -60,22 +59,22 @@ class Usecase:
"IfcRepresentationMap", "IfcRepresentationMap",
**{ **{
"MappingOrigin": self.file.createIfcAxis2Placement3D(self.zero, self.z_axis, self.x_axis), "MappingOrigin": self.file.createIfcAxis2Placement3D(self.zero, self.z_axis, self.x_axis),
"MappedRepresentation": self.settings["representation"], "MappedRepresentation": representation,
} }
) )
) )
self.settings["product"].RepresentationMaps = maps product.RepresentationMaps = maps
if self.file.schema == "IFC2X3": if self.file.schema == "IFC2X3":
types = self.settings["product"].ObjectTypeOf types = product.ObjectTypeOf
else: else:
types = self.settings["product"].Types types = product.Types
if types: if types:
for element in types[0].RelatedObjects: for element in types[0].RelatedObjects:
mapped_representation = ifcopenshell.api.geometry.map_representation( mapped_representation = ifcopenshell.api.geometry.map_representation(
self.file, representation=self.settings["representation"] self.file, representation=representation
) )
self.assign_product_representation(element, mapped_representation) self.assign_product_representation(element, mapped_representation)
ifcopenshell.api.owner.update_owner_history(self.file, element=self.settings["product"]) ifcopenshell.api.owner.update_owner_history(self.file, element=product)
def assign_product_representation( def assign_product_representation(
self, product: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance self, product: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
@@ -30,6 +30,8 @@ def assign_declaration(
) -> Union[ifcopenshell.entity_instance, None]: ) -> Union[ifcopenshell.entity_instance, None]:
"""Declares the list of elements to the project """Declares the list of elements to the project
Feature was added in IFC4.
All data in a model must be directly or indirectly related to the All data in a model must be directly or indirectly related to the
project. Most data is indirectly related, existing instead within the project. Most data is indirectly related, existing instead within the
spatial decomposition tree. Other data, such as types, may be declared spatial decomposition tree. Other data, such as types, may be declared
@@ -82,21 +84,16 @@ def assign_declaration(
# All done, just for fun let's save our asset library to disk for later use. # All done, just for fun let's save our asset library to disk for later use.
library.write("/path/to/my-library.ifc") library.write("/path/to/my-library.ifc")
""" """
settings = {
"definitions": definitions,
"relating_context": relating_context,
}
relating_context = settings["relating_context"]
all_declares = relating_context.Declares all_declares = relating_context.Declares
definitions = set(settings["definitions"]) definitions_set = set(definitions)
previous_declares_rels: set[ifcopenshell.entity_instance] = set() previous_declares_rels: set[ifcopenshell.entity_instance] = set()
objects_without_contexts: list[ifcopenshell.entity_instance] = [] objects_without_contexts: list[ifcopenshell.entity_instance] = []
objects_with_contexts: list[ifcopenshell.entity_instance] = [] objects_with_contexts: list[ifcopenshell.entity_instance] = []
# check if there is anything to change # check if there is anything to change
for definition in definitions: for definition in definitions_set:
has_context = getattr(definition, "HasContext", None) has_context = getattr(definition, "HasContext", None)
if has_context is None: if has_context is None:
continue continue
@@ -20,7 +20,6 @@ import ifcopenshell.api.control
import ifcopenshell.api.root import ifcopenshell.api.root
import ifcopenshell.api.nest import ifcopenshell.api.nest
import ifcopenshell import ifcopenshell
import ifcopenshell.guid
from typing import Optional from typing import Optional
@@ -23,14 +23,12 @@ import ifcopenshell.api.owner
import ifcopenshell.api.sequence import ifcopenshell.api.sequence
import ifcopenshell.util.date import ifcopenshell.util.date
import ifcopenshell.util.element import ifcopenshell.util.element
import ifcopenshell.util.sequence
from typing import Union, Any
# TODO: inconsistent name with other copy_xxx api methods. # TODO: inconsistent name with other copy_xxx api methods.
def duplicate_task( def duplicate_task(
file: ifcopenshell.file, task: ifcopenshell.entity_instance file: ifcopenshell.file, task: ifcopenshell.entity_instance
) -> Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]: ) -> tuple[list[ifcopenshell.entity_instance], list[ifcopenshell.entity_instance]]:
"""Duplicates a task in the project """Duplicates a task in the project
The following relationships are also duplicated: The following relationships are also duplicated:
@@ -53,24 +51,27 @@ def duplicate_task(
""" """
usecase = Usecase() usecase = Usecase()
usecase.file = file usecase.file = file
usecase.settings = {"task": task} return usecase.execute(task)
return usecase.execute()
class Usecase: class Usecase:
file: ifcopenshell.file file: ifcopenshell.file
settings: dict[str, list[ifcopenshell.entity_instance]] current: list[ifcopenshell.entity_instance]
duplicate: list[ifcopenshell.entity_instance]
def execute(self): def execute(
self.tracker = {"current": [], "duplicate": []} self, task: ifcopenshell.entity_instance
self.duplicate_task(self.settings["task"]) ) -> tuple[list[ifcopenshell.entity_instance], list[ifcopenshell.entity_instance]]:
self.copy_sequence_relationship(self.tracker["current"], self.tracker["duplicate"]) self.current = []
return self.tracker["current"], self.tracker["duplicate"] self.duplicate = []
self.duplicate_task(task)
self.copy_sequence_relationship()
return self.current, self.duplicate
def duplicate_task(self, task: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance: def duplicate_task(self, task: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
new_task = ifcopenshell.util.element.copy_deep(self.file, task) new_task = ifcopenshell.util.element.copy_deep(self.file, task)
self.tracker["current"].append(task) self.current.append(task)
self.tracker["duplicate"].append(new_task) self.duplicate.append(new_task)
self.copy_indirect_attributes(task, new_task) self.copy_indirect_attributes(task, new_task)
return new_task return new_task
@@ -118,7 +119,9 @@ class Usecase:
new_value.append(to_element) new_value.append(to_element)
inverse[i] = new_value inverse[i] = new_value
def copy_sequence_relationship(self, original_tasks, duplicated_tasks): def copy_sequence_relationship(self) -> None:
original_tasks = self.current
duplicated_tasks = self.duplicate
for i, original_task in enumerate(original_tasks): for i, original_task in enumerate(original_tasks):
for inverse in self.file.get_inverse(original_task): for inverse in self.file.get_inverse(original_task):
if inverse.is_a("IfcRelSequence") and ( if inverse.is_a("IfcRelSequence") and (
@@ -202,7 +202,7 @@ def get_root_cost_items(cost_schedule: ifcopenshell.entity_instance) -> list[ifc
def get_all_nested_cost_items( def get_all_nested_cost_items(
cost_item: ifcopenshell.entity_instance, cost_item: ifcopenshell.entity_instance,
) -> Generator[ifcopenshell.entity_instance, None, None]: ) -> Generator[ifcopenshell.entity_instance]:
for cost_item in get_nested_cost_items(cost_item): for cost_item in get_nested_cost_items(cost_item):
yield cost_item yield cost_item
yield from get_all_nested_cost_items(cost_item) yield from get_all_nested_cost_items(cost_item)
@@ -217,7 +217,7 @@ def get_nested_cost_items(cost_item: ifcopenshell.entity_instance, is_deep=False
def get_schedule_cost_items( def get_schedule_cost_items(
cost_schedule: ifcopenshell.entity_instance, cost_schedule: ifcopenshell.entity_instance,
) -> Generator[ifcopenshell.entity_instance, None, None]: ) -> Generator[ifcopenshell.entity_instance]:
"""Get all cost schedule cost items, including the nested ones.""" """Get all cost schedule cost items, including the nested ones."""
for cost_item in get_root_cost_items(cost_schedule): for cost_item in get_root_cost_items(cost_schedule):
yield cost_item yield cost_item