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)
This commit is contained in:
Dion Moult
2026-01-23 22:03:28 +11:00
parent 92ad7d2ba2
commit 5c31ae4c37
6 changed files with 44 additions and 136 deletions
+3 -13
View File
@@ -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")
+13 -14
View File
@@ -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")
-6
View File
@@ -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",
+1 -1
View File
@@ -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
+10 -39
View File
@@ -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:
+17 -63
View File
@@ -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)