mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 17:31:45 +00:00
New qto tool is now unit aware
This commit is contained in:
@@ -298,7 +298,6 @@ class WorkSchedulePsetsData(Data):
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
props = bpy.context.scene.WorkSchedulePsetProperties
|
||||
ifc_definition_id = bpy.context.scene.BIMWorkScheduleProperties.active_work_schedule_id
|
||||
cls.data = {"psets": cls.psetqtos(tool.Ifc.get().by_id(ifc_definition_id), psets_only=True)}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -49,8 +49,13 @@ def get_calculator(self, context):
|
||||
def get_calculator_function(self, context):
|
||||
calculator = ifc5d.qto.calculators[self.calculator]
|
||||
results = []
|
||||
for function in calculator.get_functions():
|
||||
results.append((function.id, function.name, function.description))
|
||||
previous_measure = None
|
||||
for function_id, function in calculator.functions.items():
|
||||
measure = function.measure.split("Measure")[0][3:]
|
||||
if previous_measure is not None and measure != previous_measure:
|
||||
results.append(None)
|
||||
results.append((function_id, f"{measure}: {function.name}", function.description))
|
||||
previous_measure = measure
|
||||
return results
|
||||
|
||||
|
||||
|
||||
+119
-80
@@ -21,13 +21,13 @@ import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.pset
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.selector
|
||||
import multiprocessing
|
||||
from collections import namedtuple
|
||||
from typing import Iterable
|
||||
|
||||
|
||||
Function = namedtuple("Function", ["id", "name", "description"])
|
||||
Function = namedtuple("Function", ["measure", "name", "description"])
|
||||
rules = {}
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
@@ -58,10 +58,77 @@ def edit_qtos(ifc_file, results) -> None:
|
||||
ifcopenshell.api.pset.edit_qto(ifc_file, qto=qto, properties=quantities)
|
||||
|
||||
|
||||
class SI2ProjectUnitConverter:
|
||||
def __init__(self, ifc_file):
|
||||
self.project_units = {
|
||||
"IfcAreaMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "AREAUNIT"),
|
||||
"IfcLengthMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "LENGTHUNIT"),
|
||||
"IfcMassMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "MASSUNIT"),
|
||||
"IfcTimeMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "TIMEUNIT"),
|
||||
"IfcVolumeMeasure": ifcopenshell.util.unit.get_project_unit(ifc_file, "VOLUMEUNIT"),
|
||||
}
|
||||
for key, value in self.project_units.items():
|
||||
if value:
|
||||
self.project_units[key] = (getattr(value, "Prefix", "None"), value.Name)
|
||||
|
||||
self.si_names = {
|
||||
"IfcAreaMeasure": "SQUARE_METRE",
|
||||
"IfcLengthMeasure": "METRE",
|
||||
"IfcMassMeasure": "GRAM",
|
||||
"IfcTimeMeasure": "SECOND",
|
||||
"IfcVolumeMeasure": "CUBIE_METRE",
|
||||
}
|
||||
|
||||
def convert(self, value, measure):
|
||||
if measure_unit := self.project_units.get(measure, None):
|
||||
return ifcopenshell.util.unit.convert(value, None, self.si_names[measure], *measure_unit)
|
||||
return value
|
||||
|
||||
|
||||
class IfcOpenShell:
|
||||
"""Calculates Model body context geometry using the default IfcOpenShell
|
||||
iterator on triangulation elements."""
|
||||
|
||||
functions = {
|
||||
# IfcLengthMeasure
|
||||
"get_x": Function("IfcLengthMeasure", "X", "Calculates the length along the local X axis"),
|
||||
"get_y": Function("IfcLengthMeasure", "Y", "Calculates the length along the local Y axis"),
|
||||
"get_z": Function("IfcLengthMeasure", "Z", "Calculates the length along the local Z axis"),
|
||||
"get_max_xyz": Function("IfcLengthMeasure", "Max XYZ", "The maximum X, Y, or Z local dimension"),
|
||||
"get_min_xyz": Function("IfcLengthMeasure", "Min XYZ", "The minimum X, Y, or Z local dimension"),
|
||||
"get_top_elevation": Function("IfcLengthMeasure", "Top elevation", "The local maximum Z ordinate"),
|
||||
"get_bottom_elevation": Function("IfcLengthMeasure", "Bottom Elevation", "The local minimum Z ordinate"),
|
||||
"get_footprint_perimeter": Function(
|
||||
"IfcLengthMeasure",
|
||||
"Footprint Perimeter",
|
||||
"The perimeter if the object's faces were projected along the Z-axis and seen top down",
|
||||
),
|
||||
# IfcAreaMeasure
|
||||
"get_area": Function("IfcAreaMeasure", "Area", "The total surface area of the element"),
|
||||
"get_footprint_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Footprint Area",
|
||||
"The area if the object's faces were projected along the Z-axis and seen top down",
|
||||
),
|
||||
"get_max_side_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Max Side Area",
|
||||
"The maximum side area when seen from either X, Y, or Z directions",
|
||||
),
|
||||
"get_outer_surface_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Outer Surface Area",
|
||||
"The total surface area except for the top or bottom, such as the ends of columns or beams",
|
||||
),
|
||||
"get_side_area": Function(
|
||||
"IfcAreaMeasure",
|
||||
"Side area",
|
||||
"The side (non-projected) are of the shape as seen from the local Y-axis",
|
||||
),
|
||||
# IfcVolumeMeasure
|
||||
"get_volume": Function("IfcVolumeMeasure", "Volume", "Calculates the volume of a manifold shape"),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file,
|
||||
@@ -103,6 +170,8 @@ class IfcOpenShell:
|
||||
if net_qtos:
|
||||
tasks.append((IfcOpenShell.create_iterator(ifc_file, net_settings, list(elements)), net_qtos))
|
||||
|
||||
unit_converter = SI2ProjectUnitConverter(ifc_file)
|
||||
|
||||
for iterator, qtos in tasks:
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
@@ -112,7 +181,9 @@ class IfcOpenShell:
|
||||
for name, quantities in qtos.items():
|
||||
results[element].setdefault(name, {})
|
||||
for quantity, formula in quantities.items():
|
||||
results[element][name][quantity] = formula_functions[formula](shape.geometry)
|
||||
results[element][name][quantity] = unit_converter.convert(
|
||||
formula_functions[formula](shape.geometry), IfcOpenShell.functions[formula].measure
|
||||
)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
@@ -122,47 +193,51 @@ class IfcOpenShell:
|
||||
) -> ifcopenshell.geom.iterator:
|
||||
return ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=elements)
|
||||
|
||||
@staticmethod
|
||||
def get_functions() -> list[Function]:
|
||||
return [
|
||||
Function("get_area", "Area", "The total surface area of the element"),
|
||||
Function("get_bottom_elevation", "Bottom Elevation", "The local minimum Z ordinate"),
|
||||
Function(
|
||||
"get_footprint_area",
|
||||
"Footprint Area",
|
||||
"The area if the object's faces were projected along the Z-axis and seen top down",
|
||||
),
|
||||
Function(
|
||||
"get_footprint_perimeter",
|
||||
"Footprint Perimeter",
|
||||
"The perimeter if the object's faces were projected along the Z-axis and seen top down",
|
||||
),
|
||||
Function(
|
||||
"get_max_side_area",
|
||||
"Max Side Area",
|
||||
"The maximum side area when seen from either X, Y, or Z directions",
|
||||
),
|
||||
Function("get_max_xyz", "Max XYZ", "The maximum X, Y, or Z local dimension"),
|
||||
Function("get_min_xyz", "Min XYZ", "The minimum X, Y, or Z local dimension"),
|
||||
Function(
|
||||
"get_outer_surface_area",
|
||||
"Outer Surface Area",
|
||||
"The total surface area except for the top or bottom, such as the ends of columns or beams",
|
||||
),
|
||||
Function(
|
||||
"get_side_area", "Side area", "The side (non-projected) are of the shape as seen from the local Y-axis"
|
||||
),
|
||||
Function("get_top_elevation", "Top elevation", "The local maximum Z ordinate"),
|
||||
Function("get_volume", "Volume", "Calculates the volume of a manifold shape"),
|
||||
Function("get_x", "X", "Calculates the length along the local X axis"),
|
||||
Function("get_y", "Y", "Calculates the length along the local Y axis"),
|
||||
Function("get_z", "Z", "Calculates the length along the local Z axis"),
|
||||
]
|
||||
|
||||
|
||||
class Blender:
|
||||
"""Calculates geometry based on currently loaded Blender objects."""
|
||||
|
||||
functions = {
|
||||
# IfcLengthMeasure
|
||||
"get_covering_width": Function("IfcLengthMeasure", "Covering Width", ""),
|
||||
"get_finish_ceiling_height": Function("IfcLengthMeasure", "Finish Ceiling Height", ""),
|
||||
"get_finish_floor_height": Function("IfcLengthMeasure", "Finish Floor Height", ""),
|
||||
"get_gross_perimeter": Function("IfcLengthMeasure", "Gross Perimeter", ""),
|
||||
"get_height": Function("IfcLengthMeasure", "Height", ""),
|
||||
"get_length": Function("IfcLengthMeasure", "Length", ""),
|
||||
"get_opening_depth": Function("IfcLengthMeasure", "Opening Depth", ""),
|
||||
"get_opening_height": Function("IfcLengthMeasure", "Opening Height", ""),
|
||||
"get_rectangular_perimeter": Function("IfcLengthMeasure", "Rectangular Perimeter", ""),
|
||||
"get_stair_length": Function("IfcLengthMeasure", "Stair Length", ""),
|
||||
"get_width": Function("IfcLengthMeasure", "Width", ""),
|
||||
# IfcAreaMeasure
|
||||
"get_covering_gross_area": Function("IfcAreaMeasure", "Covering Gross Area", ""),
|
||||
"get_covering_net_area": Function("IfcAreaMeasure", "Covering Net Area", ""),
|
||||
"get_cross_section_area": Function("IfcAreaMeasure", "Cross Section Area", ""),
|
||||
"get_gross_ceiling_area": Function("IfcAreaMeasure", "Gross Ceiling Area", ""),
|
||||
"get_gross_footprint_area": Function("IfcAreaMeasure", "Gross Footprint Area", ""),
|
||||
"get_gross_side_area": Function("IfcAreaMeasure", "Gross Side Area", ""),
|
||||
"get_gross_stair_area": Function("IfcAreaMeasure", "Gross Stair Area", ""),
|
||||
"get_gross_surface_area": Function("IfcAreaMeasure", "Gross Surface Area", ""),
|
||||
"get_gross_top_area": Function("IfcAreaMeasure", "Gross Top Area", ""),
|
||||
"get_net_ceiling_area": Function("IfcAreaMeasure", "Net Ceiling Area", ""),
|
||||
"get_net_floor_area": Function("IfcAreaMeasure", "Net Floor Area", ""),
|
||||
"get_net_footprint_area": Function("IfcAreaMeasure", "Net Footprint Area", ""),
|
||||
"get_net_side_area": Function("IfcAreaMeasure", "Net Side Area", ""),
|
||||
"get_net_stair_area": Function("IfcAreaMeasure", "Net Stair Area", ""),
|
||||
"get_net_surface_area": Function("IfcAreaMeasure", "Net Surface Area", ""),
|
||||
"get_net_top_area": Function("IfcAreaMeasure", "Net Top Area", ""),
|
||||
"get_opening_mapping_area": Function("IfcAreaMeasure", "Opening Mapping Area", ""),
|
||||
"get_outer_surface_area": Function("IfcAreaMeasure", "Outer Surface Area", ""),
|
||||
# IfcVolumeMeasure
|
||||
"get_gross_volume": Function("IfcVolumeMeasure", "Gross Volume", ""),
|
||||
"get_net_volume": Function("IfcVolumeMeasure", "Net Volume", ""),
|
||||
"get_space_net_volume": Function("IfcVolumeMeasure", "Space Net Volume", ""),
|
||||
# IfcMassMeasure
|
||||
"get_gross_weight": Function("IfcMassMeasure", "Gross Weight", ""),
|
||||
"get_net_weight": Function("IfcMassMeasure", "Net Weight", ""),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], qtos: dict, results: dict
|
||||
@@ -170,6 +245,7 @@ class Blender:
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.qto.calculator as calculator
|
||||
|
||||
unit_converter = SI2ProjectUnitConverter(ifc_file)
|
||||
formula_functions = {}
|
||||
|
||||
for element in elements:
|
||||
@@ -182,46 +258,9 @@ class Blender:
|
||||
for quantity, formula in quantities.items():
|
||||
if not (formula_function := formula_functions.get(formula)):
|
||||
formula_function = formula_functions[formula] = getattr(calculator, formula)
|
||||
results[element][name][quantity] = formula_function(obj)
|
||||
|
||||
@staticmethod
|
||||
def get_functions() -> list[Function]:
|
||||
return [
|
||||
Function("get_covering_gross_area", "Covering Gross Area", ""),
|
||||
Function("get_covering_net_area", "Covering Net Area", ""),
|
||||
Function("get_covering_width", "Covering Width", ""),
|
||||
Function("get_cross_section_area", "Cross Section Area", ""),
|
||||
Function("get_finish_ceiling_height", "Finish Ceiling Height", ""),
|
||||
Function("get_finish_floor_height", "Finish Floor Height", ""),
|
||||
Function("get_gross_ceiling_area", "Gross Ceiling Area", ""),
|
||||
Function("get_gross_footprint_area", "Gross Footprint Area", ""),
|
||||
Function("get_gross_perimeter", "Gross Perimeter", ""),
|
||||
Function("get_gross_side_area", "Gross Side Area", ""),
|
||||
Function("get_gross_stair_area", "Gross Stair Area", ""),
|
||||
Function("get_gross_surface_area", "Gross Surface Area", ""),
|
||||
Function("get_gross_top_area", "Gross Top Area", ""),
|
||||
Function("get_gross_volume", "Gross Volume", ""),
|
||||
Function("get_gross_weight", "Gross Weight", ""),
|
||||
Function("get_height", "Height", ""),
|
||||
Function("get_length", "Length", ""),
|
||||
Function("get_net_ceiling_area", "Net Ceiling Area", ""),
|
||||
Function("get_net_floor_area", "Net Floor Area", ""),
|
||||
Function("get_net_footprint_area", "Net Footprint Area", ""),
|
||||
Function("get_net_side_area", "Net Side Area", ""),
|
||||
Function("get_net_stair_area", "Net Stair Area", ""),
|
||||
Function("get_net_surface_area", "Net Surface Area", ""),
|
||||
Function("get_net_top_area", "Net Top Area", ""),
|
||||
Function("get_net_volume", "Net Volume", ""),
|
||||
Function("get_net_weight", "Net Weight", ""),
|
||||
Function("get_opening_depth", "Opening Depth", ""),
|
||||
Function("get_opening_height", "Opening Height", ""),
|
||||
Function("get_opening_mapping_area", "Opening Mapping Area", ""),
|
||||
Function("get_outer_surface_area", "Outer Surface Area", ""),
|
||||
Function("get_rectangular_perimeter", "Rectangular Perimeter", ""),
|
||||
Function("get_space_net_volume", "Space Net Volume", ""),
|
||||
Function("get_stair_length", "Stair Length", ""),
|
||||
Function("get_width", "Width", ""),
|
||||
]
|
||||
results[element][name][quantity] = unit_converter.convert(
|
||||
formula_function(obj), Blender.functions[formula].measure
|
||||
)
|
||||
|
||||
|
||||
calculators = {"Blender": Blender, "IfcOpenShell": IfcOpenShell}
|
||||
|
||||
Reference in New Issue
Block a user