From 5c31ae4c371c18525096a1ceda84dc9618c5de39 Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Fri, 23 Jan 2026 22:03:28 +1100 Subject: [PATCH] Remove preference to mass/time and make it default with simpler code I generally like to minimise knobs and dials, so this is now part of the wizard but now defaults to a "NONE" unit. You also now have the option to choose "NONE" for area / volume units which makes things consistent. Enum names also match the IFC lowercase convention for conversion based units. This also simplifies the core logic and treats all units the same way instead of special cases for each unit type. (length is still special and required in Bonsai as we are inherently graphical) --- src/bonsai/bonsai/bim/module/project/ui.py | 16 +---- src/bonsai/bonsai/bim/prop.py | 27 ++++---- src/bonsai/bonsai/bim/ui.py | 6 -- src/bonsai/bonsai/core/tool.py | 2 +- src/bonsai/bonsai/core/unit.py | 49 +++---------- src/bonsai/bonsai/tool/unit.py | 80 +++++----------------- 6 files changed, 44 insertions(+), 136 deletions(-) diff --git a/src/bonsai/bonsai/bim/module/project/ui.py b/src/bonsai/bonsai/bim/module/project/ui.py index 1a6f727b20..ac70720dbe 100644 --- a/src/bonsai/bonsai/bim/module/project/ui.py +++ b/src/bonsai/bonsai/bim/module/project/ui.py @@ -368,21 +368,11 @@ class BIM_PT_new_project_wizard(Panel): row = self.layout.row() row.prop(props, "volume_unit", text="Volume Unit") row = self.layout.row() + row.prop(props, "mass_unit", text="Mass Unit") + row = self.layout.row() + row.prop(props, "time_unit", text="Time Unit") prop_with_search(self.layout, pprops, "template_file", text="Template") - if tool.Blender.get_addon_preferences().mass_time_units_in_wizard: - header, body = self.layout.panel("Mass and Time Units", default_closed=True) - if header: - header.label(text="Mass and Time Units") - if body: - label = "Add Mass and Time Units" if not props.add_mass_time_units else "Remove Mass and Time Units" - body.prop(props, "add_mass_time_units", toggle=True, text=label) - if props.add_mass_time_units: - row = body.row() - row.prop(props, "mass_unit", text="Mass Unit") - row = body.row() - row.prop(props, "time_unit", text="Time Unit") - self.layout.use_property_split = True row = self.layout.row() row.operator("bim.create_project") diff --git a/src/bonsai/bonsai/bim/prop.py b/src/bonsai/bonsai/bim/prop.py index ec8154b167..2adbfab5d6 100644 --- a/src/bonsai/bonsai/bim/prop.py +++ b/src/bonsai/bonsai/bim/prop.py @@ -589,6 +589,7 @@ class BIMProperties(PropertyGroup): area_unit: EnumProperty( default="SQUARE_METRE", items=[ + ("NONE", "None", ""), ("NANO/SQUARE_METRE", "Square Nanometre", ""), ("MICRO/SQUARE_METRE", "Square Micrometre", ""), ("MILLI/SQUARE_METRE", "Square Millimetre", ""), @@ -606,6 +607,7 @@ class BIMProperties(PropertyGroup): volume_unit: EnumProperty( default="CUBIC_METRE", items=[ + ("NONE", "None", ""), ("NANO/CUBIC_METRE", "Cubic Nanometre", ""), ("MICRO/CUBIC_METRE", "Cubic Micrometre", ""), ("MILLI/CUBIC_METRE", "Cubic Millimetre", ""), @@ -619,31 +621,28 @@ class BIMProperties(PropertyGroup): ], name="IFC Volume Unit", ) - add_mass_time_units: bpy.props.BoolProperty( - name="Add Mass and Time Units", - description="Enable to define mass and time units for the project", - default=False, - ) mass_unit: EnumProperty( items=[ - ("KILOGRAM", "Kilogram", "Kilograms"), + ("NONE", "None", ""), ("GRAM", "Gram", "Grams"), - ("POUND", "Pound", "Pounds"), - ("OUNCE", "Ounce", "Ounces"), - ("TONNE", "Tonne", "Metric Tons"), + ("KILO/KILOGRAM", "Kilogram", "Kilograms"), + ("MEGA/TONNE", "Tonne", "Metric Tons"), + ("pound", "Pound", "Pounds"), + ("ounce", "Ounce", "Ounces"), ], name="Mass Unit", - default="KILOGRAM", + default="NONE", ) time_unit: EnumProperty( items=[ + ("NONE", "None", ""), ("SECOND", "Second", "Seconds"), - ("MINUTE", "Minute", "Minutes"), - ("HOUR", "Hour", "Hours"), - ("DAY", "Day", "Days"), + ("minute", "Minute", "Minutes"), + ("hour", "Hour", "Hours"), + ("day", "Day", "Days"), ], name="Time Unit", - default="HOUR", + default="NONE", ) tab_visibilities: CollectionProperty(type=BIMTabVisibility, name="Tab Visibilities") active_tab_visibility_index: IntProperty(name="Active Tab Visibility Index") diff --git a/src/bonsai/bonsai/bim/ui.py b/src/bonsai/bonsai/bim/ui.py index 49c2cf51b6..81eb4c45d0 100644 --- a/src/bonsai/bonsai/bim/ui.py +++ b/src/bonsai/bonsai/bim/ui.py @@ -717,12 +717,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences): default=False, ) - mass_time_units_in_wizard: BoolProperty( - name="Mass and time units in project wizard", - description="Show mass and time units section in the new project wizard panel", - default=False, - ) - chain_filter_with_set_operations: BoolProperty( name="NEW Filter mode: Enable chained filters with set operations", description="Enable chaining search filters with set operations: ADD (union: combine sets), SUBTRACT (difference: remove from set), FILTER (intersection: only elements in both sets), with autocomplete suggestions for filter values", diff --git a/src/bonsai/bonsai/core/tool.py b/src/bonsai/bonsai/core/tool.py index e231571834..c07ac9f483 100644 --- a/src/bonsai/bonsai/core/tool.py +++ b/src/bonsai/bonsai/core/tool.py @@ -1124,7 +1124,7 @@ class Unit: def enable_editing_units(cls): pass def export_unit_attributes(cls): pass def get_scene_unit_name(cls, unit_type): pass - def get_scene_unit_si_prefix(cls, unit_type): pass + def get_scene_unit_si_prefix(cls, name): pass def import_unit_attributes(cls, unit): pass def import_units(cls): pass def is_scene_unit_metric(cls): pass diff --git a/src/bonsai/bonsai/core/unit.py b/src/bonsai/bonsai/core/unit.py index fde75297b1..a8325f9d89 100644 --- a/src/bonsai/bonsai/core/unit.py +++ b/src/bonsai/bonsai/core/unit.py @@ -27,46 +27,17 @@ if TYPE_CHECKING: def assign_scene_units(ifc: type[tool.Ifc], unit: type[tool.Unit]) -> None: - if unit.is_scene_unit_metric(): - lengthunit = ifc.run( - "unit.add_si_unit", unit_type="LENGTHUNIT", prefix=unit.get_scene_unit_si_prefix("LENGTHUNIT") - ) - areaunit = ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix=unit.get_scene_unit_si_prefix("AREAUNIT")) - volumeunit = ifc.run( - "unit.add_si_unit", unit_type="VOLUMEUNIT", prefix=unit.get_scene_unit_si_prefix("VOLUMEUNIT") - ) - planeangleunit = ifc.run("unit.add_conversion_based_unit", name="degree") - units = [lengthunit, areaunit, volumeunit, planeangleunit] - - if unit.add_mass_and_time_units(): - prefix = unit.get_scene_unit_si_prefix("MASSUNIT") - if prefix == "CONVERSION": - massunit = ifc.run("unit.add_conversion_based_unit", name=unit.get_scene_unit_name("MASSUNIT").lower()) + units = [] + for unit_type in ["LENGTHUNIT", "AREAUNIT", "VOLUMEUNIT", "MASSUNIT", "TIMEUNIT"]: + if name := unit.get_scene_unit_name(unit_type): + if unit.is_si_unit(name): + units.append( + ifc.run("unit.add_si_unit", unit_type=unit_type, prefix=unit.get_scene_unit_si_prefix(name)) + ) else: - massunit = ifc.run("unit.add_si_unit", unit_type="MASSUNIT", prefix=prefix) - prefix = unit.get_scene_unit_si_prefix("TIMEUNIT") - if prefix == "CONVERSION": - timeunit = ifc.run("unit.add_conversion_based_unit", name=unit.get_scene_unit_name("TIMEUNIT").lower()) - else: - timeunit = ifc.run("unit.add_si_unit", unit_type="TIMEUNIT", prefix=prefix) - units += [massunit, timeunit] - - else: - lengthunit = ifc.run("unit.add_conversion_based_unit", name=unit.get_scene_unit_name("LENGTHUNIT")) - areaunit = ifc.run("unit.add_conversion_based_unit", name=unit.get_scene_unit_name("AREAUNIT")) - volumeunit = ifc.run("unit.add_conversion_based_unit", name=unit.get_scene_unit_name("VOLUMEUNIT")) - planeangleunit = ifc.run("unit.add_conversion_based_unit", name="degree") - units = [lengthunit, areaunit, volumeunit, planeangleunit] - - if unit.add_mass_and_time_units(): - massunit = ifc.run("unit.add_conversion_based_unit", name=unit.get_scene_unit_name("MASSUNIT").lower()) - time_unit_name = unit.get_scene_unit_name("TIMEUNIT") - if time_unit_name == "SECOND": - timeunit = ifc.run("unit.add_si_unit", unit_type="TIMEUNIT", prefix=None) - else: - timeunit = ifc.run("unit.add_conversion_based_unit", name=time_unit_name.lower()) - units += [massunit, timeunit] - ifc.run("unit.assign_unit", units=units) + units.append(ifc.run("unit.add_conversion_based_unit", name=name)) + if units: + ifc.run("unit.assign_unit", units=units) def assign_unit(ifc: type[tool.Ifc], unit_tool: type[tool.Unit], unit: ifcopenshell.entity_instance) -> None: diff --git a/src/bonsai/bonsai/tool/unit.py b/src/bonsai/bonsai/tool/unit.py index f7c9ce300b..b98e4fc272 100644 --- a/src/bonsai/bonsai/tool/unit.py +++ b/src/bonsai/bonsai/tool/unit.py @@ -330,66 +330,26 @@ class Unit(bonsai.core.tool.Unit): return bonsai.bim.helper.export_attributes(props.unit_attributes, callback=callback) @classmethod - def get_scene_unit_name(cls, unit_type: UNIT_TYPE) -> str: - bim_props = tool.Blender.get_bim_props() + def get_scene_unit_name(cls, unit_type: UNIT_TYPE) -> str | None: if unit_type == "LENGTHUNIT": - assert bpy.context.scene - props = bpy.context.scene.unit_settings - if props.length_unit == "MILES": - return "mile" - elif props.length_unit == "FEET" or props.length_unit == "ADAPTIVE": - return "foot" - elif props.length_unit == "INCHES": - return "inch" - elif props.length_unit == "THOU": - return "thou" - return "foot" - elif unit_type == "AREAUNIT": - return bim_props.area_unit - elif unit_type == "VOLUMEUNIT": - return bim_props.volume_unit - elif unit_type == "MASSUNIT": - return bim_props.mass_unit.lower() - elif unit_type == "TIMEUNIT": - return bim_props.time_unit.lower() - else: - assert_never(unit_type) + name = bpy.context.scene.unit_settings.length_unit + name = {"MILES": "mile", "FEET": "foot", "INCHES": "inch", "THOU": "thou", "ADAPTIVE": "METERS"}.get( + name, name + ) + if len(name) > len("METERS") and name.endswith("METERS"): + return f"{name[:-6]}/METRE" + return name + bim_props = tool.Blender.get_bim_props() + if (name := getattr(bim_props, f"{unit_type[:-4].lower()}_unit")) != "NONE": + return name @classmethod - def get_scene_unit_si_prefix(cls, unit_type: UNIT_TYPE) -> Union[str, None]: - bim_props = tool.Blender.get_bim_props() - if unit_type == "LENGTHUNIT": - assert bpy.context.scene - props = bpy.context.scene.unit_settings - if props.length_unit == "ADAPTIVE" or props.length_unit == "METERS": - return - return props.length_unit.replace("METERS", "") - elif unit_type == "AREAUNIT": - unit = bim_props.area_unit - elif unit_type == "VOLUMEUNIT": - unit = bim_props.volume_unit - elif unit_type == "MASSUNIT": - unit = bim_props.mass_unit - if unit == "GRAM": - return None - elif unit == "KILOGRAM": - return "KILO" - elif unit == "TONNE": - return "MEGA" - elif unit in ["POUND", "OUNCE"]: - return "CONVERSION" - else: - return None - elif unit_type == "TIMEUNIT": - unit = bim_props.time_unit - if unit == "SECOND": - return None - else: - return "CONVERSION" - else: - assert_never(unit_type) - if "/" in unit: - return unit.split("/")[0] + def is_si_unit(cls, name: str) -> bool: + return name[0].isupper() + + @classmethod + def get_scene_unit_si_prefix(cls, name: str) -> str | None: + return name.split("/")[0] if "/" in name else None @classmethod def import_unit_attributes(cls, unit: ifcopenshell.entity_instance) -> None: @@ -504,9 +464,3 @@ class Unit(bonsai.core.tool.Unit): elif ifc_class == "IfcMonetaryUnit": return "COPY_ID" return "MOD_MESHDEFORM" - - @classmethod - def add_mass_and_time_units(cls) -> bool: - """Return True if the user wants to add mass and time units, False otherwise.""" - bim_props = tool.Blender.get_bim_props() - return getattr(bim_props, "add_mass_time_units", False)