Thanks to visit codestin.com
Credit goes to github.com

Skip to content
2 changes: 2 additions & 0 deletions src/bonsai/bonsai/bim/module/project/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ def execute(self, context) -> set["rna_enums.OperatorReturnItems"]:
bpy.context.scene.unit_settings.length_unit = "MILLIMETERS"
bim_props.area_unit = "SQUARE_METRE"
bim_props.volume_unit = "CUBIC_METRE"
bim_props.mass_unit = "KILOGRAM"
bim_props.time_unit = "SECOND"
pprops.template_file = "IFC4 Demo Template.ifc"

if self.preset != "wizard":
Expand Down
15 changes: 15 additions & 0 deletions src/bonsai/bonsai/bim/module/project/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,27 @@ def draw(self, context):
row.prop(props, "area_unit", text="Area Unit")
row = self.layout.row()
row.prop(props, "volume_unit", text="Volume Unit")
row = self.layout.row()
prop_with_search(self.layout, pprops, "template_file", text="Template")

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")



class BIM_PT_project_library(Panel):
bl_label = "Project Library"
bl_idname = "BIM_PT_project_library"
Expand Down
28 changes: 28 additions & 0 deletions src/bonsai/bonsai/bim/prop.py
Original file line number Diff line number Diff line change
Expand Up @@ -586,7 +586,33 @@ 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"),
("GRAM", "Gram", "Grams"),
("POUND", "Pound", "Pounds"),
("OUNCE", "Ounce", "Ounces"),
("TONNE", "Tonne", "Metric Tons"),
],
name="Mass Unit",
default="KILOGRAM",
)

time_unit: EnumProperty(
items=[
("SECOND", "Second", "Seconds"),
("MINUTE", "Minute", "Minutes"),
("HOUR", "Hour", "Hours"),
("DAY", "Day", "Days"),
],
name="Time Unit",
default="HOUR",
)
if TYPE_CHECKING:
is_dirty: bool
schema_dir: str
Expand All @@ -599,6 +625,8 @@ class BIMProperties(PropertyGroup):
section_line_decorator_width: float
area_unit: str
volume_unit: str
mass_unit: str
time_unit: str


class IfcParameter(PropertyGroup):
Expand Down
2 changes: 1 addition & 1 deletion src/bonsai/bonsai/core/tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -1113,7 +1113,7 @@ def is_unit_class(cls, unit, ifc_class): pass
def set_active_unit(cls, unit): pass
def get_project_currency_unit(cls): pass
def get_currency_name(cls): pass

def add_mass_and_time_units(cls): pass

@interface
class Voider:
Expand Down
43 changes: 33 additions & 10 deletions src/bonsai/bonsai/core/unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,43 @@

def assign_scene_units(ifc: type[tool.Ifc], unit: type[tool.Unit]) -> None:
if unit.is_scene_unit_metric():
prefix = unit.get_scene_unit_si_prefix("LENGTHUNIT")
lengthunit = ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", prefix=prefix)
prefix = unit.get_scene_unit_si_prefix("AREAUNIT")
areaunit = ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix=prefix)
prefix = unit.get_scene_unit_si_prefix("VOLUMEUNIT")
volumeunit = ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", prefix=prefix)
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())
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")

ifc.run("unit.assign_unit", units=[lengthunit, areaunit, volumeunit, planeangleunit])
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]
print("Add mass and time units:", unit.add_mass_and_time_units())
print("Assigning units:", 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:
Expand Down
30 changes: 29 additions & 1 deletion src/bonsai/bonsai/tool/unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@


class Unit(bonsai.core.tool.Unit):
UNIT_TYPE = Literal["LENGTHUNIT", "AREAUNIT", "VOLUMEUNIT"]
UNIT_TYPE = Literal["LENGTHUNIT", "AREAUNIT", "VOLUMEUNIT", "MASSUNIT", "TIMEUNIT"]

@classmethod
def get_unit_props(cls) -> BIMUnitProperties:
Expand Down Expand Up @@ -84,6 +84,10 @@ def get_scene_unit_name(cls, unit_type: UNIT_TYPE) -> str:
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)

