From 52302841d91176d757363588f3d71fb4cf4289ad Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Wed, 24 May 2023 17:40:18 +0500 Subject: [PATCH] Window Modifier - support typing units in props Example - https://i.imgur.com/C99RRs9.png --- .../blenderbim/bim/module/model/prop.py | 75 +++++++------ .../blenderbim/bim/module/model/stair.py | 3 +- .../blenderbim/bim/module/model/window.py | 102 ++++++++---------- src/blenderbim/blenderbim/tool/model.py | 40 ++++++- 4 files changed, 123 insertions(+), 97 deletions(-) diff --git a/src/blenderbim/blenderbim/bim/module/model/prop.py b/src/blenderbim/blenderbim/bim/module/model/prop.py index 1a7e0aa370..66c45333cb 100644 --- a/src/blenderbim/blenderbim/bim/module/model/prop.py +++ b/src/blenderbim/blenderbim/bim/module/model/prop.py @@ -225,21 +225,13 @@ class BIMStairProperties(PropertyGroup): if not convert_to_project_units: return stair_kwargs - si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - for prop_name in stair_kwargs: - if prop_name in self.non_si_units_props: - continue - prop_value = stair_kwargs[prop_name] - stair_kwargs[prop_name] = prop_value / si_conversion + stair_kwargs = tool.Model.convert_data_to_project_units(stair_kwargs, self.non_si_units_props) return stair_kwargs def set_props_kwargs_from_ifc_data(self, kwargs): - si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + kwargs = tool.Model.convert_data_to_si_units(kwargs, self.non_si_units_props) for prop_name in kwargs: - prop_value = kwargs[prop_name] - if prop_name not in self.non_si_units_props: - prop_value = prop_value * si_conversion - setattr(self, prop_name, prop_value) + setattr(self, prop_name, kwargs[prop_name]) class BIMSverchokProperties(PropertyGroup): @@ -248,15 +240,13 @@ class BIMSverchokProperties(PropertyGroup): def window_type_prop_update(self, context): number_of_panels, panels_data = self.window_types_panels[self.window_type] - - si_coversion = 1.0 / ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) - panels_data = [[v * si_coversion for v in data] for data in panels_data] self.first_mullion_offset, self.second_mullion_offset = panels_data[0] self.first_transom_offset, self.second_transom_offset = panels_data[1] # default prop values are in mm and converted later class BIMWindowProperties(PropertyGroup): + non_si_units_props = ("is_editing", "window_type", "window_added_previously") window_types = ( ("SINGLE_PANEL", "SINGLE_PANEL", ""), ("DOUBLE_PANEL_HORIZONTAL", "DOUBLE_PANEL_HORIZONTAL", ""), @@ -289,50 +279,63 @@ class BIMWindowProperties(PropertyGroup): window_type: bpy.props.EnumProperty( name="Window Type", items=window_types, default="SINGLE_PANEL", update=window_type_prop_update ) - overall_height: bpy.props.FloatProperty(name="Overall Height", default=0.9) - overall_width: bpy.props.FloatProperty(name="Overall Width", default=0.6) + overall_height: bpy.props.FloatProperty(name="Overall Height", default=0.9, subtype="DISTANCE") + overall_width: bpy.props.FloatProperty(name="Overall Width", default=0.6, subtype="DISTANCE") # lining properties - lining_depth: bpy.props.FloatProperty(name="Lining Depth", default=0.050) - lining_thickness: bpy.props.FloatProperty(name="Lining Thickness", default=0.050) - lining_offset: bpy.props.FloatProperty(name="Lining Offset", default=0.050) - lining_to_panel_offset_x: bpy.props.FloatProperty(name="Lining to Panel Offset X", default=0.025) - lining_to_panel_offset_y: bpy.props.FloatProperty(name="Lining to Panel Offset Y", default=0.025) - mullion_thickness: bpy.props.FloatProperty(name="Mullion Thickness", default=0.050) + lining_depth: bpy.props.FloatProperty(name="Lining Depth", default=0.050, subtype="DISTANCE") + lining_thickness: bpy.props.FloatProperty(name="Lining Thickness", default=0.050, subtype="DISTANCE") + lining_offset: bpy.props.FloatProperty(name="Lining Offset", default=0.050, subtype="DISTANCE") + lining_to_panel_offset_x: bpy.props.FloatProperty( + name="Lining to Panel Offset X", default=0.025, subtype="DISTANCE" + ) + lining_to_panel_offset_y: bpy.props.FloatProperty( + name="Lining to Panel Offset Y", default=0.025, subtype="DISTANCE" + ) + mullion_thickness: bpy.props.FloatProperty(name="Mullion Thickness", default=0.050, subtype="DISTANCE") first_mullion_offset: bpy.props.FloatProperty( name="First Mullion Offset", description="Distance from the first lining to the first mullion center", default=0.3, + subtype="DISTANCE", ) second_mullion_offset: bpy.props.FloatProperty( name="Second Mullion Offset", description="Distance from the first lining to the second mullion center", default=0.45, + subtype="DISTANCE", ) - transom_thickness: bpy.props.FloatProperty(name="Transom Thickness", default=0.050) + transom_thickness: bpy.props.FloatProperty(name="Transom Thickness", default=0.050, subtype="DISTANCE") first_transom_offset: bpy.props.FloatProperty( name="First Transom Offset", description="Distance from the first lining to the first transom center", default=0.3, + subtype="DISTANCE", ) second_transom_offset: bpy.props.FloatProperty( name="Second Transom Offset", description="Distance from the first lining to the second transom center", default=0.6, + subtype="DISTANCE", ) # panel properties - frame_depth: bpy.props.FloatVectorProperty(name="Frame Depth", size=3, default=[0.035] * 3) - frame_thickness: bpy.props.FloatVectorProperty(name="Frame Thickness", size=3, default=[0.035] * 3) + frame_depth: bpy.props.FloatVectorProperty(name="Frame Depth", size=3, default=[0.035] * 3, subtype="TRANSLATION") + frame_thickness: bpy.props.FloatVectorProperty( + name="Frame Thickness", size=3, default=[0.035] * 3, subtype="TRANSLATION" + ) - def get_general_kwargs(self): - return { + def get_general_kwargs(self, convert_to_project_units=False): + kwargs = { "window_type": self.window_type, "overall_height": self.overall_height, "overall_width": self.overall_width, } + if not convert_to_project_units: + return kwargs + return tool.Model.convert_data_to_project_units(kwargs, ["window_type"]) - def get_lining_kwargs(self): + def get_lining_kwargs(self, convert_to_project_units=False): kwargs = { "lining_depth": self.lining_depth, "lining_thickness": self.lining_thickness, @@ -369,13 +372,23 @@ class BIMWindowProperties(PropertyGroup): if self.window_type in ("TRIPLE_PANEL_HORIZONTAL",): kwargs["second_transom_offset"] = self.second_transom_offset - return kwargs + if not convert_to_project_units: + return kwargs + return tool.Model.convert_data_to_project_units(kwargs) - def get_panel_kwargs(self): - return { + def get_panel_kwargs(self, convert_to_project_units=False): + kwargs = { "frame_depth": self.frame_depth, "frame_thickness": self.frame_thickness, } + if not convert_to_project_units: + return kwargs + return tool.Model.convert_data_to_project_units(kwargs) + + def set_props_kwargs_from_ifc_data(self, kwargs): + kwargs = tool.Model.convert_data_to_si_units(kwargs, self.non_si_units_props) + for prop_name in kwargs: + setattr(self, prop_name, kwargs[prop_name]) class BIMDoorProperties(PropertyGroup): diff --git a/src/blenderbim/blenderbim/bim/module/model/stair.py b/src/blenderbim/blenderbim/bim/module/model/stair.py index b89f086505..3868743785 100644 --- a/src/blenderbim/blenderbim/bim/module/model/stair.py +++ b/src/blenderbim/blenderbim/bim/module/model/stair.py @@ -18,7 +18,7 @@ import bpy from bpy.types import Operator -from bpy.props import FloatProperty, IntProperty, BoolProperty +from bpy.props import FloatProperty, IntProperty from bpy_extras.object_utils import AddObjectHelper, object_data_add import bmesh @@ -28,7 +28,6 @@ import ifcopenshell from ifcopenshell.util.shape_builder import V, ShapeBuilder import blenderbim import blenderbim.tool as tool -from blenderbim.bim.helper import convert_property_group_from_si from mathutils import Vector from pprint import pprint diff --git a/src/blenderbim/blenderbim/bim/module/model/window.py b/src/blenderbim/blenderbim/bim/module/model/window.py index a9bcb7a153..e3aeccfc4e 100644 --- a/src/blenderbim/blenderbim/bim/module/model/window.py +++ b/src/blenderbim/blenderbim/bim/module/model/window.py @@ -27,11 +27,7 @@ import blenderbim.core.root import blenderbim.core.geometry from ifcopenshell.api.geometry.add_window_representation import DEFAULT_PANEL_SCHEMAS from ifcopenshell.util.shape_builder import V -from bpy.types import Operator -from bpy.props import FloatProperty, IntProperty, BoolProperty from bmesh.types import BMVert -from blenderbim.bim.helper import convert_property_group_from_si -from blenderbim.bim.ifc import IfcStore from mathutils import Vector @@ -105,31 +101,32 @@ def update_window_modifier_representation(context): element = tool.Ifc.get_entity(obj) props = obj.BIMWindowProperties ifc_file = tool.Ifc.get() + si_conversion = ifcopenshell.util.unit.calculate_unit_scale(ifc_file) representation_data = { "partition_type": props.window_type, - "overall_height": props.overall_height, - "overall_width": props.overall_width, + "overall_height": props.overall_height / si_conversion, + "overall_width": props.overall_width / si_conversion, "lining_properties": { - "LiningDepth": props.lining_depth, - "LiningThickness": props.lining_thickness, - "LiningOffset": props.lining_offset, - "LiningToPanelOffsetX": props.lining_to_panel_offset_x, - "LiningToPanelOffsetY": props.lining_to_panel_offset_y, - "MullionThickness": props.mullion_thickness, - "FirstMullionOffset": props.first_mullion_offset, - "SecondMullionOffset": props.second_mullion_offset, - "TransomThickness": props.transom_thickness, - "FirstTransomOffset": props.first_transom_offset, - "SecondTransomOffset": props.second_transom_offset, + "LiningDepth": props.lining_depth / si_conversion, + "LiningThickness": props.lining_thickness / si_conversion, + "LiningOffset": props.lining_offset / si_conversion, + "LiningToPanelOffsetX": props.lining_to_panel_offset_x / si_conversion, + "LiningToPanelOffsetY": props.lining_to_panel_offset_y / si_conversion, + "MullionThickness": props.mullion_thickness / si_conversion, + "FirstMullionOffset": props.first_mullion_offset / si_conversion, + "SecondMullionOffset": props.second_mullion_offset / si_conversion, + "TransomThickness": props.transom_thickness / si_conversion, + "FirstTransomOffset": props.first_transom_offset / si_conversion, + "SecondTransomOffset": props.second_transom_offset / si_conversion, }, "panel_properties": [], } number_of_panels, panels_data = props.window_types_panels[props.window_type] for panel_i in range(number_of_panels): panel_data = { - "FrameDepth": props.frame_depth[panel_i], - "FrameThickness": props.frame_thickness[panel_i], + "FrameDepth": props.frame_depth[panel_i] / si_conversion, + "FrameThickness": props.frame_thickness[panel_i] / si_conversion, } representation_data["panel_properties"].append(panel_data) @@ -175,10 +172,10 @@ def update_window_modifier_representation(context): # occurences attributes occurences = tool.Ifc.get_all_element_occurences(element) for occurence in occurences: - occurence.OverallWidth = props.overall_width - occurence.OverallHeight = props.overall_height + occurence.OverallWidth = props.overall_width / si_conversion + occurence.OverallHeight = props.overall_height / si_conversion - update_simple_openings(element, props.overall_width, props.overall_height) + update_simple_openings(element, props.overall_width / si_conversion, props.overall_height / si_conversion) def create_bm_window_frame(bm, size: Vector, thickness: list, position: Vector = V(0, 0, 0).freeze()): @@ -291,27 +288,26 @@ def create_bm_window( def update_window_modifier_bmesh(context): obj = context.object props = obj.BIMWindowProperties - si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) panel_schema = DEFAULT_PANEL_SCHEMAS[props.window_type] accumulated_height = [0] * len(panel_schema[0]) built_panels = [] - overall_width = props.overall_width * si_conversion - lining_depth = props.lining_depth * si_conversion - overall_height = props.overall_height * si_conversion - lining_to_panel_offset_x = props.lining_to_panel_offset_x * si_conversion - lining_to_panel_offset_y = props.lining_to_panel_offset_y * si_conversion - lining_thickness = props.lining_thickness * si_conversion - lining_offset = props.lining_offset * si_conversion + overall_width = props.overall_width + lining_depth = props.lining_depth + overall_height = props.overall_height + lining_to_panel_offset_x = props.lining_to_panel_offset_x + lining_to_panel_offset_y = props.lining_to_panel_offset_y + lining_thickness = props.lining_thickness + lining_offset = props.lining_offset - mullion_thickness = props.mullion_thickness * si_conversion / 2 - first_mullion_offset = props.first_mullion_offset * si_conversion - second_mullion_offset = props.second_mullion_offset * si_conversion - transom_thickness = props.transom_thickness * si_conversion / 2 - first_transom_offset = props.first_transom_offset * si_conversion - second_transom_offset = props.second_transom_offset * si_conversion + mullion_thickness = props.mullion_thickness / 2 + first_mullion_offset = props.first_mullion_offset + second_mullion_offset = props.second_mullion_offset + transom_thickness = props.transom_thickness / 2 + first_transom_offset = props.first_transom_offset + second_transom_offset = props.second_transom_offset - glass_thickness = 0.01 * si_conversion + glass_thickness = 0.01 bm = bmesh.new() panel_schema = list(reversed(panel_schema)) @@ -349,8 +345,8 @@ def update_window_modifier_bmesh(context): accumulated_width += panel_width continue - frame_depth = props.frame_depth[panel_i] * si_conversion - frame_thickness = props.frame_thickness[panel_i] * si_conversion + frame_depth = props.frame_depth[panel_i] + frame_thickness = props.frame_thickness[panel_i] # add window window_lining_size = V( @@ -456,13 +452,9 @@ class AddWindow(bpy.types.Operator, tool.Ifc.Operator): self.report({"ERROR"}, "Object has to be IfcWindow/IfcWindowType type to add a window.") return {"CANCELLED"} - # need to make sure all default props will have correct units - if not props.window_added_previously: - convert_property_group_from_si(props, skip_props=("is_editing", "window_type", "window_added_previously")) - - window_data = props.get_general_kwargs() - lining_props = props.get_lining_kwargs() - panel_props = props.get_panel_kwargs() + window_data = props.get_general_kwargs(convert_to_project_units=True) + lining_props = props.get_lining_kwargs(convert_to_project_units=True) + panel_props = props.get_panel_kwargs(convert_to_project_units=True) window_data["lining_properties"] = lining_props window_data["panel_properties"] = panel_props @@ -490,8 +482,7 @@ class CancelEditingWindow(bpy.types.Operator, tool.Ifc.Operator): element = tool.Ifc.get_entity(obj) data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Window", "Data")) props = obj.BIMWindowProperties - for prop_name in data: - setattr(props, prop_name, data[prop_name]) + props.set_props_kwargs_from_ifc_data(data) body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW") blenderbim.core.geometry.switch_representation( @@ -518,9 +509,9 @@ class FinishEditingWindow(bpy.types.Operator, tool.Ifc.Operator): element = tool.Ifc.get_entity(obj) props = obj.BIMWindowProperties - window_data = props.get_general_kwargs() - lining_props = props.get_lining_kwargs() - panel_props = props.get_panel_kwargs() + window_data = props.get_general_kwargs(convert_to_project_units=True) + lining_props = props.get_lining_kwargs(convert_to_project_units=True) + panel_props = props.get_panel_kwargs(convert_to_project_units=True) window_data["lining_properties"] = lining_props window_data["panel_properties"] = panel_props @@ -549,14 +540,7 @@ class EnableEditingWindow(bpy.types.Operator, tool.Ifc.Operator): data.update(data.pop("panel_properties")) # required since we could load pset from .ifc and BIMWindowProperties won't be set - for prop_name in data: - setattr(props, prop_name, data[prop_name]) - - # need to make sure all props that weren't used before - # will have correct units - skip_props = ("is_editing", "window_type", "window_added_previously") - skip_props += tuple(data.keys()) - convert_property_group_from_si(props, skip_props=skip_props) + props.set_props_kwargs_from_ifc_data(data) props.is_editing = 1 return {"FINISHED"} diff --git a/src/blenderbim/blenderbim/tool/model.py b/src/blenderbim/blenderbim/tool/model.py index a8d8b4dc93..7c27eafc74 100644 --- a/src/blenderbim/blenderbim/tool/model.py +++ b/src/blenderbim/blenderbim/tool/model.py @@ -28,6 +28,7 @@ import blenderbim.core.geometry as geometry from mathutils import Matrix, Vector from blenderbim.bim import import_ifc from blenderbim.bim.module.geometry.helper import Helper +import collections class Model(blenderbim.core.tool.Model): @@ -43,6 +44,33 @@ class Model(blenderbim.core.tool.Model): return [v * cls.unit_scale for v in value] return value * cls.unit_scale + @classmethod + def convert_data_to_project_units(cls, data, non_si_props=[]): + si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + for prop_name in data: + if prop_name in non_si_props: + continue + prop_value = data[prop_name] + if isinstance(prop_value, collections.abc.Iterable): + data[prop_name] = [v / si_conversion for v in prop_value] + else: + data[prop_name] = prop_value / si_conversion + return data + + @classmethod + def convert_data_to_si_units(cls, data, non_si_props=[]): + si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get()) + for prop_name in data: + if prop_name in non_si_props: + continue + prop_value = data[prop_name] + print(prop_name, prop_value) + if isinstance(prop_value, collections.abc.Iterable): + data[prop_name] = [v * si_conversion for v in prop_value] + else: + data[prop_name] = prop_value * si_conversion + return data + @classmethod def export_curve(cls, position, edge_indices, points=None): position_i = position.inverted() @@ -147,11 +175,13 @@ class Model(blenderbim.core.tool.Model): cls.bm.edges.ensure_lookup_table() surface = tool.Ifc.get().createIfcCurveBoundedPlane() - surface.BasisSurface = tool.Ifc.get().createIfcPlane(tool.Ifc.get().createIfcAxis2Placement3D( - tool.Ifc.get().createIfcCartesianPoint([o / cls.unit_scale for o in p1]), - tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]), - tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]), - )) + surface.BasisSurface = tool.Ifc.get().createIfcPlane( + tool.Ifc.get().createIfcAxis2Placement3D( + tool.Ifc.get().createIfcCartesianPoint([o / cls.unit_scale for o in p1]), + tool.Ifc.get().createIfcDirection([float(o) for o in z_axis]), + tool.Ifc.get().createIfcDirection([float(o) for o in x_axis]), + ) + ) if tool.Ifc.get().schema != "IFC2X3": cls.points = cls.export_points(position, indices["points"])