This commit is contained in:
Andrej730
2025-04-21 12:43:08 +05:00
parent 558bda1aea
commit 476604202a
11 changed files with 127 additions and 65 deletions
@@ -38,18 +38,17 @@ def depsgraph_update_pre_handler(scene):
def set_active_camera_resolution(scene: bpy.types.Scene) -> None: def set_active_camera_resolution(scene: bpy.types.Scene) -> None:
props = tool.Drawing.get_document_props() props = tool.Drawing.get_document_props()
if not scene.camera or "/" not in scene.camera.name or not props.drawings: camera_obj = scene.camera
if not camera_obj or "/" not in camera_obj.name or not props.drawings:
return return
assert isinstance(scene.camera.data, bpy.types.Camera) assert isinstance((camera := camera_obj.data), bpy.types.Camera)
props = scene.camera.data.BIMCameraProperties props = tool.Drawing.get_camera_props(camera)
ortho_scale = max((props.width, props.height)) ortho_scale = max((props.width, props.height))
aspect_ratio = props.width / props.height aspect_ratio = props.width / props.height
if (scene.camera.data.ortho_scale != ortho_scale) or ( if (camera.ortho_scale != ortho_scale) or (scene.render.resolution_x / scene.render.resolution_y != aspect_ratio):
scene.render.resolution_x / scene.render.resolution_y != aspect_ratio camera.ortho_scale = ortho_scale
):
scene.camera.data.ortho_scale = ortho_scale
diagram_scale = tool.Drawing.get_diagram_scale(scene.camera) diagram_scale = tool.Drawing.get_diagram_scale(camera_obj)
scale_ratio = tool.Drawing.get_scale_ratio(diagram_scale["Scale"]) scale_ratio = tool.Drawing.get_scale_ratio(diagram_scale["Scale"])
if props.width > props.height: if props.width > props.height:
@@ -61,5 +60,5 @@ def set_active_camera_resolution(scene: bpy.types.Scene) -> None:
raster_x = ortho_scale * aspect_ratio * scale_ratio * props.dpi / 0.0254 raster_x = ortho_scale * aspect_ratio * scale_ratio * props.dpi / 0.0254
raster_y = ortho_scale * scale_ratio * props.dpi / 0.0254 raster_y = ortho_scale * scale_ratio * props.dpi / 0.0254
scene.render.resolution_x = scene.camera.data.BIMCameraProperties.raster_x = int(raster_x) scene.render.resolution_x = props.raster_x = int(raster_x)
scene.render.resolution_y = scene.camera.data.BIMCameraProperties.raster_y = int(raster_y) scene.render.resolution_y = props.raster_y = int(raster_y)
@@ -413,12 +413,13 @@ def get_project_collection(scene):
return colls[0] return colls[0]
def parse_diagram_scale(camera): def parse_diagram_scale(camera: bpy.types.Camera) -> float:
"""Returns numeric value of scale""" """Returns numeric value of scale"""
if camera.BIMCameraProperties.diagram_scale == "CUSTOM": props = tool.Drawing.get_camera_props(camera)
_, fraction = camera.BIMCameraProperties.custom_diagram_scale.split("|") if props.diagram_scale == "CUSTOM":
_, fraction = props.custom_diagram_scale.split("|")
else: else:
_, fraction = camera.BIMCameraProperties.diagram_scale.split("|") _, fraction = props.diagram_scale.split("|")
numerator, denominator = fraction.split("/") numerator, denominator = fraction.split("/")
return float(numerator) / float(denominator) return float(numerator) / float(denominator)
@@ -431,12 +432,11 @@ def ortho_view_frame(
Similar to `bpy.types.Camera.view_frame` Similar to `bpy.types.Camera.view_frame`
:arg camera: camera of drawing :arg camera: camera of drawing
:type camera: bpy.types.Camera + BIMCameraProperties
:arg margin: margins, in scene units :arg margin: margins, in scene units
:type margin: float
:return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates :return: (xmin, xmax, ymin, ymax, zmin, zmax) in local camera coordinates
""" """
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x props = tool.Drawing.get_camera_props(camera)
aspect = props.raster_y / props.raster_x
size = camera.ortho_scale size = camera.ortho_scale
hwidth = size * 0.5 hwidth = size * 0.5
hheight = size * 0.5 * aspect hheight = size * 0.5 * aspect
@@ -228,7 +228,9 @@ class CreateDrawing(bpy.types.Operator):
if not tool.Drawing.is_drawing_active(): if not tool.Drawing.is_drawing_active():
cls.poll_message_set("No active drawing.") cls.poll_message_set("No active drawing.")
return False return False
if context.scene.camera.data.BIMCameraProperties.linework_mode == "FREESTYLE" and not hasattr( assert context.scene
assert (camera_obj := context.scene.camera)
if tool.Drawing.get_camera_props(camera_obj).linework_mode == "FREESTYLE" and not hasattr(
context.scene, "svg_export" context.scene, "svg_export"
): ):
cls.poll_message_set( cls.poll_message_set(
@@ -248,6 +250,7 @@ class CreateDrawing(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.props = tool.Drawing.get_document_props() self.props = tool.Drawing.get_document_props()
assert context.scene and context.scene.camera
active_drawing_id = tool.Blender.get_ifc_definition_id(context.scene.camera) active_drawing_id = tool.Blender.get_ifc_definition_id(context.scene.camera)
if self.print_all: if self.print_all:
@@ -262,13 +265,14 @@ class CreateDrawing(bpy.types.Operator):
bpy.ops.bim.activate_drawing(drawing=drawing_id, should_view_from_camera=False) bpy.ops.bim.activate_drawing(drawing=drawing_id, should_view_from_camera=False)
self.camera = context.scene.camera self.camera = context.scene.camera
self.camera_element = tool.Ifc.get_entity(self.camera) assert (camera_element := tool.Ifc.get_entity(self.camera))
self.camera_element = camera_element
self.camera_document = tool.Drawing.get_drawing_document(self.camera_element) self.camera_document = tool.Drawing.get_drawing_document(self.camera_element)
self.file = tool.Ifc.get() self.file = tool.Ifc.get()
with profile("Drawing generation process"): with profile("Drawing generation process"):
with profile("Initialize drawing generation process"): with profile("Initialize drawing generation process"):
self.cprops = self.camera.data.BIMCameraProperties self.cprops = tool.Drawing.get_camera_props(self.camera)
self.drawing = self.file.by_id(drawing_id) self.drawing = self.file.by_id(drawing_id)
self.drawing_name = self.drawing.Name self.drawing_name = self.drawing.Name
self.metadata = tool.Drawing.get_drawing_metadata(self.camera_element) self.metadata = tool.Drawing.get_drawing_metadata(self.camera_element)
@@ -297,11 +301,11 @@ class CreateDrawing(bpy.types.Operator):
with profile("Generate linework"): with profile("Generate linework"):
if tool.Drawing.is_camera_orthographic(): if tool.Drawing.is_camera_orthographic():
if self.camera.data.BIMCameraProperties.linework_mode == "OPENCASCADE": if self.cprops.linework_mode == "OPENCASCADE":
linework_svg = self.generate_linework(context) linework_svg = self.generate_linework(context)
elif self.camera.data.BIMCameraProperties.linework_mode == "FREESTYLE": elif self.cprops.linework_mode == "FREESTYLE":
linework_svg = self.generate_freestyle_linework(context) linework_svg = self.generate_freestyle_linework(context)
elif self.camera.data.BIMCameraProperties.linework_mode == "FREESTYLE": elif self.cprops.linework_mode == "FREESTYLE":
linework_svg = self.generate_freestyle_linework(context) linework_svg = self.generate_freestyle_linework(context)
with profile("Generate annotation"): with profile("Generate annotation"):
@@ -876,19 +880,19 @@ class CreateDrawing(bpy.types.Operator):
return svg_path return svg_path
if self.camera.data.BIMCameraProperties.cut_mode == "BISECT": if self.cprops.cut_mode == "BISECT":
self.remove_cut_linework(root) self.remove_cut_linework(root)
self.generate_bisect_linework(context, root) self.generate_bisect_linework(context, root)
self.generate_wall_layers(context, root) self.generate_wall_layers(context, root)
self.merge_linework_and_add_metadata(root) self.merge_linework_and_add_metadata(root)
self.move_elements_to_top(root) self.move_elements_to_top(root)
elif self.camera.data.BIMCameraProperties.cut_mode == "OPENCASCADE": elif self.cprops.cut_mode == "OPENCASCADE":
self.move_projection_to_bottom(root) self.move_projection_to_bottom(root)
self.generate_wall_layers(context, root) self.generate_wall_layers(context, root)
self.merge_linework_and_add_metadata(root) self.merge_linework_and_add_metadata(root)
self.move_elements_to_top(root) self.move_elements_to_top(root)
if self.camera.data.BIMCameraProperties.fill_mode == "SHAPELY": if self.cprops.fill_mode == "SHAPELY":
# shapely variant # shapely variant
group = root.find("{http://www.w3.org/2000/svg}g") group = root.find("{http://www.w3.org/2000/svg}g")
@@ -960,7 +964,7 @@ class CreateDrawing(bpy.types.Operator):
path.set("class", " ".join(list(classes))) path.set("class", " ".join(list(classes)))
group.insert(0, path) group.insert(0, path)
if self.camera.data.BIMCameraProperties.fill_mode == "SVGFILL": if self.cprops.fill_mode == "SVGFILL":
results = etree.tostring(root).decode("utf8") results = etree.tostring(root).decode("utf8")
svg_data_1 = results svg_data_1 = results
from xml.dom.minidom import parseString from xml.dom.minidom import parseString
@@ -1449,10 +1453,9 @@ class CreateDrawing(bpy.types.Operator):
g.set("class", " ".join(list(classes))) g.set("class", " ".join(list(classes)))
group.append(g) group.append(g)
def drawing_to_model_co(self, x, y): def drawing_to_model_co(self, x: float, y: float) -> Vector:
camera_xy = np.array((x, -y)) / self.scale / 1000 camera_xy = np.array((x, -y)) / self.scale / 1000
camera_props = self.camera.data.BIMCameraProperties camera_xy += np.array((self.cprops.width / -2, self.cprops.height / 2)) # top left offset
camera_xy += np.array((camera_props.width / -2, camera_props.height / 2)) # top left offset
return self.camera.matrix_world @ Vector(camera_xy).to_3d() return self.camera.matrix_world @ Vector(camera_xy).to_3d()
def pythonize(self, arr): def pythonize(self, arr):
@@ -2158,7 +2161,7 @@ class ActivateDrawingBase:
# Save drawing bounds to the .ifc file # Save drawing bounds to the .ifc file
camera = context.scene.camera camera = context.scene.camera
camera_props = camera.data.BIMCameraProperties camera_props = tool.Drawing.get_camera_props(camera)
if camera_props.update_representation(camera): if camera_props.update_representation(camera):
bpy.ops.bim.update_representation(obj=camera.name, ifc_representation_class="") bpy.ops.bim.update_representation(obj=camera.name, ifc_representation_class="")
# See 6452 and 6478. # See 6452 and 6478.
@@ -2298,7 +2301,8 @@ class ReloadDrawingStyles(bpy.types.Operator):
if not DrawingsData.is_loaded: if not DrawingsData.is_loaded:
DrawingsData.load() DrawingsData.load()
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"] drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
camera_props = context.scene.camera.data.BIMCameraProperties assert context.scene and (camera := context.scene.camera)
camera_props = tool.Drawing.get_camera_props(camera)
# added this part as a temporary fallback # added this part as a temporary fallback
# TODO: should remove it a bit later when projects get more accommodated # TODO: should remove it a bit later when projects get more accommodated
@@ -2365,12 +2369,14 @@ class AddDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
bl_options = {"REGISTER", "UNDO"} bl_options = {"REGISTER", "UNDO"}
def execute(self, context): def execute(self, context):
assert context.scene and (camera_obj := context.scene.camera)
props = tool.Drawing.get_document_props() props = tool.Drawing.get_document_props()
drawing_styles = props.drawing_styles drawing_styles = props.drawing_styles
new = drawing_styles.add() new = drawing_styles.add()
# drawing style is saved to ifc on rename # drawing style is saved to ifc on rename
new.name = tool.Blender.ensure_unique_name("New Drawing Style", drawing_styles) new.name = tool.Blender.ensure_unique_name("New Drawing Style", drawing_styles)
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = len(drawing_styles) - 1 camera_props = tool.Drawing.get_camera_props(camera_obj)
camera_props.active_drawing_style_index = len(drawing_styles) - 1
return {"FINISHED"} return {"FINISHED"}
@@ -2381,9 +2387,11 @@ class RemoveDrawingStyle(bpy.types.Operator, tool.Ifc.Operator):
index: bpy.props.IntProperty() index: bpy.props.IntProperty()
def execute(self, context): def execute(self, context):
assert context.scene and (camera_obj := context.scene.camera)
props = tool.Drawing.get_document_props() props = tool.Drawing.get_document_props()
props.drawing_styles.remove(self.index) props.drawing_styles.remove(self.index)
context.scene.camera.data.BIMCameraProperties.active_drawing_style_index = max(self.index - 1, 0) camera_props = tool.Drawing.get_camera_props(camera_obj)
camera_props.active_drawing_style_index = max(self.index - 1, 0)
bpy.ops.bim.save_drawing_styles_data() bpy.ops.bim.save_drawing_styles_data()
return {"FINISHED"} return {"FINISHED"}
+26 -1
View File
@@ -522,9 +522,34 @@ class BIMCameraProperties(PropertyGroup):
exclude_filter_groups: CollectionProperty(type=BIMFilterGroup, name="Exclude Filter") exclude_filter_groups: CollectionProperty(type=BIMFilterGroup, name="Exclude Filter")
update_props: BoolProperty(name="Enable Props Auto Update", default=True) update_props: BoolProperty(name="Enable Props Auto Update", default=True)
if TYPE_CHECKING:
linework_mode: Literal["OPENCASCADE", "FREESTYLE"]
fill_mode: Literal["NONE", "SHAPELY", "SVGFILL"]
cut_mode: Literal["BISECT", "OPENCASCADE"]
has_underlay: bool
has_linework: bool
has_annotation: bool
representation: str
view_name: str
diagram_scale: str
custom_scale_numerator: str
custom_scale_denominator: str
raster_x: int
raster_y: int
dpi: int
width: float
height: float
is_nts: bool
active_drawing_style_index: int
filter_mode: str
include_filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]
exclude_filter_groups: bpy.types.bpy_prop_collection_idprop[BIMFilterGroup]
update_props: bool
# For now, this JSON dump are all the parameters that determine a camera's "Block representation" # For now, this JSON dump are all the parameters that determine a camera's "Block representation"
# By checking this, you will know whether or not the camera IFC representation needs to be refreshed # By checking this, you will know whether or not the camera IFC representation needs to be refreshed
def update_representation(self, obj): def update_representation(self, obj: bpy.types.Object) -> bool:
assert isinstance(obj.data, bpy.types.Camera)
representation = json.dumps( representation = json.dumps(
{ {
"matrix": [list(x) for x in obj.matrix_world], "matrix": [list(x) for x in obj.matrix_world],
@@ -1192,6 +1192,9 @@ class LinkIfc(bpy.types.Operator, ImportHelper):
filepath: str filepath: str
files: list[bpy.types.OperatorFileListElement] files: list[bpy.types.OperatorFileListElement]
directory: str directory: str
filter_glob: str
use_relative_path: bool
use_cache: bool
def draw(self, context): def draw(self, context):
pprops = tool.Project.get_project_props() pprops = tool.Project.get_project_props()
+9 -9
View File
@@ -82,7 +82,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def activate_camera(cls, obj: bpy.types.Object) -> None: def activate_camera(cls, obj: bpy.types.Object) -> None:
area = tool.Blender.get_view3d_area() area = cls.get_view3d_area()
is_local_view = area.spaces[0].local_view is not None is_local_view = area.spaces[0].local_view is not None
if is_local_view: if is_local_view:
@@ -206,7 +206,7 @@ class Blender(bonsai.core.tool.Blender):
if context is None: if context is None:
context = bpy.context context = bpy.context
if obj_type == "Object": if obj_type == "Object":
props = tool.Blender.get_object_bim_props(bpy.data.objects[obj]) props = cls.get_object_bim_props(bpy.data.objects[obj])
return props.ifc_definition_id return props.ifc_definition_id
elif obj_type == "Material": elif obj_type == "Material":
props = tool.Material.get_material_props() props = tool.Material.get_material_props()
@@ -239,7 +239,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def is_ifc_object(cls, obj: bpy.types.Object) -> bool: def is_ifc_object(cls, obj: bpy.types.Object) -> bool:
props = tool.Blender.get_object_bim_props(obj) props = cls.get_object_bim_props(obj)
return bool(props.ifc_definition_id) return bool(props.ifc_definition_id)
@classmethod @classmethod
@@ -338,7 +338,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def set_viewport_tool(cls, tool_name: str) -> None: def set_viewport_tool(cls, tool_name: str) -> None:
with bpy.context.temp_override(**tool.Blender.get_viewport_context()): with bpy.context.temp_override(**cls.get_viewport_context()):
bpy.ops.wm.tool_set_by_id(name=tool_name) bpy.ops.wm.tool_set_by_id(name=tool_name)
@classmethod @classmethod
@@ -398,7 +398,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def update_viewport(cls) -> None: def update_viewport(cls) -> None:
tool.Blender.get_viewport_context()["area"].tag_redraw() cls.get_viewport_context()["area"].tag_redraw()
@classmethod @classmethod
def force_depsgraph_update(cls) -> None: def force_depsgraph_update(cls) -> None:
@@ -711,7 +711,7 @@ class Blender(bonsai.core.tool.Blender):
False if enum is still invalid (as there no enum items) False if enum is still invalid (as there no enum items)
and update callback was not triggered (may need to trigger it manually). and update callback was not triggered (may need to trigger it manually).
""" """
current_value = tool.Blender.get_enum_safe(props, prop_name) current_value = cls.get_enum_safe(props, prop_name)
if current_value is not None: if current_value is not None:
# Value is valid, just trigger the update callback. # Value is valid, just trigger the update callback.
setattr(props, prop_name, current_value) setattr(props, prop_name, current_value)
@@ -958,7 +958,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def get_layer_collection(cls, collection: bpy.types.Collection) -> Union[bpy.types.LayerCollection, None]: def get_layer_collection(cls, collection: bpy.types.Collection) -> Union[bpy.types.LayerCollection, None]:
project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0]) project = tool.Ifc.get_object(tool.Ifc.get().by_type("IfcProject")[0])
project_collection = tool.Blender.get_object_bim_props(project).collection project_collection = cls.get_object_bim_props(project).collection
for layer_collection in bpy.context.view_layer.layer_collection.children: for layer_collection in bpy.context.view_layer.layer_collection.children:
if layer_collection.collection == project_collection: if layer_collection.collection == project_collection:
for layer_collection2 in layer_collection.children: for layer_collection2 in layer_collection.children:
@@ -1564,7 +1564,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def get_user_data_dir(cls) -> Path: def get_user_data_dir(cls) -> Path:
props = tool.Blender.get_bim_props() props = cls.get_bim_props()
return Path(props.data_dir) return Path(props.data_dir)
@classmethod @classmethod
@@ -1649,7 +1649,7 @@ class Blender(bonsai.core.tool.Blender):
@classmethod @classmethod
def get_ifc_definition_id(cls, obj: IFC_CONNECTED_TYPE) -> int: def get_ifc_definition_id(cls, obj: IFC_CONNECTED_TYPE) -> int:
if isinstance(obj, bpy.types.Object): if isinstance(obj, bpy.types.Object):
return tool.Blender.get_object_bim_props(obj).ifc_definition_id return cls.get_object_bim_props(obj).ifc_definition_id
return tool.Style.get_material_style_props(obj).ifc_definition_id return tool.Style.get_material_style_props(obj).ifc_definition_id
@classmethod @classmethod
+21 -4
View File
@@ -55,7 +55,13 @@ from typing import Optional, Union, Iterable, Any, Literal, Sequence, TYPE_CHECK
from pathlib import Path from pathlib import Path
if TYPE_CHECKING: if TYPE_CHECKING:
from bonsai.bim.module.drawing.prop import DocProperties, Sheet, BIMAnnotationProperties, BIMTextProperties from bonsai.bim.module.drawing.prop import (
DocProperties,
Sheet,
BIMAnnotationProperties,
BIMTextProperties,
BIMCameraProperties,
)
from bonsai.bim.module.drawing.prop import Drawing as DrawingProperties from bonsai.bim.module.drawing.prop import Drawing as DrawingProperties
@@ -106,6 +112,17 @@ class Drawing(bonsai.core.tool.Drawing):
def get_text_props(cls, obj: bpy.types.Object) -> BIMTextProperties: def get_text_props(cls, obj: bpy.types.Object) -> BIMTextProperties:
return obj.BIMTextProperties return obj.BIMTextProperties
@classmethod
def get_camera_props(cls, camera: Union[bpy.types.Object, bpy.types.Camera]) -> BIMCameraProperties:
"""
:param camera: Camera object or camera.
"""
if isinstance(camera, bpy.types.Camera):
data = camera
else:
assert isinstance(data := camera.data, bpy.types.Camera)
return data.BIMCameraProperties
@classmethod @classmethod
def canonicalise_class_name(cls, name: str) -> str: def canonicalise_class_name(cls, name: str) -> str:
return re.sub("[^0-9a-zA-Z]+", "", name) return re.sub("[^0-9a-zA-Z]+", "", name)
@@ -780,7 +797,7 @@ class Drawing(bonsai.core.tool.Drawing):
from bonsai.bim.module.drawing.prop import get_diagram_scales from bonsai.bim.module.drawing.prop import get_diagram_scales
# Temporarily clear the definition id to prevent prop update callbacks to IFC. # Temporarily clear the definition id to prevent prop update callbacks to IFC.
camera_props = camera.BIMCameraProperties camera_props = tool.Drawing.get_camera_props(camera)
update_props = camera_props.update_props update_props = camera_props.update_props
camera_props.update_props = False camera_props.update_props = False
@@ -2199,8 +2216,8 @@ class Drawing(bonsai.core.tool.Drawing):
return float(numerator) / float(denominator) return float(numerator) / float(denominator)
@classmethod @classmethod
def get_diagram_scale(cls, obj: bpy.types.Object) -> dict[str, float]: def get_diagram_scale(cls, obj: bpy.types.Object) -> dict[str, str]:
props = obj.data.BIMCameraProperties props = cls.get_camera_props(obj)
scale = props.diagram_scale scale = props.diagram_scale
if scale != "CUSTOM": if scale != "CUSTOM":
human_scale, scale = scale.split("|") human_scale, scale = scale.split("|")
+5 -4
View File
@@ -839,13 +839,14 @@ class Loader(bonsai.core.tool.Loader):
camera = bpy.data.cameras.new(tool.Loader.get_mesh_name_from_shape(geometry)) camera = bpy.data.cameras.new(tool.Loader.get_mesh_name_from_shape(geometry))
camera.type = camera_type camera.type = camera_type
camera.show_limits = True camera.show_limits = True
props = tool.Drawing.get_camera_props(camera)
if camera_type == "ORTHO": if camera_type == "ORTHO":
camera.clip_start = 0.002 # Technically 0, but Blender doesn't allow this, so 2mm it is! camera.clip_start = 0.002 # Technically 0, but Blender doesn't allow this, so 2mm it is!
camera.clip_end = depth camera.clip_end = depth
camera.BIMCameraProperties.width = width props.width = width
camera.BIMCameraProperties.height = height props.height = height
elif camera_type == "PERSP": elif camera_type == "PERSP":
abs_min_z = abs(min(z)) abs_min_z = abs(min(z))
abs_max_z = abs(max(z)) abs_max_z = abs(max(z))
@@ -853,8 +854,8 @@ class Loader(bonsai.core.tool.Loader):
camera.clip_end = abs_min_z camera.clip_end = abs_min_z
max_res = 1000 max_res = 1000
camera.BIMCameraProperties.width = width props.width = width
camera.BIMCameraProperties.height = height props.height = height
if width > height: if width > height:
fov = 2 * atan(width / (2 * abs_min_z)) fov = 2 * atan(width / (2 * abs_min_z))
+4 -2
View File
@@ -51,9 +51,11 @@ class Search(bonsai.core.tool.Search):
elif module == "diff": elif module == "diff":
return tool.Blender.get_diff_props().filter_groups return tool.Blender.get_diff_props().filter_groups
elif module == "drawing_include": elif module == "drawing_include":
return bpy.context.scene.camera.data.BIMCameraProperties.include_filter_groups assert (scene := bpy.context.scene) and (camera_obj := (scene.camera))
return tool.Drawing.get_camera_props(camera_obj).include_filter_groups
elif module == "drawing_exclude": elif module == "drawing_exclude":
return bpy.context.scene.camera.data.BIMCameraProperties.exclude_filter_groups assert (scene := bpy.context.scene) and (camera_obj := (scene.camera))
return tool.Drawing.get_camera_props(camera_obj).exclude_filter_groups
elif module.startswith("clash"): elif module.startswith("clash"):
_, clash_set_index, ab, clash_source_index = module.split("_") _, clash_set_index, ab, clash_source_index = module.split("_")
props = tool.Clash.get_clash_props() props = tool.Clash.get_clash_props()
@@ -28,6 +28,7 @@ from typing import Union, Optional, Literal, Any, TYPE_CHECKING
from ifcopenshell.util.shape_builder import ifc_safe_vector_type, VectorType from ifcopenshell.util.shape_builder import ifc_safe_vector_type, VectorType
if TYPE_CHECKING: if TYPE_CHECKING:
import bonsai.tool as tool
from bonsai.bim.module.geometry.helper import Helper from bonsai.bim.module.geometry.helper import Helper
@@ -81,9 +82,11 @@ def add_representation(
""" """
# lazy import Helper to avoid circular import # lazy import Helper to avoid circular import
if "Helper" not in globals(): if "Helper" not in globals():
import bonsai.tool as tool
from bonsai.bim.module.geometry.helper import Helper from bonsai.bim.module.geometry.helper import Helper
globals()["Helper"] = Helper globals()["Helper"] = Helper
globals()["tool"] = tool
usecase = Usecase() usecase = Usecase()
# TODO: This usecase currently depends on Blender's data model # TODO: This usecase currently depends on Blender's data model
@@ -111,7 +114,7 @@ class Usecase:
settings: dict[str, Any] settings: dict[str, Any]
ifc_vertices: list[ifcopenshell.entity_instance] ifc_vertices: list[ifcopenshell.entity_instance]
coordinate_offset: Union[npt.NDArray[np.float64], None] coordinate_offset: Union[npt.NDArray[np.float64], None]
geometry: Union[bpy.types.Mesh, bpy.types.Curve] geometry: Union[bpy.types.Mesh, bpy.types.Curve, bpy.types.Camera]
blender_object: bpy.types.Object blender_object: bpy.types.Object
def execute(self) -> Union[ifcopenshell.entity_instance, None]: def execute(self) -> Union[ifcopenshell.entity_instance, None]:
@@ -319,8 +322,10 @@ class Usecase:
return self.create_mesh_representation() return self.create_mesh_representation()
def create_camera_block_representation(self) -> ifcopenshell.entity_instance: def create_camera_block_representation(self) -> ifcopenshell.entity_instance:
raster_x = self.settings["geometry"].BIMCameraProperties.raster_x assert isinstance(self.geometry, bpy.types.Camera)
raster_y = self.settings["geometry"].BIMCameraProperties.raster_y props = tool.Drawing.get_camera_props(self.geometry)
raster_x = props.raster_x
raster_y = props.raster_y
if self.is_camera_landscape(): if self.is_camera_landscape():
width = self.settings["geometry"].ortho_scale width = self.settings["geometry"].ortho_scale
@@ -347,8 +352,10 @@ class Usecase:
) )
def create_camera_pyramid_representation(self) -> ifcopenshell.entity_instance: def create_camera_pyramid_representation(self) -> ifcopenshell.entity_instance:
raster_x = self.settings["geometry"].BIMCameraProperties.raster_x assert isinstance(self.geometry, bpy.types.Camera)
raster_y = self.settings["geometry"].BIMCameraProperties.raster_y props = tool.Drawing.get_camera_props(self.geometry)
raster_x = props.raster_x
raster_y = props.raster_y
fov = self.settings["geometry"].angle fov = self.settings["geometry"].angle
clip_end = self.settings["geometry"].clip_end clip_end = self.settings["geometry"].clip_end
@@ -389,10 +396,9 @@ class Usecase:
) )
def is_camera_landscape(self) -> bool: def is_camera_landscape(self) -> bool:
return ( assert isinstance(self.geometry, bpy.types.Camera)
self.settings["geometry"].BIMCameraProperties.raster_x props = tool.Drawing.get_camera_props(self.geometry)
> self.settings["geometry"].BIMCameraProperties.raster_y return props.raster_x > props.raster_y
)
def create_swept_disk_solid_representation(self) -> ifcopenshell.entity_instance: def create_swept_disk_solid_representation(self) -> ifcopenshell.entity_instance:
return self.file.createIfcShapeRepresentation( return self.file.createIfcShapeRepresentation(
@@ -16,6 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>. # along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
import ifcopenshell
import datetime import datetime
import isodate import isodate
from re import findall from re import findall
@@ -37,7 +38,7 @@ def timedelta2duration(timedelta):
return isodate.Duration(**components) return isodate.Duration(**components)
def ifc2datetime(element): def ifc2datetime(element: Union[str, int, ifcopenshell.entity_instance]):
if isinstance(element, str) and "P" in element[0:2]: # IfcDuration if isinstance(element, str) and "P" in element[0:2]: # IfcDuration
duration = parse_duration(element) duration = parse_duration(element)
if isinstance(duration, datetime.timedelta): if isinstance(duration, datetime.timedelta):
@@ -246,7 +247,7 @@ def parse_duration(value: Union[str, None]) -> Union[datetime.timedelta, None]:
return None return None
def canonicalise_time(time): def canonicalise_time(time: Union[datetime.datetime, None]) -> str:
if not time: if not time:
return "-" return "-"
return time.strftime("%d/%m/%y") return time.strftime("%d/%m/%y")