Expand All @@ -100,6 +104,24 @@ def get_scene_unit_si_prefix(cls, unit_type: UNIT_TYPE) -> Union[str, None]:
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:
Expand Down Expand Up @@ -218,3 +240,9 @@ def get_icon_for_unit_class(cls, ifc_class: str) -> str:
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)
101 changes: 91 additions & 10 deletions src/bonsai/test/core/test_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,116 @@ def test_creating_and_assigning_metric_units(self, ifc, unit):
unit.get_scene_unit_si_prefix("LENGTHUNIT").should_be_called().will_return("prefix")
unit.get_scene_unit_si_prefix("AREAUNIT").should_be_called().will_return("prefix")
unit.get_scene_unit_si_prefix("VOLUMEUNIT").should_be_called().will_return("prefix")
unit.add_mass_and_time_units().should_be_called().will_return(True)
unit.get_scene_unit_si_prefix("MASSUNIT").should_be_called().will_return("KILO")
unit.get_scene_unit_si_prefix("TIMEUNIT").should_be_called().will_return(None)

ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", prefix="prefix").should_be_called().will_return(
"lengthunit"
)

ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix="prefix").should_be_called().will_return("areaunit")

ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", prefix="prefix").should_be_called().will_return(
"volumeunit"
)

ifc.run("unit.add_si_unit", unit_type="MASSUNIT", prefix="KILO").should_be_called().will_return("massunit")
ifc.run("unit.add_si_unit", unit_type="TIMEUNIT", prefix=None).should_be_called().will_return("timeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")

ifc.run(
"unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit", "massunit", "timeunit"]
).should_be_called()
subject.assign_scene_units(ifc, unit)

def test_creating_and_assigning_metric_units_without_mass_and_time(self, ifc, unit):
unit.is_scene_unit_metric().should_be_called().will_return(True)
unit.get_scene_unit_si_prefix("LENGTHUNIT").should_be_called().will_return("CENTI")
unit.get_scene_unit_si_prefix("AREAUNIT").should_be_called().will_return("CENTI")
unit.get_scene_unit_si_prefix("VOLUMEUNIT").should_be_called().will_return("CENTI")
unit.add_mass_and_time_units().should_be_called().will_return(False)
ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", prefix="CENTI").should_be_called().will_return("lengthunit")
ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix="CENTI").should_be_called().will_return("areaunit")
ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", prefix="CENTI").should_be_called().will_return("volumeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")
ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called()
subject.assign_scene_units(ifc, unit)

def test_creating_and_assigning_imperial_units(self, ifc, unit):
unit.is_scene_unit_metric().should_be_called().will_return(False)
unit.get_scene_unit_name("LENGTHUNIT").should_be_called().will_return("lengthname")
ifc.run("unit.add_conversion_based_unit", name="lengthname").should_be_called().will_return("lengthunit")
unit.get_scene_unit_name("LENGTHUNIT").should_be_called().will_return("foot")
unit.get_scene_unit_name("AREAUNIT").should_be_called().will_return("square foot")
unit.get_scene_unit_name("VOLUMEUNIT").should_be_called().will_return("cubic foot")
unit.add_mass_and_time_units().should_be_called().will_return(True)
unit.get_scene_unit_name("MASSUNIT").should_be_called().will_return("pound")
unit.get_scene_unit_name("TIMEUNIT").should_be_called().will_return("SECOND")

ifc.run("unit.add_conversion_based_unit", name="foot").should_be_called().will_return("lengthunit")
ifc.run("unit.add_conversion_based_unit", name="square foot").should_be_called().will_return("areaunit")
ifc.run("unit.add_conversion_based_unit", name="cubic foot").should_be_called().will_return("volumeunit")
ifc.run("unit.add_conversion_based_unit", name="pound").should_be_called().will_return("massunit")
ifc.run("unit.add_si_unit", unit_type="TIMEUNIT", prefix=None).should_be_called().will_return("timeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")

ifc.run(
"unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit", "massunit", "timeunit"]
).should_be_called()
subject.assign_scene_units(ifc, unit)

def test_creating_and_assigning_imperial_units_without_mass_and_time(self, ifc, unit):
unit.is_scene_unit_metric().should_be_called().will_return(False)
unit.get_scene_unit_name("LENGTHUNIT").should_be_called().will_return("yard")
unit.get_scene_unit_name("AREAUNIT").should_be_called().will_return("square yard")
unit.get_scene_unit_name("VOLUMEUNIT").should_be_called().will_return("cubic yard")
unit.add_mass_and_time_units().should_be_called().will_return(False)
ifc.run("unit.add_conversion_based_unit", name="yard").should_be_called().will_return("lengthunit")
ifc.run("unit.add_conversion_based_unit", name="square yard").should_be_called().will_return("areaunit")
ifc.run("unit.add_conversion_based_unit", name="cubic yard").should_be_called().will_return("volumeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")
ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called()
subject.assign_scene_units(ifc, unit)

unit.get_scene_unit_name("AREAUNIT").should_be_called().will_return("areaname")
ifc.run("unit.add_conversion_based_unit", name="areaname").should_be_called().will_return("areaunit")

unit.get_scene_unit_name("VOLUMEUNIT").should_be_called().will_return("volumename")
ifc.run("unit.add_conversion_based_unit", name="volumename").should_be_called().will_return("volumeunit")
def test_creating_metric_units_with_conversion_based_mass_and_time(self, ifc, unit):
unit.is_scene_unit_metric().should_be_called().will_return(True)
unit.get_scene_unit_si_prefix("LENGTHUNIT").should_be_called().will_return("MILLI")
unit.get_scene_unit_si_prefix("AREAUNIT").should_be_called().will_return(None)
unit.get_scene_unit_si_prefix("VOLUMEUNIT").should_be_called().will_return(None)
unit.add_mass_and_time_units().should_be_called().will_return(True)
unit.get_scene_unit_si_prefix("MASSUNIT").should_be_called().will_return("CONVERSION")
unit.get_scene_unit_name("MASSUNIT").should_be_called().will_return("tonne")
unit.get_scene_unit_si_prefix("TIMEUNIT").should_be_called().will_return("CONVERSION")
unit.get_scene_unit_name("TIMEUNIT").should_be_called().will_return("minute")

ifc.run("unit.add_si_unit", unit_type="LENGTHUNIT", prefix="MILLI").should_be_called().will_return("lengthunit")
ifc.run("unit.add_si_unit", unit_type="AREAUNIT", prefix=None).should_be_called().will_return("areaunit")
ifc.run("unit.add_si_unit", unit_type="VOLUMEUNIT", prefix=None).should_be_called().will_return("volumeunit")
ifc.run("unit.add_conversion_based_unit", name="tonne").should_be_called().will_return("massunit")
ifc.run("unit.add_conversion_based_unit", name="minute").should_be_called().will_return("timeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")

ifc.run(
"unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit", "massunit", "timeunit"]
).should_be_called()
subject.assign_scene_units(ifc, unit)

def test_creating_imperial_units_with_conversion_based_units(self, ifc, unit):
unit.is_scene_unit_metric().should_be_called().will_return(False)
unit.get_scene_unit_name("LENGTHUNIT").should_be_called().will_return("inch")
unit.get_scene_unit_name("AREAUNIT").should_be_called().will_return("square inch")
unit.get_scene_unit_name("VOLUMEUNIT").should_be_called().will_return("cubic inch")
unit.add_mass_and_time_units().should_be_called().will_return(True)
unit.get_scene_unit_name("MASSUNIT").should_be_called().will_return("ounce")
unit.get_scene_unit_name("TIMEUNIT").should_be_called().will_return("hour")

ifc.run("unit.add_conversion_based_unit", name="inch").should_be_called().will_return("lengthunit")
ifc.run("unit.add_conversion_based_unit", name="square inch").should_be_called().will_return("areaunit")
ifc.run("unit.add_conversion_based_unit", name="cubic inch").should_be_called().will_return("volumeunit")
ifc.run("unit.add_conversion_based_unit", name="ounce").should_be_called().will_return("massunit")
ifc.run("unit.add_conversion_based_unit", name="hour").should_be_called().will_return("timeunit")
ifc.run("unit.add_conversion_based_unit", name="degree").should_be_called().will_return("planeangleunit")

ifc.run("unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit"]).should_be_called()
ifc.run(
"unit.assign_unit", units=["lengthunit", "areaunit", "volumeunit", "planeangleunit", "massunit", "timeunit"]
).should_be_called()
subject.assign_scene_units(ifc, unit)


Expand Down
Loading