mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5466be9b7b | |||
| b86dab2a76 | |||
| a57e063c14 | |||
| c7830e9e5b | |||
| d533ba1e6b | |||
| 45a1655485 | |||
| 3679fccfbd | |||
| cf9f792a3b | |||
| d87665de7e | |||
| 95daf7efa3 | |||
| 435ce8d58a | |||
| f92a2b3c8e | |||
| 5ea1c94822 | |||
| ab1bf7592e | |||
| f4247c3527 | |||
| 8c3d10a2bd | |||
| 1bda47f76e | |||
| 9001aa6383 | |||
| 097c5cee3c | |||
| 9d789425ce | |||
| c0b86ea016 | |||
| 6806eb0a05 | |||
| ce26d931c1 | |||
| eeaa0a5530 | |||
| a09cb5dee4 | |||
| 6035683492 | |||
| f27dbacd28 | |||
| 8817a7e6d5 | |||
| 80c4c13998 | |||
| 5159e2daee | |||
| 54951d2102 | |||
| 8403c32e76 | |||
| 316934de97 | |||
| dd48fbcb0f | |||
| 54978a9868 | |||
| 02fd78df14 | |||
| ed1202e1f2 | |||
| d9de967197 | |||
| e57076c84f | |||
| 75e6495999 | |||
| fcf07bebfb | |||
| f190ddcd92 | |||
| 39a841fb4e | |||
| bdfd0c4aa7 | |||
| 37b14f2a3d | |||
| 70a4af2605 | |||
| 7142f3856c |
@@ -170,6 +170,7 @@ classes = [
|
||||
ui.BIM_PT_tab_structural,
|
||||
# Construction scheduling
|
||||
ui.BIM_PT_tab_status,
|
||||
ui.BIM_PT_tab_qto,
|
||||
ui.BIM_PT_tab_resources,
|
||||
ui.BIM_PT_tab_cost,
|
||||
ui.BIM_PT_tab_sequence,
|
||||
|
||||
@@ -5,6 +5,7 @@ FILE_NAME('EQto_BodyGeometryValidation.ifc','2020-01-01T00:00:00',(),(),'EQto_Bo
|
||||
FILE_SCHEMA(('IFC4'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
/* This quantity set is Qto_BodyGeometryValidation backport from IFC4X3 to support IFC4. */
|
||||
#1=IFCPROPERTYSETTEMPLATE('2KS9su6r517uLXTGKwwDdn',$,'EQto_BodyGeometryValidation','Quantities supplied for validating the correct interpretation of the body shape representation at import. In case of multiple representation items, the quantities are summed for each of the items (irrespective of any overlap). Choosing a suitable tolerance value for comparing the supplied numbers to the numbers calculated from the reconstructed geometry is at the discretion of the importing application.',.QTO_OCCURRENCEDRIVEN.,'IfcProduct',(#2,#3,#4,#5,#6,#7));
|
||||
#2=IFCSIMPLEPROPERTYTEMPLATE('3iCdOOLRjCwQ_HRTB41Xh8',$,'GrossSurfaceArea','Total gross area of the object, normally generated as perimeter * length + 2 * cross section area. It is the sum of OuterSurfaceArea + (2 x CrossSectionArea) and shall only be given, if the OuterSurfaceArea and CrossSectionArea cannot be established separately.\X2\000A000A\X0\Total gross surface area of the element before applying product-level geometric features such as openings and projections.',.Q_AREA.,$,$,$,$,$,$,.READWRITE.);
|
||||
#3=IFCSIMPLEPROPERTYTEMPLATE('0juemEqF5889vg7HwR87Fv',$,'NetSurfaceArea','Net surface area of the object, normally generated as perimeter * length + 2 * cross section area taking into account possible processing features (cut-out''s, etc.) or openings and recesses.\X2\000A000A\X0\Total net surface area of the element after applying product-level geometric features such as openings and projections.',.Q_AREA.,$,$,$,$,$,$,.READWRITE.);
|
||||
|
||||
@@ -301,7 +301,8 @@ def load_post(scene):
|
||||
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.should_setup_workspace:
|
||||
if "BIM" in bpy.data.workspaces:
|
||||
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||
if bpy.context.preferences.addons["blenderbim"].preferences.activate_workspace:
|
||||
bpy.context.window.workspace = bpy.data.workspaces["BIM"]
|
||||
else:
|
||||
bpy.ops.workspace.append_activate(idname="BIM", filepath=os.path.join(cwd, "data", "workspace.blend"))
|
||||
|
||||
|
||||
@@ -225,15 +225,6 @@ def get_enum_items(data, prop_name, context=None):
|
||||
return items
|
||||
|
||||
|
||||
# hack to close popup
|
||||
# https://blender.stackexchange.com/a/202576/130742
|
||||
def close_operator_panel(event):
|
||||
x, y = event.mouse_x, event.mouse_y
|
||||
bpy.context.window.cursor_warp(10, 10)
|
||||
move_back = lambda: bpy.context.window.cursor_warp(x, y)
|
||||
bpy.app.timers.register(move_back, first_interval=0.01)
|
||||
|
||||
|
||||
def convert_property_group_from_si(property_group, skip_props=()):
|
||||
"""Method converts property group values from si to current ifc project units
|
||||
|
||||
|
||||
@@ -198,10 +198,16 @@ class CostSchedulesData:
|
||||
# data["DerivedUnitSymbol"] = "?"
|
||||
# print("Total Cost", data["DerivedTotalCostQuantity"], cost_item.Name)
|
||||
|
||||
# TODO: dead code?
|
||||
@classmethod
|
||||
def _get_object_quantities(cls, cost_item, element):
|
||||
def _get_object_quantities(
|
||||
cls, cost_item: ifcopenshell.entity_instance, element: ifcopenshell.entity_instance
|
||||
) -> list[int]:
|
||||
if not element.is_a("IfcObject"):
|
||||
return []
|
||||
cost_quantities = cost_item.CostQuantities
|
||||
if not cost_quantities:
|
||||
return []
|
||||
results = []
|
||||
for relationship in element.IsDefinedBy:
|
||||
if not relationship.is_a("IfcRelDefinesByProperties"):
|
||||
@@ -210,7 +216,7 @@ class CostSchedulesData:
|
||||
if not qto.is_a("IfcElementQuantity"):
|
||||
continue
|
||||
for prop in qto.Quantities:
|
||||
if prop in cost_item.CostQuantities or []:
|
||||
if prop in cost_quantities:
|
||||
results.append(prop.id())
|
||||
return results
|
||||
|
||||
|
||||
@@ -755,9 +755,11 @@ class BIM_UL_product_cost_items(UIList):
|
||||
class BIM_PT_Costing_Tools(Panel):
|
||||
bl_label = "5D Tools"
|
||||
bl_idname = "BIM_PT_Costing_Tools"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "4D/5D Toolkit"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_cost"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMCostProperties
|
||||
|
||||
@@ -93,7 +93,8 @@ class ConvertToBlender(bpy.types.Operator):
|
||||
if obj.data:
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
for material in bpy.data.materials:
|
||||
material.BIMMaterialProperties.ifc_style_id = False
|
||||
material.BIMObjectProperties.ifc_definition_id = 0
|
||||
material.BIMMaterialProperties.ifc_style_id = 0
|
||||
context.scene.BIMProperties.ifc_file = ""
|
||||
context.scene.BIMDebugProperties.attributes.clear()
|
||||
IfcStore.purge()
|
||||
|
||||
@@ -22,14 +22,20 @@ from . import ui, prop, operator
|
||||
classes = (
|
||||
operator.AddRepresentation,
|
||||
operator.CopyRepresentation,
|
||||
operator.DisableEditingRepresentationItemShapeAspect,
|
||||
operator.DisableEditingRepresentationItemStyle,
|
||||
operator.DisableEditingRepresentationItems,
|
||||
operator.DuplicateLinkedAggregateTo3dCursor,
|
||||
operator.DuplicateMoveLinkedAggregate,
|
||||
operator.DuplicateMoveLinkedAggregateMacro,
|
||||
operator.EditObjectPlacement,
|
||||
operator.EditRepresentationItemShapeAspect,
|
||||
operator.EditRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItemShapeAspect,
|
||||
operator.EnableEditingRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItems,
|
||||
operator.FlipObject,
|
||||
operator.GetRepresentationIfcParameters,
|
||||
operator.DuplicateMoveLinkedAggregate,
|
||||
operator.DuplicateMoveLinkedAggregateMacro,
|
||||
operator.DuplicateLinkedAggregateTo3dCursor,
|
||||
operator.OverrideDelete,
|
||||
operator.OverrideDuplicateMove,
|
||||
operator.OverrideDuplicateMoveLinked,
|
||||
@@ -46,19 +52,13 @@ classes = (
|
||||
operator.RefreshLinkedAggregate,
|
||||
operator.RemoveConnection,
|
||||
operator.RemoveRepresentation,
|
||||
operator.RemoveRepresentationItem,
|
||||
operator.RemoveRepresentationItemFromShapeAspect,
|
||||
operator.SelectConnection,
|
||||
operator.SwitchRepresentation,
|
||||
operator.UnassignRepresentationItemStyle,
|
||||
operator.UpdateParametricRepresentation,
|
||||
operator.UpdateRepresentation,
|
||||
operator.RemoveRepresentationItem,
|
||||
operator.EnableEditingRepresentationItemStyle,
|
||||
operator.EditRepresentationItemStyle,
|
||||
operator.DisableEditingRepresentationItemStyle,
|
||||
operator.UnassignRepresentationItemStyle,
|
||||
operator.EnableEditingRepresentationItemShapeAspect,
|
||||
operator.EditRepresentationItemShapeAspect,
|
||||
operator.DisableEditingRepresentationItemShapeAspect,
|
||||
operator.RemoveRepresentationItemFromShapeAspect,
|
||||
prop.RepresentationItem,
|
||||
prop.ShapeAspect,
|
||||
prop.BIMObjectGeometryProperties,
|
||||
@@ -120,6 +120,7 @@ def register():
|
||||
|
||||
km = wm.keyconfigs.addon.keymaps.new(name="Mesh", space_type="EMPTY")
|
||||
kmi = km.keymap_items.new("bim.override_mode_set_object", "TAB", "PRESS")
|
||||
kmi.properties.should_save = True
|
||||
addon_keymaps.append((km, kmi))
|
||||
kmi = km.keymap_items.new("wm.call_menu", "P", "PRESS")
|
||||
kmi.properties.name = ui.BIM_MT_hotkey_separate.bl_idname
|
||||
|
||||
@@ -28,6 +28,7 @@ import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.api
|
||||
import blenderbim.core.geometry
|
||||
import blenderbim.core.geometry as core
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.style
|
||||
@@ -713,6 +714,10 @@ class OverrideOutlinerDelete(bpy.types.Operator):
|
||||
else:
|
||||
bpy.data.objects.remove(obj)
|
||||
for collection in collections_to_delete:
|
||||
# Removing an aggregate object would also remove it's collection
|
||||
# making the collection data-block invalid.
|
||||
if not tool.Blender.is_valid_data_block(collection):
|
||||
continue
|
||||
bpy.data.collections.remove(collection)
|
||||
if self.is_batch:
|
||||
old_file = tool.Ifc.get()
|
||||
@@ -1683,20 +1688,11 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
if self.is_valid:
|
||||
row = self.layout.row()
|
||||
row.prop(self, "should_save")
|
||||
else:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Geometry Found: Object will revert to previous state.")
|
||||
|
||||
def invoke(self, context, event):
|
||||
return IfcStore.execute_ifc_operator(self, context, is_invoke=True)
|
||||
|
||||
def _invoke(self, context, event):
|
||||
self.is_valid = True
|
||||
self.should_save = True
|
||||
|
||||
bpy.ops.object.mode_set(mode="EDIT", toggle=True)
|
||||
|
||||
@@ -1758,8 +1754,6 @@ class OverrideModeSetObject(bpy.types.Operator):
|
||||
else:
|
||||
tool.Ifc.finish_edit(obj)
|
||||
|
||||
if self.edited_objs:
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
return self.execute(context)
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ from blenderbim.bim.module.layer.data import LayersData
|
||||
|
||||
def mode_menu(self, context):
|
||||
row = self.layout.row(align=True)
|
||||
if context.scene.BIMGeometryProperties.mode == "EDIT":
|
||||
row.operator("bim.override_mode_set_object", icon="CANCEL", text="Discard Changes").should_save = False
|
||||
row.prop(context.scene.BIMGeometryProperties, "mode", text="", icon_value=blenderbim.bim.icons["IFC"].icon_id)
|
||||
|
||||
|
||||
|
||||
@@ -247,9 +247,11 @@ class IFCGIT_PT_revision_inspector(bpy.types.Panel):
|
||||
bl_idname = "IFCGIT_PT_revision_inspector"
|
||||
bl_label = "Git History"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "IFCGIT_PT_panel"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
|
||||
@@ -22,10 +22,11 @@ import bpy
|
||||
class BIM_PT_misc_utilities(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_misc_utilities"
|
||||
bl_label = "Miscellaneous"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "output"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_parent_id = "BIM_PT_tab_sandbox"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
@@ -83,8 +83,17 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props = obj.BIMArrayProperties
|
||||
|
||||
relating_obj = props.relating_array_object
|
||||
|
||||
if relating_obj:
|
||||
element = tool.Ifc.get_entity(relating_obj)
|
||||
parent_globalid = ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Parent")
|
||||
parent_element = tool.Ifc.get().by_guid(parent_globalid)
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(parent_element, "BBIM_Array", "Data"))[self.item]
|
||||
else:
|
||||
data = json.loads(ifcopenshell.util.element.get_pset(element, "BBIM_Array", "Data"))[self.item]
|
||||
props.count = data["count"]
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
props.x = data["x"] * si_conversion
|
||||
@@ -93,7 +102,9 @@ class EnableEditingArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
props.use_local_space = data.get("use_local_space", False)
|
||||
props.sync_children = data.get("sync_children", False)
|
||||
props.method = data.get("method", "OFFSET")
|
||||
|
||||
props.is_editing = self.item
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -137,6 +148,10 @@ class EditArray(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
tool.Blender.Modifier.Array.set_children_lock_state(element, self.item, True)
|
||||
tool.Blender.Modifier.Array.constrain_children_to_parent(element)
|
||||
|
||||
#clears the relating_array_object so it doesn't show again next time
|
||||
props.relating_array_object = None
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -87,6 +87,21 @@ def update_type_page(self, context):
|
||||
AuthoringData.data["paginated_relating_types"] = AuthoringData.paginated_relating_types()
|
||||
|
||||
|
||||
def update_relating_array_from_object(self, context):
|
||||
bpy.ops.bim.enable_editing_array(item=self.is_editing)
|
||||
return
|
||||
|
||||
|
||||
def is_object_array_applicable(self, obj):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
return False
|
||||
return ifcopenshell.util.element.get_pset(element, "BBIM_Array")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BIMModelProperties(PropertyGroup):
|
||||
ifc_class: bpy.props.EnumProperty(items=get_ifc_class, name="Construction Class", update=update_ifc_class)
|
||||
relating_type_id: bpy.props.EnumProperty(
|
||||
@@ -203,6 +218,14 @@ class BIMArrayProperties(PropertyGroup):
|
||||
description="Regenerate all children based on the parent object",
|
||||
default=False,
|
||||
)
|
||||
relating_array_object: bpy.props.PointerProperty(
|
||||
type=bpy.types.Object,
|
||||
name="Copy Array Properties",
|
||||
update=update_relating_array_from_object,
|
||||
poll=is_object_array_applicable,
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class BIMStairProperties(PropertyGroup):
|
||||
|
||||
@@ -223,6 +223,8 @@ class BIM_PT_array(bpy.types.Panel):
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "z")
|
||||
row.operator("bim.input_cursor_z_array", icon="CURSOR", text="")
|
||||
row = col.row(align=True)
|
||||
row.prop(props, "relating_array_object", icon="COPYDOWN")
|
||||
else:
|
||||
row = box.row(align=True)
|
||||
name = f"{array['count']} Items ({array.get('method', 'OFFSET').capitalize()})"
|
||||
|
||||
@@ -22,7 +22,7 @@ import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.type.prop as type_prop
|
||||
from blenderbim.bim.helper import prop_with_search, close_operator_panel
|
||||
from blenderbim.bim.helper import prop_with_search
|
||||
from bpy.types import WorkSpaceTool
|
||||
from blenderbim.bim.module.model.data import AuthoringData
|
||||
from blenderbim.bim.module.drawing.data import DecoratorData
|
||||
@@ -485,7 +485,7 @@ class BimToolUI:
|
||||
|
||||
cls.layout.separator()
|
||||
add_layout_hotkey_operator(
|
||||
cls.layout, "Calculate All Quantities", "S_Q", bpy.ops.bim.calculate_all_quantities.__doc__
|
||||
cls.layout, "Perform Quantity Take-off", "S_Q", bpy.ops.bim.perform_quantity_take_off.__doc__
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -614,7 +614,7 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
if not bpy.context.selected_objects:
|
||||
return
|
||||
|
||||
bpy.ops.bim.calculate_all_quantities()
|
||||
bpy.ops.bim.perform_quantity_take_off()
|
||||
|
||||
def hotkey_C_P(self):
|
||||
if not bpy.context.selected_objects:
|
||||
|
||||
@@ -76,6 +76,11 @@ classes = (
|
||||
gizmo.ClippingPlane,
|
||||
)
|
||||
|
||||
if bpy.app.version >= (4, 1, 0):
|
||||
classes += (
|
||||
operator.IFCFileHandlerOperator,
|
||||
operator.BIM_FH_import_ifc,
|
||||
)
|
||||
|
||||
addon_keymaps = []
|
||||
|
||||
|
||||
@@ -2085,3 +2085,46 @@ class BIM_OT_load_clipping_planes(bpy.types.Operator):
|
||||
obj.location = values["location"]
|
||||
obj.rotation_euler = values["rotation"]
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
if bpy.app.version >= (4, 1, 0):
|
||||
|
||||
class IFCFileHandlerOperator(bpy.types.Operator):
|
||||
bl_idname = "bim.load_project_file_handler"
|
||||
bl_label = "Import .ifc file"
|
||||
bl_options = {"REGISTER", "UNDO", "INTERNAL"}
|
||||
|
||||
directory: bpy.props.StringProperty(subtype="FILE_PATH", options={"SKIP_SAVE", "HIDDEN"})
|
||||
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={"SKIP_SAVE", "HIDDEN"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
# Keeping code in .invoke() as we'll probably add some
|
||||
# popup windows later.
|
||||
|
||||
# `files` contain only .ifc files.
|
||||
filepath = Path(self.directory)
|
||||
# If user is just drag'n'dropping a single file -> load it as a new project,
|
||||
# if they're holding ALT -> link the file/files to the current project.
|
||||
if event.alt:
|
||||
# Passing self.files directly results in TypeError.
|
||||
serialized_files = [{"name": f.name} for f in self.files]
|
||||
return bpy.ops.bim.link_ifc(directory=self.directory, files=serialized_files)
|
||||
else:
|
||||
if len(self.files) == 1:
|
||||
return bpy.ops.bim.load_project(filepath=(filepath / self.files[0].name).as_posix())
|
||||
else:
|
||||
self.report(
|
||||
{"INFO"},
|
||||
"To link multiple IFC files hold ALT while drag'n'dropping them.",
|
||||
)
|
||||
return {"FINISHED"}
|
||||
|
||||
class BIM_FH_import_ifc(bpy.types.FileHandler):
|
||||
bl_label = "IFC File Handler"
|
||||
bl_import_operator = IFCFileHandlerOperator.bl_idname
|
||||
bl_file_extensions = ".ifc"
|
||||
|
||||
# FileHandler won't work without poll_drop defined.
|
||||
@classmethod
|
||||
def poll_drop(cls, context):
|
||||
return True
|
||||
|
||||
@@ -27,8 +27,6 @@ classes = (
|
||||
operator.DisablePsetEditing,
|
||||
operator.EditPset,
|
||||
operator.EnablePsetEditing,
|
||||
operator.GuessQuantity,
|
||||
operator.CalculateQuantity,
|
||||
operator.RemovePset,
|
||||
operator.TogglePsetExpansion,
|
||||
operator.BIM_OT_add_property_to_edit,
|
||||
|
||||
@@ -1,577 +0,0 @@
|
||||
mapper = {
|
||||
'Qto_AudioVisualApplianceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_PlateBaseQuantities' : {
|
||||
'Width' : "get_height",
|
||||
'Perimeter' : "get_gross_perimeter",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
'NetArea' : "get_net_footprint_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_OpeningElementBaseQuantities' : {
|
||||
'Width' : "get_length",
|
||||
'Height' : "get_opening_height",
|
||||
'Depth' : "get_opening_depth",
|
||||
'Area' : "get_opening_mapping_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_MarineFacilityBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ChillerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_PileBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_VibrationIsolatorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_LampBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_VehicleBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_PipeFittingBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : None,
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_CableCarrierFittingBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_HeatExchangerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_DoorBaseQuantities' : {
|
||||
'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Height' : "get_height",
|
||||
'Perimeter' : "get_rectangular_perimeter",
|
||||
'Area' : "get_net_side_area",
|
||||
},
|
||||
'Qto_DuctSegmentBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_TransformerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FacilityPartBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ProjectionElementBaseQuantities' : {
|
||||
'Area' : "get_net_side_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_SignBaseQuantities' : {
|
||||
'Height' : "get_height",
|
||||
'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Thickness' : "get_width",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_CableSegmentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
},
|
||||
'Qto_BuildingBaseQuantities' : {
|
||||
'Height' : None,
|
||||
'EavesHeight' : None,
|
||||
'FootPrintArea' : None,
|
||||
'GrossFloorArea' : None,
|
||||
'NetFloorArea' : None,
|
||||
'GrossVolume' : None,
|
||||
'NetVolume' : None,
|
||||
},
|
||||
'Qto_ElectricFlowStorageDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CommunicationsApplianceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_RailBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Volume' : "get_net_volume",
|
||||
'Weight' : "get_net_weight",
|
||||
},
|
||||
'Qto_PictorialSignQuantities' : {
|
||||
'Area' : "get_net_side_area",
|
||||
'SignArea' : None,
|
||||
},
|
||||
'Qto_SpaceHeaterBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_CoveringBaseQuantities' : {
|
||||
'Width' : "get_covering_width",
|
||||
'GrossArea' : "get_covering_gross_area",
|
||||
'NetArea' : "get_covering_net_area",
|
||||
},
|
||||
'Qto_PipeSegmentBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_HumidifierBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ConstructionEquipmentResourceBaseQuantities' : {
|
||||
'UsageTime' : None,
|
||||
'OperatingTime' : None,
|
||||
},
|
||||
'Qto_AlarmBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_JunctionBoxBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NumberOfGangs' : None,
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_ArealStratumBaseQuantities' : {
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Length' : "get_length",
|
||||
'PlanLength' : None,
|
||||
},
|
||||
'Qto_SiteBaseQuantities' : {
|
||||
'GrossPerimeter' : "get_gross_perimeter",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
},
|
||||
'Qto_MotorConnectionBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_RoofBaseQuantities' : {
|
||||
'GrossArea' : "get_gross_top_area",
|
||||
'NetArea' : "get_net_top_area",
|
||||
'ProjectedArea' : None,
|
||||
},
|
||||
'Qto_ChimneyBaseQuantities' : {
|
||||
'Length' : "get_height",
|
||||
},
|
||||
'Qto_ElectricTimeControlBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ElectricMotorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_EarthworksFillBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'Width' : None,
|
||||
'Depth' : None,
|
||||
'CompactedVolume' : None,
|
||||
'LooseVolume' : None,
|
||||
},
|
||||
'Qto_ConduitSegmentBaseQuantities' : {
|
||||
'InnerDiameter' : None,
|
||||
'OuterDiameter' : None,
|
||||
},
|
||||
'Qto_SignalBaseQuantities' : {
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_DuctFittingBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'GrossCrossSectionArea' : None,
|
||||
'NetCrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_UnitaryControlElementBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ActuatorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CurtainWallQuantities' : {
|
||||
'Length' : None,
|
||||
'Height' : None,
|
||||
'Width' : None,
|
||||
'GrossSideArea' : None,
|
||||
'NetSideArea' : None,
|
||||
},
|
||||
'Qto_BoilerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
'TotalSurfaceArea' : None,
|
||||
},
|
||||
'Qto_FlowMeterBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_AirTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'Perimeter' : None,
|
||||
'TotalSurfaceArea' : None,
|
||||
},
|
||||
'Qto_DuctSilencerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_WasteTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SlabBaseQuantities' : {
|
||||
'Width' : "get_width",
|
||||
'Length' : "get_length",
|
||||
'Depth' : "get_height",
|
||||
'Perimeter' : "get_gross_perimeter",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
'NetArea' : "get_net_footprint_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_ImpactProtectionDeviceBaseQuantities' : {
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_LightFixtureBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FlowInstrumentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_BuildingStoreyBaseQuantities' : {
|
||||
'GrossHeight' : None,
|
||||
'NetHeight' : None,
|
||||
'GrossPerimeter' : None,
|
||||
'GrossFloorArea' : None,
|
||||
'NetFloorArea' : None,
|
||||
'GrossVolume' : None,
|
||||
'NetVolume' : None,
|
||||
},
|
||||
'Qto_ReinforcedSoilBaseQuantities' : {
|
||||
'Length' : None,
|
||||
'Width' : None,
|
||||
'Depth' : None,
|
||||
'Area' : None,
|
||||
'Volume' : None,
|
||||
},
|
||||
'Qto_DistributionBoardBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NumberOfCircuits' : None,
|
||||
},
|
||||
'Qto_FootingBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_PumpBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CableCarrierSegmentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'Length' : None,
|
||||
'CrossSectionArea' : None,
|
||||
'OuterSurfaceArea' : None,
|
||||
},
|
||||
'Qto_InterceptorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ColumnBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_EarthworksCutBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Depth' : "get_height",
|
||||
'UndisturbedVolume' : "get_net_volume",
|
||||
'LooseVolume' : None,
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_StackTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CoilBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_KerbBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'Depth' : None,
|
||||
'Volume' : "get_net_volume",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_PavementBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Depth' : "get_height",
|
||||
'GrossArea' : "get_gross_footprint_area",
|
||||
'NetArea' : "get_net_footprint_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ControllerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SolarDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'GrossArea' : None,
|
||||
},
|
||||
'Qto_RampFlightBaseQuantities' : {
|
||||
'Length' : "get_stair_length",
|
||||
'Width' : "get_width",
|
||||
'GrossArea' : "get_gross_stair_area",
|
||||
'NetArea' : "get_net_stair_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_ElectricApplianceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ValveBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_DamperBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SurfaceFeatureBaseQuantities' : {
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Length' : "get_length",
|
||||
},
|
||||
'Qto_WallBaseQuantities' : {
|
||||
'Length' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
'GrossFootprintArea' : "get_gross_footprint_area",
|
||||
'NetFootprintArea' : "get_net_footprint_area",
|
||||
'GrossSideArea' : "get_gross_side_area",
|
||||
'NetSideArea' : "get_net_side_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_StairFlightBaseQuantities' : {
|
||||
'Length' : "get_stair_length",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_SwitchingDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_BurnerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ConstructionMaterialResourceBaseQuantities' : {
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_ElectricGeneratorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_LinearStratumBaseQuantities' : {
|
||||
'Diameter' : None,
|
||||
'Length' : None,
|
||||
},
|
||||
'Qto_CableFittingBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_DistributionChamberElementBaseQuantities' : {
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'Depth' : "get_length",
|
||||
},
|
||||
'Qto_CompressorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_ProtectiveDeviceTrippingUnitBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_EvaporatorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SpaceBaseQuantities' : {
|
||||
'Height' : "get_height",
|
||||
'FinishCeilingHeight' : "get_finish_ceiling_height",
|
||||
'FinishFloorHeight' : "get_finish_floor_height",
|
||||
'GrossPerimeter' : "get_gross_perimeter",
|
||||
'NetPerimeter' : None,
|
||||
'GrossFloorArea' : "get_gross_footprint_area",
|
||||
'NetFloorArea' : "get_net_floor_area",
|
||||
'GrossWallArea' : None,
|
||||
'NetWallArea' : None,
|
||||
'GrossCeilingArea' : "get_gross_ceiling_area",
|
||||
'NetCeilingArea' : "get_net_ceiling_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_space_net_volume",
|
||||
},
|
||||
'Qto_CourseBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Thickness' : "get_height",
|
||||
'Volume' : "get_net_volume",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_CondenserBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FireSuppressionTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_RailingBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
},
|
||||
'Qto_TubeBundleBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
},
|
||||
'Qto_BeamBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_SleeperBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_ProtectiveDeviceBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CooledBeamBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_TankBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
'NetWeight' : None,
|
||||
'TotalSurfaceArea' : "get_outer_surface_area",
|
||||
},
|
||||
'Qto_AirToAirHeatRecoveryBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_CoolingTowerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_SensorBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_WindowBaseQuantities' : {
|
||||
'Width' : { "function_name" : "get_length", "args" : ", main_axis = 'x'"},
|
||||
'Height' : "get_height",
|
||||
'Perimeter' : "get_rectangular_perimeter",
|
||||
'Area' : "get_net_side_area",
|
||||
},
|
||||
'Qto_SanitaryTerminalBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_BuildingElementProxyQuantities' : {
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'NetVolume' : "get_net_volume",
|
||||
},
|
||||
'Qto_FanBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_OutletBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_UnitaryEquipmentBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_FilterBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_MemberBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'CrossSectionArea' : "get_cross_section_area",
|
||||
'OuterSurfaceArea' : "get_outer_surface_area",
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'GrossWeight' : "get_gross_weight",
|
||||
'NetWeight' : "get_net_weight",
|
||||
},
|
||||
'Qto_BodyGeometryValidation' : {
|
||||
'GrossSurfaceArea' : "get_gross_surface_area",
|
||||
'NetSurfaceArea' : "get_net_surface_area",
|
||||
'GrossVolume' : "get_gross_volume",
|
||||
'NetVolume' : "get_net_volume",
|
||||
'SurfaceGenusBeforeFeatures' : None,
|
||||
'SurfaceGenusAfterFeatures' : None,
|
||||
},
|
||||
'Qto_VolumetricStratumBaseQuantities' : {
|
||||
'Area' : "get_net_footprint_area",
|
||||
'Mass' : None,
|
||||
'PlanArea' : "get_net_footprint_area",
|
||||
'Volume' : "get_net_volume",
|
||||
},
|
||||
'Qto_SpatialZoneBaseQuantities' : {
|
||||
'Length' : "get_length",
|
||||
'Width' : "get_width",
|
||||
'Height' : "get_height",
|
||||
},
|
||||
'Qto_ReinforcingElementBaseQuantities' : {
|
||||
'Count' : None,
|
||||
'Length' : "get_length",
|
||||
'Weight' : None,
|
||||
},
|
||||
'Qto_EvaporativeCoolerBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_AirTerminalBoxTypeBaseQuantities' : {
|
||||
'GrossWeight' : None,
|
||||
},
|
||||
'Qto_LaborResourceBaseQuantities' : {
|
||||
'StandardWork' : None,
|
||||
'OvertimeWork' : None,
|
||||
},
|
||||
}
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.doc
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.schema
|
||||
|
||||
@@ -297,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
|
||||
|
||||
@@ -27,10 +27,8 @@ import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.pset as core
|
||||
import blenderbim.core.qto as QtoCore
|
||||
import blenderbim.bim.module.pset.data
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
|
||||
|
||||
class Operator:
|
||||
@@ -142,6 +140,8 @@ class EditPset(bpy.types.Operator, Operator):
|
||||
)
|
||||
else:
|
||||
for key, value in properties.items():
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, float):
|
||||
properties[key] = round(value, 4)
|
||||
elif not isinstance(value, int):
|
||||
@@ -214,55 +214,6 @@ class AddQto(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class CalculateQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.calculate_quantity"
|
||||
bl_label = "Calculate Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Calculates the quantity with a defined formula for this exact entity and quantity"
|
||||
prop: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.qto_calculator = QtoCalculator()
|
||||
obj = context.active_object
|
||||
prop = obj.PsetProperties.properties.get(self.prop)
|
||||
quantity = self.calculate_quantity(obj, context)
|
||||
|
||||
if quantity is None:
|
||||
self.report({"ERROR"}, "Could not calculate quantity")
|
||||
return {"CANCELLED"}
|
||||
|
||||
prop.metadata.float_value = quantity
|
||||
return {"FINISHED"}
|
||||
|
||||
def calculate_quantity(self, obj, context):
|
||||
quantity = self.qto_calculator.calculate_quantity(obj.PsetProperties.active_pset_name, self.prop, obj)
|
||||
if quantity is None:
|
||||
return
|
||||
return round(quantity, 3)
|
||||
|
||||
|
||||
class GuessQuantity(bpy.types.Operator):
|
||||
bl_idname = "bim.guess_quantity"
|
||||
bl_label = "Guess Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = (
|
||||
"Calculate the quantity by guessing the formula from the quantity name. "
|
||||
"Less reliable than Calculate Quantity"
|
||||
)
|
||||
prop: bpy.props.StringProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.qto_calculator = QtoCalculator()
|
||||
obj = context.active_object
|
||||
prop = obj.PsetProperties.properties.get(self.prop)
|
||||
prop.metadata.float_value = self.guess_quantity(obj, context)
|
||||
return {"FINISHED"}
|
||||
|
||||
def guess_quantity(self, obj, context):
|
||||
quantity = self.qto_calculator.guess_quantity(self.prop, [p.name for p in obj.PsetProperties.properties], obj)
|
||||
return round(quantity, 3) if quantity is not None else None
|
||||
|
||||
|
||||
class CopyPropertyToSelection(bpy.types.Operator, Operator):
|
||||
bl_idname = "bim.copy_property_to_selection"
|
||||
bl_label = "Copy Property To Selection"
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import blenderbim.bim.schema
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.doc
|
||||
import ifcopenshell.util.element
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.prop import Attribute, StrProperty
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -103,8 +103,6 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
op.obj_type = obj_type
|
||||
elif not props.active_pset_id:
|
||||
row.label(text=pset["Name"], icon="COPY_ID")
|
||||
if "Qto" in pset["Name"] and "Base" in pset["Name"]:
|
||||
op = row.operator("bim.calculate_all_quantities", icon="MOD_EDGESPLIT", text="")
|
||||
op = row.operator("bim.enable_pset_editing", icon="GREASEPENCIL", text="")
|
||||
op.pset_id = pset_id
|
||||
op.obj = obj_name
|
||||
@@ -160,25 +158,6 @@ def draw_psetqto_ui(context, pset_id, pset, props, layout, obj_type, allow_remov
|
||||
def draw_psetqto_editable_ui(box, props, prop):
|
||||
row = box.row(align=True)
|
||||
draw_property(prop, row, copy_operator="bim.copy_property_to_selection")
|
||||
if prop.metadata.has_calculator:
|
||||
op = row.operator("bim.calculate_quantity", icon="MOD_EDGESPLIT", text="")
|
||||
op.prop = prop.name
|
||||
# Old "guess quantity" feature to be removed once new calculator is comprehensive
|
||||
if (
|
||||
"length" in prop.name.lower()
|
||||
or "width" in prop.name.lower()
|
||||
or "height" in prop.name.lower()
|
||||
or "depth" in prop.name.lower()
|
||||
or "perimeter" in prop.name.lower()
|
||||
):
|
||||
op = row.operator("bim.guess_quantity", icon="IPO_EASE_IN_OUT", text="")
|
||||
op.prop = prop.name
|
||||
elif "area" in prop.name.lower():
|
||||
op = row.operator("bim.guess_quantity", icon="MESH_CIRCLE", text="")
|
||||
op.prop = prop.name
|
||||
elif "volume" in prop.name.lower():
|
||||
op = row.operator("bim.guess_quantity", icon="SPHERE", text="")
|
||||
op.prop = prop.name
|
||||
|
||||
|
||||
class BIM_PT_object_psets(Panel):
|
||||
|
||||
@@ -24,12 +24,13 @@ classes = (
|
||||
operator.CalculateEdgeLengths,
|
||||
operator.CalculateFaceAreas,
|
||||
operator.CalculateObjectVolumes,
|
||||
operator.ExecuteQtoMethod,
|
||||
operator.QuantifyObjects,
|
||||
operator.AssignBaseQto,
|
||||
operator.CalculateAllQuantities,
|
||||
operator.CalculateSingleQuantity,
|
||||
operator.PerformQuantityTakeOff,
|
||||
prop.BIMQtoProperties,
|
||||
ui.BIM_PT_qto_utilities,
|
||||
ui.BIM_PT_qto,
|
||||
ui.BIM_PT_qto_manual,
|
||||
ui.BIM_PT_qto_simple,
|
||||
ui.BIM_PT_qto_cost,
|
||||
)
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,9 +19,11 @@
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def refresh():
|
||||
QtoData.is_loaded = False
|
||||
|
||||
|
||||
class QtoData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
@@ -29,9 +31,9 @@ class QtoData:
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"has_cost_item" : cls.has_cost_item(),
|
||||
"relating_cost_items" : cls.relating_cost_items(),
|
||||
}
|
||||
"has_cost_item": cls.has_cost_item(),
|
||||
"relating_cost_items": cls.relating_cost_items(),
|
||||
}
|
||||
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -53,23 +55,13 @@ class QtoData:
|
||||
for relating_cost_item in relating_cost_items:
|
||||
results.append(
|
||||
{
|
||||
'cost_item_id' : relating_cost_item['cost_item_id'],
|
||||
'cost_item_name' : relating_cost_item['cost_item_name'],
|
||||
'quantity_id' : relating_cost_item['quantity_id'],
|
||||
'quantity_name' : relating_cost_item['quantity_name'],
|
||||
'quantity_value' : relating_cost_item['quantity_value'],
|
||||
'quantity_type' : relating_cost_item['quantity_type'],
|
||||
"cost_item_id": relating_cost_item["cost_item_id"],
|
||||
"cost_item_name": relating_cost_item["cost_item_name"],
|
||||
"quantity_id": relating_cost_item["quantity_id"],
|
||||
"quantity_name": relating_cost_item["quantity_name"],
|
||||
"quantity_value": relating_cost_item["quantity_value"],
|
||||
"quantity_type": relating_cost_item["quantity_type"],
|
||||
}
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.qto as core
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.qto import helper
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
|
||||
|
||||
class CalculateCircleRadius(bpy.types.Operator):
|
||||
@@ -85,115 +84,67 @@ class CalculateObjectVolumes(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ExecuteQtoMethod(bpy.types.Operator):
|
||||
bl_idname = "bim.execute_qto_method"
|
||||
bl_label = "Execute Qto Method"
|
||||
class CalculateSingleQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_single_quantity"
|
||||
bl_label = "Calculate Single Quantity"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
selected_mesh_objects = [o for o in context.selected_objects if o.type == "MESH"]
|
||||
props = context.scene.BIMQtoProperties
|
||||
result = 0
|
||||
if props.qto_methods == "HEIGHT":
|
||||
for obj in selected_mesh_objects:
|
||||
result += helper.calculate_height(obj)
|
||||
elif props.qto_methods == "VOLUME":
|
||||
result = helper.calculate_volumes(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "FORMWORK":
|
||||
result = helper.calculate_formwork_area(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "SIDE_FORMWORK":
|
||||
result = helper.calculate_side_formwork_area(selected_mesh_objects, context)
|
||||
elif props.qto_methods == "NetFootprintArea":
|
||||
result = QtoCalculator().get_net_footprint_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "NetRoofprintArea":
|
||||
result = QtoCalculator().get_net_roofprint_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "LateralArea":
|
||||
result = QtoCalculator().get_lateral_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "TotalSurfaceArea":
|
||||
result = QtoCalculator().get_total_surface_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "OpeningArea":
|
||||
result = QtoCalculator().get_opening_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "GrossTopArea":
|
||||
result = QtoCalculator().get_gross_top_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "NetTopArea":
|
||||
result = QtoCalculator().get_net_top_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "ProjectedArea":
|
||||
result = QtoCalculator().get_projected_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "TotalContactArea":
|
||||
result = QtoCalculator().get_total_contact_area(selected_mesh_objects[0])
|
||||
elif props.qto_methods == "ContactArea":
|
||||
result = QtoCalculator().get_contact_area(selected_mesh_objects[0], selected_mesh_objects[1])
|
||||
props.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class QuantifyObjects(bpy.types.Operator):
|
||||
bl_idname = "bim.quantify_objects"
|
||||
bl_label = "Quantify Objects"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return IfcStore.get_file() and context.selected_objects
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMQtoProperties
|
||||
self.file = IfcStore.get_file()
|
||||
for obj in (o for o in context.selected_objects if o.type == "MESH"):
|
||||
if not obj.BIMObjectProperties.ifc_definition_id:
|
||||
continue
|
||||
result = 0
|
||||
if props.qto_methods == "HEIGHT":
|
||||
result = helper.calculate_height(obj)
|
||||
elif props.qto_methods == "VOLUME":
|
||||
result = helper.calculate_volumes([obj], context)
|
||||
elif props.qto_methods == "FORMWORK":
|
||||
result = helper.calculate_formwork_area([obj], context)
|
||||
elif props.qto_methods == "SIDE_FORMWORK":
|
||||
result = helper.calculate_side_formwork_area([obj], context)
|
||||
if not result:
|
||||
continue
|
||||
result = round(result, 3)
|
||||
qto = ifcopenshell.api.run(
|
||||
"pset.add_qto",
|
||||
self.file,
|
||||
product=self.file.by_id(obj.BIMObjectProperties.ifc_definition_id),
|
||||
name=props.qto_name,
|
||||
)
|
||||
ifcopenshell.api.run("pset.edit_qto", self.file, qto=qto, properties={props.prop_name: result})
|
||||
return {"FINISHED"}
|
||||
|
||||
class AssignBaseQto(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_objects_base_qto"
|
||||
bl_label = "Assign IFC Object Quantity Set"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Assign IFC quantity set to selected object"
|
||||
bl_description = "Calculate a single quantity using a function on the selected objects"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get() and context.selected_objects
|
||||
|
||||
def _execute(self, context):
|
||||
core.assign_objects_base_qto(tool.Ifc, tool.Qto, selected_objects = context.selected_objects)
|
||||
import ifc5d.qto
|
||||
|
||||
props = context.scene.BIMQtoProperties
|
||||
elements = set()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
elements.add(element)
|
||||
|
||||
rules = {
|
||||
"calculators": {
|
||||
props.calculator: {
|
||||
"IfcProduct": {props.qto_name: {props.prop_name: props.calculator_function}},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
results = ifc5d.qto.quantify(ifc_file, elements, rules)
|
||||
ifc5d.qto.edit_qtos(ifc_file, results)
|
||||
return {"FINISHED"}
|
||||
|
||||
class CalculateAllQuantities(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_all_quantities"
|
||||
bl_label = "Calculate All Quantities"
|
||||
|
||||
class PerformQuantityTakeOff(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.perform_quantity_take_off"
|
||||
bl_label = "Perform Quantity Take-off"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Calculate all possible quantities and assign them to selected object"
|
||||
bl_description = "Perform a quantity take off based of a QTO rule configuration"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get() and context.selected_objects
|
||||
return tool.Ifc.get()
|
||||
|
||||
def _execute(self, context):
|
||||
core.calculate_objects_base_quantities(tool.Ifc, tool.Cost, tool.Qto, QtoCalculator(), selected_objects = context.selected_objects)
|
||||
import ifc5d.qto
|
||||
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
if context.selected_objects:
|
||||
elements = set()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
elements.add(element)
|
||||
else:
|
||||
elements = set(tool.Ifc.get().by_type("IfcElement"))
|
||||
|
||||
rules = ifc5d.qto.rules[props.qto_rule]
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
results = ifc5d.qto.quantify(ifc_file, elements, rules)
|
||||
ifc5d.qto.edit_qtos(ifc_file, results)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import ifc5d.qto
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
@@ -31,34 +32,37 @@ from bpy.props import (
|
||||
)
|
||||
|
||||
|
||||
def get_qto_rule(self, context):
|
||||
results = []
|
||||
for rule_id, rule in ifc5d.qto.rules.items():
|
||||
results.append((rule_id, rule["name"], rule["description"]))
|
||||
return results
|
||||
|
||||
|
||||
def get_calculator(self, context):
|
||||
results = []
|
||||
for name, calculator in ifc5d.qto.calculators.items():
|
||||
results.append((name, name, calculator.__doc__))
|
||||
return results
|
||||
|
||||
|
||||
def get_calculator_function(self, context):
|
||||
calculator = ifc5d.qto.calculators[self.calculator]
|
||||
results = []
|
||||
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
|
||||
|
||||
|
||||
class BIMQtoProperties(PropertyGroup):
|
||||
qto_rule: EnumProperty(items=get_qto_rule, name="Qto Rule")
|
||||
calculator: EnumProperty(items=get_calculator, name="Calculator")
|
||||
calculator_function: EnumProperty(items=get_calculator_function, name="Calculator Function")
|
||||
qto_result: StringProperty(default="", name="Qto Result")
|
||||
qto_methods: EnumProperty(
|
||||
items=[
|
||||
("HEIGHT", "Height", "Calculate the Z height of an object"),
|
||||
("VOLUME", "Volume", "Calculate the volume of an object"),
|
||||
(
|
||||
"FORMWORK",
|
||||
"Formwork",
|
||||
"Calculate the exposed formwork for all bottoms and sides (e.g. for beams and slabs) of one or more objects",
|
||||
),
|
||||
(
|
||||
"SIDE_FORMWORK",
|
||||
"Side Formwork",
|
||||
"Calculate the exposed formwork for all sides only (e.g. for columns) of one or more objects",
|
||||
),
|
||||
("NetFootprintArea", "Net footprint area", "Calculate the net footprint area"),
|
||||
("NetRoofprintArea", "Net roofprint area", "Calculate the net roofprint area"),
|
||||
("LateralArea", "Lateral area", "Calculate the lateral area"),
|
||||
("TotalSurfaceArea", "Total surface area", "Calculate the total surface area"),
|
||||
("OpeningArea", "Opening area", "Calculate the opening area"),
|
||||
("GrossTopArea", "Gross top area", "Calculate the gross top area"),
|
||||
("NetTopArea", "Net top area", "Calculate the net top area"),
|
||||
("ProjectedArea", "Projected area", "Calculate the projected area"),
|
||||
("TotalContactArea", "Total contact area", "Get the total contact area"),
|
||||
("ContactArea", "Contact area between two objects", "Get the contact area")
|
||||
],
|
||||
name="Qto Methods",
|
||||
)
|
||||
qto_name: StringProperty(name="Qto Name")
|
||||
prop_name: StringProperty(name="Prop Name")
|
||||
qto_name: StringProperty(name="Qto Name", default="My_Qto")
|
||||
prop_name: StringProperty(name="Prop Name", default="MyDimension")
|
||||
|
||||
@@ -20,18 +20,67 @@ import bpy
|
||||
from blenderbim.bim.module.qto.data import QtoData
|
||||
|
||||
|
||||
class BIM_PT_qto_utilities(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_utilities"
|
||||
class BIM_PT_qto(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto"
|
||||
bl_label = "Quantity Take-off"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "BlenderBIM"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
|
||||
def draw(self, context):
|
||||
if not QtoData.is_loaded:
|
||||
QtoData.load()
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
row = layout.row()
|
||||
if context.selected_objects:
|
||||
row.label(text=f"Quantifying {len(context.selected_objects)} Selected Objects", icon="MOD_EDGESPLIT")
|
||||
else:
|
||||
row.label(text="Quantifying All Objects", icon="MOD_EDGESPLIT")
|
||||
row = layout.row()
|
||||
row.prop(props, "qto_rule", text="")
|
||||
row = layout.row()
|
||||
row.operator("bim.perform_quantity_take_off")
|
||||
|
||||
|
||||
class BIM_PT_qto_manual(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_manual"
|
||||
bl_label = "Manual Quantification"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "calculator")
|
||||
row = layout.row()
|
||||
row.prop(props, "calculator_function", text="Function")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "qto_name", text="")
|
||||
row.prop(props, "prop_name", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_single_quantity")
|
||||
|
||||
|
||||
class BIM_PT_qto_simple(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_simple"
|
||||
bl_label = "Simple Quantity Calculator"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
|
||||
@@ -47,42 +96,44 @@ class BIM_PT_qto_utilities(bpy.types.Panel):
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_object_volumes")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "qto_methods", text="")
|
||||
row.operator("bim.execute_qto_method", icon="PROPERTIES", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(props, "qto_name", text="")
|
||||
row.prop(props, "prop_name", text="")
|
||||
row.operator("bim.quantify_objects", icon="COPYDOWN", text="")
|
||||
class BIM_PT_qto_cost(bpy.types.Panel):
|
||||
bl_idname = "BIM_PT_qto_cost"
|
||||
bl_label = "Parametric Cost Relationships"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_qto"
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.assign_objects_base_qto")
|
||||
def draw(self, context):
|
||||
if not QtoData.is_loaded:
|
||||
QtoData.load()
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_all_quantities", icon="MOD_EDGESPLIT")
|
||||
if not context.selected_objects:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Selected Object")
|
||||
return
|
||||
|
||||
if context.selected_objects:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Relating Cost Item:")
|
||||
|
||||
if QtoData.data['has_cost_item']:
|
||||
for relating_cost_item in QtoData.data['relating_cost_items']:
|
||||
row.label(text=f"\n")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Cost item name:")
|
||||
row.label(text=f"{relating_cost_item['cost_item_name']}")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Quantity name:")
|
||||
row.label(text=f"{relating_cost_item['quantity_name']}")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Quantity value:")
|
||||
row.label(text=f"{relating_cost_item['quantity_value']}")
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"Quantity type:")
|
||||
row.label(text=f"{relating_cost_item['quantity_type']}")
|
||||
row = layout.row(align=True)
|
||||
else:
|
||||
row = layout.row(align=True)
|
||||
row.label(text = f"No cost item related")
|
||||
if not QtoData.data["has_cost_item"]:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Related Cost Item")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Relating Cost Item:")
|
||||
for relating_cost_item in QtoData.data["relating_cost_items"]:
|
||||
row.label(text="\n")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Cost item name:")
|
||||
row.label(text=f"{relating_cost_item['cost_item_name']}")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Quantity name:")
|
||||
row.label(text=f"{relating_cost_item['quantity_name']}")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Quantity value:")
|
||||
row.label(text=f"{relating_cost_item['quantity_value']}")
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Quantity type:")
|
||||
row.label(text=f"{relating_cost_item['quantity_type']}")
|
||||
row = self.layout.row(align=True)
|
||||
|
||||
@@ -37,13 +37,29 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
)
|
||||
|
||||
|
||||
quantitytypes_enum = []
|
||||
quantitytypes_enum = {}
|
||||
|
||||
|
||||
def purge():
|
||||
global quantitytypes_enum
|
||||
quantitytypes_enum = []
|
||||
def setup_quantity_types_enum():
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcConstructionResource.htm#Table-7.3.3.7.1.3.H
|
||||
resources = {
|
||||
"IfcCrewResource": ("IfcQuantityTime",),
|
||||
"IfcLaborResource": ("IfcQuantityTime",),
|
||||
"IfcSubContractResource": ("IfcQuantityTime",),
|
||||
"IfcConstructionEquipmentResource": ("IfcQuantityTime",),
|
||||
"IfcConstructionMaterialResource": (
|
||||
"IfcQuantityVolume",
|
||||
"IfcQuantityArea",
|
||||
"IfcQuantityLength",
|
||||
"IfcQuantityWeight",
|
||||
),
|
||||
"IfcConstructionProductResource": ("IfcQuantityCount",),
|
||||
}
|
||||
for resource, quantities in resources.items():
|
||||
quantitytypes_enum[resource] = [(q, q, "") for q in quantities]
|
||||
|
||||
|
||||
setup_quantity_types_enum()
|
||||
|
||||
|
||||
def updateResourceName(self, context):
|
||||
@@ -63,15 +79,7 @@ def updateResourceName(self, context):
|
||||
|
||||
|
||||
def get_quantity_types(self, context):
|
||||
global quantitytypes_enum
|
||||
if len(quantitytypes_enum) == 0 and IfcStore.get_schema():
|
||||
quantitytypes_enum.extend(
|
||||
[
|
||||
(t.name(), t.name(), "")
|
||||
for t in IfcStore.get_schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
|
||||
]
|
||||
)
|
||||
return quantitytypes_enum
|
||||
return quantitytypes_enum[self.active_resource_class]
|
||||
|
||||
|
||||
def update_active_resource_index(self, context):
|
||||
@@ -88,9 +96,7 @@ def updateResourceUsage(self, context):
|
||||
resource = tool.Ifc.get().by_id(self.ifc_definition_id)
|
||||
if resource.Usage and resource.Usage.ScheduleUsage == self.schedule_usage:
|
||||
return
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={
|
||||
"ScheduleUsage": self.schedule_usage
|
||||
})
|
||||
tool.Resource.run_edit_resource_time(resource, attributes={"ScheduleUsage": self.schedule_usage})
|
||||
tool.Sequence.load_task_properties()
|
||||
tool.Resource.load_resource_properties()
|
||||
tool.Sequence.refresh_task_resources()
|
||||
@@ -127,12 +133,21 @@ class BIMResourceProperties(PropertyGroup):
|
||||
is_editing: BoolProperty(name="Is Editing")
|
||||
active_resource_index: IntProperty(name="Active Resource Index", update=update_active_resource_index)
|
||||
active_resource_id: IntProperty(name="Active Resource Id")
|
||||
active_resource_class: StringProperty(name="Active Resource Type")
|
||||
contracted_resources: StringProperty(name="Contracted Resources", default="[]")
|
||||
is_resource_update_enabled: BoolProperty(name="Is Resource Update Enabled", default=True)
|
||||
is_loaded: BoolProperty(name="Is Editing")
|
||||
active_resource_time_id: IntProperty(name="Active Resource Usage Id")
|
||||
resource_time_attributes: CollectionProperty(name="Resource Usage Attributes", type=Attribute)
|
||||
editing_resource_type: StringProperty(name="Editing Resource Type")
|
||||
editing_resource_type: EnumProperty(
|
||||
name="Editing Resource Type",
|
||||
items=(
|
||||
("ATTRIBUTES", "", ""),
|
||||
("USAGE", "", ""),
|
||||
("COSTS", "", ""),
|
||||
("QUANTITY", "", ""),
|
||||
),
|
||||
)
|
||||
cost_types: EnumProperty(
|
||||
items=[
|
||||
("FIXED", "Fixed", "The cost value is a fixed number"),
|
||||
|
||||
@@ -27,7 +27,6 @@ import ifcopenshell.util.selector
|
||||
from ifcopenshell.util.selector import Selector
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.helper import close_operator_panel
|
||||
from blenderbim.bim.module.group import ui
|
||||
import blenderbim.core.search as core
|
||||
from itertools import cycle
|
||||
@@ -122,14 +121,12 @@ class SelectFilterElements(bpy.types.Operator):
|
||||
filter_groups = tool.Search.get_filter_groups(self.module)
|
||||
global_ids = []
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
global_id = getattr(element, "GlobalId", None)
|
||||
if global_id:
|
||||
if element := tool.Ifc.get_entity(obj):
|
||||
if global_id := getattr(element, "GlobalId", None):
|
||||
global_ids.append(global_id)
|
||||
if len(global_ids) > 50:
|
||||
# Too much to store in a string property
|
||||
name = "globalid-filter-" + ifcopenshell.guid.new()
|
||||
name = f"globalid-filter-{ifcopenshell.guid.new()}"
|
||||
text_data = bpy.data.texts.new(name)
|
||||
text_data.from_string(",".join(global_ids))
|
||||
filter_groups[self.group_index].filters[self.index].value = f"bpy.data.texts['{name}']"
|
||||
@@ -182,8 +179,7 @@ class Search(Operator):
|
||||
|
||||
total_selected = 0
|
||||
for element in results:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
if obj := tool.Ifc.get_object(element):
|
||||
obj.select_set(True)
|
||||
self.report({"INFO"}, f"{len(results)} Results")
|
||||
return {"FINISHED"}
|
||||
@@ -285,8 +281,7 @@ class ColourByProperty(Operator):
|
||||
else:
|
||||
colourscheme[value] = {"colour": next(colours)[0:3], "total": 1}
|
||||
obj.color = (*colourscheme[value]["colour"], 1)
|
||||
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
||||
if areas:
|
||||
if areas := [a for a in context.screen.areas if a.type == "VIEW_3D"]:
|
||||
areas[0].spaces[0].shading.color_type = "OBJECT"
|
||||
|
||||
props.colourscheme.clear()
|
||||
@@ -299,8 +294,7 @@ class ColourByProperty(Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
def store_state(self, context):
|
||||
areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
|
||||
if areas:
|
||||
if areas := [a for a in context.screen.areas if a.type == "VIEW_3D"]:
|
||||
self.transaction_data = {"area": areas[0], "color_type": areas[0].spaces[0].shading.color_type}
|
||||
|
||||
def rollback(self, data):
|
||||
@@ -450,8 +444,7 @@ class SelectIfcClass(Operator):
|
||||
classes = set()
|
||||
predefined_types = set()
|
||||
for obj in objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if element:
|
||||
if element := tool.Ifc.get_entity(obj):
|
||||
classes.add(element.is_a())
|
||||
predefined_types.add(ifcopenshell.util.element.get_predefined_type(element))
|
||||
for cls in classes:
|
||||
@@ -461,9 +454,8 @@ class SelectIfcClass(Operator):
|
||||
and ifcopenshell.util.element.get_predefined_type(element) not in predefined_types
|
||||
):
|
||||
continue
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
if obj := tool.Ifc.get_object(element):
|
||||
tool.Blender.select_object(obj)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -489,10 +481,7 @@ class ToggleFilterSelection(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
props = bpy.context.scene.BIMSearchProperties
|
||||
if self.action == "SELECT":
|
||||
self.selecting_actionbool = True
|
||||
else:
|
||||
self.selecting_actionbool = False
|
||||
self.selecting_actionbool = self.action == "SELECT"
|
||||
if props.filter_type == "CLASSES":
|
||||
for ifc_class in props.filter_classes:
|
||||
ifc_class.is_selected = self.selecting_actionbool
|
||||
@@ -546,11 +535,7 @@ class ActivateIfcClassFilter(Operator):
|
||||
"filter_classes",
|
||||
context.scene.BIMSearchProperties,
|
||||
"filter_classes_index",
|
||||
rows=(
|
||||
20
|
||||
if len(bpy.context.scene.BIMSearchProperties.filter_classes) > 20
|
||||
else len(bpy.context.scene.BIMSearchProperties.filter_classes)
|
||||
),
|
||||
rows=min(len(bpy.context.scene.BIMSearchProperties.filter_classes), 20),
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
||||
@@ -605,11 +590,7 @@ class ActivateContainerFilter(Operator):
|
||||
"filter_container",
|
||||
context.scene.BIMSearchProperties,
|
||||
"filter_container_index",
|
||||
rows=(
|
||||
20
|
||||
if len(bpy.context.scene.BIMSearchProperties.filter_container) > 20
|
||||
else len(bpy.context.scene.BIMSearchProperties.filter_container)
|
||||
),
|
||||
rows=min(len(bpy.context.scene.BIMSearchProperties.filter_container), 20),
|
||||
)
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.toggle_filter_selection", text="Select All").action = "SELECT"
|
||||
|
||||
@@ -1409,9 +1409,13 @@ class LoadProductTasks(bpy.types.Operator):
|
||||
return True
|
||||
|
||||
def execute(self, context):
|
||||
core.load_product_related_tasks(
|
||||
result = core.load_product_related_tasks(
|
||||
tool.Sequence, product=tool.Ifc.get().by_id(context.active_object.BIMObjectProperties.ifc_definition_id)
|
||||
)
|
||||
if isinstance(result, str):
|
||||
self.report({"INFO"}, result)
|
||||
else:
|
||||
self.report({"INFO"}, f"{len(result)} product tasks loaded.")
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
|
||||
@@ -1084,9 +1084,11 @@ class BIM_PT_work_calendars(Panel):
|
||||
class BIM_PT_4D_Tools(Panel):
|
||||
bl_label = "4D Tools"
|
||||
bl_idname = "BIM_PT_4D_Tools"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "UI"
|
||||
bl_category = "4D/5D Toolkit"
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_sequence"
|
||||
|
||||
def draw(self, context):
|
||||
self.props = context.scene.BIMWorkScheduleProperties
|
||||
|
||||
@@ -22,6 +22,7 @@ import blenderbim.bim.helper
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.style as core
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
from blenderbim.bim.module.style.prop import switch_shading
|
||||
from pathlib import Path
|
||||
|
||||
@@ -263,7 +263,6 @@ class Attribute(PropertyGroup):
|
||||
is_optional: BoolProperty(name="Is Optional")
|
||||
is_uri: BoolProperty(name="Is Uri", default=False)
|
||||
is_selected: BoolProperty(name="Is Selected", default=False)
|
||||
has_calculator: BoolProperty(name="Has Calculator", default=False)
|
||||
value_min: FloatProperty(description="This is used to validate int_value and float_value")
|
||||
value_min_constraint: BoolProperty(default=False, description="True if the numerical value has a lower bound")
|
||||
value_max: FloatProperty(description="This is used to validate int_value and float_value")
|
||||
|
||||
@@ -195,18 +195,19 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
pdf_command: StringProperty(name="PDF Command", description='E.g. [["firefox", "path"]]')
|
||||
spreadsheet_command: StringProperty(name="Spreadsheet Command", description='E.g. [["libreoffice", "path"]]')
|
||||
openlca_port: IntProperty(name="OpenLCA IPC Port", default=8080)
|
||||
should_hide_empty_props: BoolProperty(name="Should Hide Empty Properties", default=True)
|
||||
should_setup_workspace: BoolProperty(name="Should Setup Workspace Layout for BIM", default=True)
|
||||
should_hide_empty_props: BoolProperty(name="Hide Empty Properties", default=True)
|
||||
should_setup_workspace: BoolProperty(name="Setup Workspace Layout for BIM", default=True)
|
||||
activate_workspace: BoolProperty(name="Activate BIM Workspace on Startup", default=True)
|
||||
should_setup_toolbar: BoolProperty(
|
||||
name="Always Show Toolbar In 3D Viewport",
|
||||
default=True,
|
||||
description="If disabled, the toolbar will only load when an IFC model is active",
|
||||
)
|
||||
should_play_chaching_sound: BoolProperty(
|
||||
name="Should Make A Cha-Ching Sound When Project Costs Updates", default=False
|
||||
name="Play A Cha-Ching Sound When Project Costs Updates", default=False
|
||||
)
|
||||
lock_grids_on_import: BoolProperty(name="Should Lock Grids By Default", default=True)
|
||||
spatial_elements_unselectable: BoolProperty(name="Should Make Spatial Elements Unselectable By Default", default=True)
|
||||
lock_grids_on_import: BoolProperty(name="Lock Grids By Default", default=True)
|
||||
spatial_elements_unselectable: BoolProperty(name="Make Spatial Elements Unselectable By Default", default=True)
|
||||
decorations_colour: bpy.props.FloatVectorProperty(
|
||||
name="Decorations Colour", subtype="COLOR", default=(1, 1, 1, 1), min=0.0, max=1.0, size=4
|
||||
)
|
||||
@@ -283,6 +284,8 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row = layout.row()
|
||||
row.prop(self, "should_setup_workspace")
|
||||
row = layout.row()
|
||||
row.prop(self, "activate_workspace")
|
||||
row = layout.row()
|
||||
row.prop(self, "should_setup_toolbar")
|
||||
row = layout.row()
|
||||
row.prop(self, "should_play_chaching_sound")
|
||||
@@ -535,6 +538,20 @@ class BIM_PT_tab_status(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_qto(Panel):
|
||||
bl_label = "Quantity Take-off"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "SCHEDULING") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_resources(Panel):
|
||||
bl_label = "Resources"
|
||||
bl_space_type = "PROPERTIES"
|
||||
|
||||
@@ -16,16 +16,30 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
def enable_editing_aggregate(aggregator, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def enable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None:
|
||||
aggregator.enable_editing(obj)
|
||||
|
||||
|
||||
def disable_editing_aggregate(aggregator, obj=None):
|
||||
def disable_editing_aggregate(aggregator: tool.Aggregate, obj: bpy.types.Object) -> None:
|
||||
aggregator.disable_editing(obj)
|
||||
|
||||
|
||||
def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=None):
|
||||
def assign_object(
|
||||
ifc: tool.Ifc,
|
||||
aggregator: tool.Aggregate,
|
||||
collector: tool.Collector,
|
||||
relating_obj: Optional[bpy.types.Object] = None,
|
||||
related_obj: Optional[bpy.types.Object] = None,
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not aggregator.can_aggregate(relating_obj, related_obj):
|
||||
return
|
||||
rel = ifc.run(
|
||||
@@ -37,7 +51,13 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non
|
||||
return rel
|
||||
|
||||
|
||||
def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=None):
|
||||
def unassign_object(
|
||||
ifc: tool.Ifc,
|
||||
aggregate: tool.Aggregate,
|
||||
collector: tool.Collector,
|
||||
relating_obj: Optional[bpy.types.Object] = None,
|
||||
related_obj: Optional[bpy.types.Object] = None,
|
||||
) -> None:
|
||||
related_element = ifc.get_entity(related_obj)
|
||||
container = aggregate.get_container(related_element)
|
||||
if not relating_obj:
|
||||
@@ -52,7 +72,15 @@ def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=No
|
||||
collector.assign(related_obj)
|
||||
|
||||
|
||||
def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None):
|
||||
def add_part_to_object(
|
||||
ifc: tool.Ifc,
|
||||
aggregator: tool.Aggregate,
|
||||
collector: tool.Collector,
|
||||
blender: tool.Blender,
|
||||
obj: bpy.types.Object,
|
||||
part_class: str,
|
||||
part_name: Optional[str] = None,
|
||||
) -> None:
|
||||
part_obj = blender.create_ifc_object(ifc_class=part_class, name=part_name)
|
||||
assign_object(ifc, aggregator, collector, relating_obj=obj, related_obj=part_obj)
|
||||
blender.set_active_object(obj)
|
||||
|
||||
@@ -16,15 +16,33 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Union
|
||||
|
||||
def unlink_material(ifc, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def unlink_material(ifc: tool.Ifc, obj: bpy.types.Material) -> None:
|
||||
ifc.unlink(obj=obj)
|
||||
|
||||
|
||||
def add_material(ifc, material, style, obj=None, name=None, category=None, description=None):
|
||||
def add_material(
|
||||
ifc: tool.Ifc,
|
||||
material: tool.Material,
|
||||
style: tool.Style,
|
||||
obj: Optional[bpy.types.Material] = None,
|
||||
name: Optional[str] = None,
|
||||
category: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
if not obj:
|
||||
obj = material.add_default_material_object(name)
|
||||
ifc_material = ifc.run("material.add_material", name=material.get_name(obj), category=category, description=description)
|
||||
ifc_material = ifc.run(
|
||||
"material.add_material", name=material.get_name(obj), category=category, description=description
|
||||
)
|
||||
ifc.link(ifc_material, obj)
|
||||
ifc_style = style.get_style(obj)
|
||||
if ifc_style:
|
||||
@@ -36,14 +54,16 @@ def add_material(ifc, material, style, obj=None, name=None, category=None, descr
|
||||
return ifc_material
|
||||
|
||||
|
||||
def add_material_set(ifc, material, set_type=None):
|
||||
def add_material_set(ifc: tool.Ifc, material: tool.Material, set_type: str) -> ifcopenshell.entity_instance:
|
||||
ifc_material = ifc.run("material.add_material_set", name="Unnamed", set_type=set_type)
|
||||
if material.is_editing_materials():
|
||||
material.import_material_definitions(material.get_active_material_type())
|
||||
return ifc_material
|
||||
|
||||
|
||||
def remove_material(ifc, material_tool, style, material=None) -> bool:
|
||||
def remove_material(
|
||||
ifc: tool.Ifc, material_tool: tool.Material, style: tool.Style, material: ifcopenshell.entity_instance
|
||||
) -> bool:
|
||||
"""returns True after deleting False,\n
|
||||
returns False if material used in material sets and cannot be removed"""
|
||||
if material_tool.is_material_used_in_sets(material):
|
||||
@@ -58,31 +78,33 @@ def remove_material(ifc, material_tool, style, material=None) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def remove_material_set(ifc, material_tool, material=None):
|
||||
def remove_material_set(ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None:
|
||||
ifc.run("material.remove_material_set", material=material)
|
||||
if material_tool.is_editing_materials():
|
||||
material_tool.import_material_definitions(material_tool.get_active_material_type())
|
||||
|
||||
|
||||
def load_materials(material, material_type=None):
|
||||
def load_materials(material: tool.Material, material_type: str) -> None:
|
||||
material.import_material_definitions(material_type)
|
||||
material.enable_editing_materials()
|
||||
|
||||
|
||||
def disable_editing_materials(material):
|
||||
def disable_editing_materials(material: tool.Material) -> None:
|
||||
material.disable_editing_materials()
|
||||
|
||||
|
||||
def select_by_material(material_tool, spatial, material=None):
|
||||
def select_by_material(
|
||||
material_tool: tool.Material, spatial: tool.Spatial, material: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
spatial.select_products(material_tool.get_elements_by_material(material))
|
||||
|
||||
|
||||
def enable_editing_material(material_tool, material):
|
||||
def enable_editing_material(material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None:
|
||||
material_tool.load_material_attributes(material)
|
||||
material_tool.enable_editing_material(material)
|
||||
|
||||
|
||||
def edit_material(ifc, material_tool, material):
|
||||
def edit_material(ifc: tool.Ifc, material_tool: tool.Material, material: ifcopenshell.entity_instance) -> None:
|
||||
attributes = material_tool.get_material_attributes()
|
||||
ifc.run("material.edit_material", material=material, attributes=attributes)
|
||||
material_tool.sync_blender_material_name(material)
|
||||
@@ -92,11 +114,13 @@ def edit_material(ifc, material_tool, material):
|
||||
material_tool.enable_editing_materials()
|
||||
|
||||
|
||||
def disable_editing_material(material_tool):
|
||||
def disable_editing_material(material_tool: tool.Material) -> None:
|
||||
material_tool.disable_editing_material()
|
||||
|
||||
|
||||
def assign_material(ifc, material_tool, material_type, objects):
|
||||
def assign_material(
|
||||
ifc: tool.Ifc, material_tool: tool.Material, material_type: Union[str, None], objects: list[bpy.types.Object]
|
||||
) -> None:
|
||||
material_type = material_type or material_tool.get_active_object_material()
|
||||
material = material_tool.get_active_material()
|
||||
for obj in objects:
|
||||
@@ -109,7 +133,7 @@ def assign_material(ifc, material_tool, material_type, objects):
|
||||
material_tool.add_material_to_set(material_set=assigned_material, material=material)
|
||||
|
||||
|
||||
def unassign_material(ifc, material_tool, objects):
|
||||
def unassign_material(ifc: tool.Ifc, material_tool: tool.Material, objects: list[bpy.types.Object]) -> None:
|
||||
for obj in objects:
|
||||
element = ifc.get_entity(obj)
|
||||
if element:
|
||||
@@ -125,7 +149,9 @@ def unassign_material(ifc, material_tool, objects):
|
||||
ifc.run("material.unassign_material", products=[element])
|
||||
|
||||
|
||||
def patch_non_parametric_mep_segment(ifc, material_tool, profile_tool, obj):
|
||||
def patch_non_parametric_mep_segment(
|
||||
ifc: tool.Ifc, material_tool: tool.Material, profile_tool: tool.Profile, obj: bpy.types.Object
|
||||
) -> None:
|
||||
element = ifc.get_entity(obj)
|
||||
if not element:
|
||||
return
|
||||
|
||||
@@ -16,53 +16,15 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
def calculate_circle_radius(qto, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def calculate_circle_radius(qto: tool.Qto, obj: bpy.types.Object) -> float:
|
||||
result = qto.get_radius_of_selected_vertices(obj)
|
||||
qto.set_qto_result(result)
|
||||
return result
|
||||
|
||||
|
||||
def assign_objects_base_qto(ifc, qto, selected_objects):
|
||||
for obj in selected_objects:
|
||||
assign_object_base_qto(ifc, qto, obj)
|
||||
|
||||
|
||||
def assign_object_base_qto(ifc, qto, obj):
|
||||
product = ifc.get_entity(obj)
|
||||
if not product:
|
||||
return
|
||||
base_quantity_name = qto.get_applicable_base_quantity_name(product)
|
||||
if not base_quantity_name:
|
||||
return
|
||||
ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name=base_quantity_name,
|
||||
)
|
||||
|
||||
|
||||
def calculate_objects_base_quantities(ifc, cost, qto, calculator, selected_objects):
|
||||
if selected_objects:
|
||||
for obj in selected_objects:
|
||||
calculate_object_base_quantities(ifc, cost, qto, calculator, obj)
|
||||
|
||||
|
||||
def calculate_object_base_quantities(ifc, cost, qto, calculator, obj):
|
||||
product = ifc.get_entity(obj)
|
||||
if not product:
|
||||
return
|
||||
base_quantity_name = qto.get_applicable_base_quantity_name(product)
|
||||
if not base_quantity_name:
|
||||
print(f"There is no base quantity")
|
||||
return
|
||||
base_qto = qto.get_base_qto(product)
|
||||
if not base_qto:
|
||||
base_qto = ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name=base_quantity_name,
|
||||
)
|
||||
calculated_quantities = qto.get_calculated_object_quantities(calculator, base_quantity_name, obj)
|
||||
ifc.run("pset.edit_qto", qto=base_qto, properties=calculated_quantities)
|
||||
cost.update_cost_items(product=product)
|
||||
@@ -533,20 +533,6 @@ def go_to_task(sequence: tool.Sequence, task: ifcopenshell.entity_instance) -> U
|
||||
return "Work schedule is not active"
|
||||
|
||||
|
||||
def highlight_product_related_task(sequence: tool.Sequence, spatial: tool.Spatial, product_type=None) -> None:
|
||||
products = spatial.get_selected_products()
|
||||
if products:
|
||||
if product_type == "Output":
|
||||
tasks = sequence.find_related_output_tasks(products[0])
|
||||
elif product_type == "Input":
|
||||
tasks = sequence.find_related_input_tasks(products[0])
|
||||
for task in tasks:
|
||||
work_schedule = sequence.get_work_schedule(task)
|
||||
is_work_schedule_active = sequence.is_work_schedule_active(work_schedule)
|
||||
if is_work_schedule_active:
|
||||
sequence.go_to_task(task)
|
||||
|
||||
|
||||
def guess_date_range(sequence: tool.Sequence, work_schedule: ifcopenshell.entity_instance) -> None:
|
||||
start, finish = sequence.guess_date_range(work_schedule)
|
||||
sequence.update_visualisation_date(start, finish)
|
||||
@@ -593,17 +579,20 @@ def generate_gantt_chart(sequence: tool.Sequence, work_schedule: ifcopenshell.en
|
||||
sequence.generate_gantt_browser_chart(json, work_schedule)
|
||||
|
||||
|
||||
def load_product_related_tasks(sequence: tool.Sequence, product: ifcopenshell.entity_instance) -> Union[None, str]:
|
||||
def load_product_related_tasks(
|
||||
sequence: tool.Sequence, product: ifcopenshell.entity_instance
|
||||
) -> Union[list[ifcopenshell.entity_instance], str]:
|
||||
filter_by_schedule = sequence.is_filter_by_active_schedule()
|
||||
if filter_by_schedule:
|
||||
work_schedule = sequence.get_active_work_schedule()
|
||||
if work_schedule:
|
||||
task_inputs, task_ouputs = sequence.get_tasks_for_product(product, work_schedule)
|
||||
else:
|
||||
return "No active work schedule"
|
||||
return "No active work schedule."
|
||||
else:
|
||||
task_inputs, task_ouputs = sequence.get_tasks_for_product(product)
|
||||
sequence.load_product_related_tasks(task_inputs, task_ouputs)
|
||||
return task_inputs + task_ouputs
|
||||
|
||||
|
||||
def reorder_task_nesting(
|
||||
|
||||
@@ -16,8 +16,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Optional, Any
|
||||
|
||||
def add_style(ifc, style, obj=None):
|
||||
if TYPE_CHECKING:
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
def add_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material) -> ifcopenshell.entity_instance:
|
||||
element = ifc.run("style.add_style", name=style.get_name(obj))
|
||||
ifc.link(element, obj)
|
||||
if style.can_support_rendering_style(obj):
|
||||
@@ -33,18 +41,26 @@ def add_style(ifc, style, obj=None):
|
||||
return element
|
||||
|
||||
|
||||
def add_external_style(ifc, style, obj, attributes):
|
||||
def add_external_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material, attributes: dict[str, Any]) -> None:
|
||||
element = style.get_style(obj)
|
||||
ifc.run(
|
||||
"style.add_surface_style", style=element, ifc_class="IfcExternallyDefinedSurfaceStyle", attributes=attributes
|
||||
)
|
||||
|
||||
|
||||
def update_external_style(ifc, style, external_style, attributes):
|
||||
# TODO: unused `style` argument?
|
||||
def update_external_style(
|
||||
ifc: tool.Ifc,
|
||||
style: ifcopenshell.entity_instance,
|
||||
external_style: ifcopenshell.entity_instance,
|
||||
attributes: dict[str, Any],
|
||||
) -> None:
|
||||
ifc.run("style.edit_surface_style", style=external_style, attributes=attributes)
|
||||
|
||||
|
||||
def remove_style(ifc, material, style_tool, style=None):
|
||||
def remove_style(
|
||||
ifc: tool.Ifc, material: tool.Material, style_tool: tool.Style, style: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
obj = ifc.get_object(style)
|
||||
ifc.unlink(obj=obj, element=style)
|
||||
ifc.run("style.remove_style", style=style)
|
||||
@@ -54,7 +70,7 @@ def remove_style(ifc, material, style_tool, style=None):
|
||||
style_tool.import_presentation_styles(style_tool.get_active_style_type())
|
||||
|
||||
|
||||
def update_style_colours(ifc, style, obj=None, verbose=False):
|
||||
def update_style_colours(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material, verbose: bool = False) -> None:
|
||||
element = style.get_style(obj)
|
||||
|
||||
if style.can_support_rendering_style(obj):
|
||||
@@ -91,7 +107,9 @@ def update_style_colours(ifc, style, obj=None, verbose=False):
|
||||
style.record_shading(obj)
|
||||
|
||||
|
||||
def update_style_textures(ifc, style, obj=None, representation=None):
|
||||
def update_style_textures(
|
||||
ifc: tool.Ifc, style: tool.Style, obj: ifcopenshell.entity_instance, representation: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
element = style.get_style(obj)
|
||||
|
||||
uv_maps = style.get_uv_maps(representation)
|
||||
@@ -111,34 +129,34 @@ def update_style_textures(ifc, style, obj=None, representation=None):
|
||||
ifc.run("style.remove_surface_style", style=texture_style)
|
||||
|
||||
|
||||
def unlink_style(ifc, style=None):
|
||||
def unlink_style(ifc: tool.Ifc, style: ifcopenshell.entity_instance) -> None:
|
||||
obj = ifc.get_object(style)
|
||||
ifc.unlink(obj=obj, element=style)
|
||||
|
||||
|
||||
def enable_editing_style(style, obj=None):
|
||||
def enable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None:
|
||||
style.enable_editing(obj)
|
||||
style.import_surface_attributes(style.get_style(obj), obj)
|
||||
|
||||
|
||||
def disable_editing_style(style, obj=None):
|
||||
def disable_editing_style(style: tool.Style, obj: bpy.types.Material) -> None:
|
||||
style.disable_editing(obj)
|
||||
|
||||
|
||||
def edit_style(ifc, style, obj=None):
|
||||
def edit_style(ifc: tool.Ifc, style: tool.Style, obj: bpy.types.Material) -> None:
|
||||
attributes = style.export_surface_attributes(obj)
|
||||
ifc.run("style.edit_presentation_style", style=style.get_style(obj), attributes=attributes)
|
||||
style.disable_editing(obj)
|
||||
|
||||
|
||||
def load_styles(style, style_type=None):
|
||||
def load_styles(style: tool.Style, style_type: str) -> None:
|
||||
style.import_presentation_styles(style_type)
|
||||
style.enable_editing_styles()
|
||||
|
||||
|
||||
def disable_editing_styles(style):
|
||||
def disable_editing_styles(style: tool.Style) -> None:
|
||||
style.disable_editing_styles()
|
||||
|
||||
|
||||
def select_by_style(style_tool, spatial, style=None):
|
||||
def select_by_style(style_tool: tool.Style, spatial: tool.Spatial, style: ifcopenshell.entity_instance) -> None:
|
||||
spatial.select_products(style_tool.get_elements_by_style(style))
|
||||
|
||||
@@ -637,16 +637,9 @@ class Pset:
|
||||
@interface
|
||||
class Qto:
|
||||
def get_radius_of_selected_vertices(cls, obj): pass
|
||||
def set_qto_result(cls, result): pass
|
||||
def get_applicable_quantity_names(cls, qto_name): pass
|
||||
def get_applicable_base_quantity_name(cls, product): pass
|
||||
def get_rounded_value(cls, new_quantity): pass
|
||||
def get_calculated_object_quantities(cls, calculator, baste_qto, object): pass
|
||||
def add_object_base_qto(cls, object): pass
|
||||
def add_product_base_qto(cls, product): pass
|
||||
def get_new_calculated_quantity(cls, qto_name, quantity_name, object): pass
|
||||
def get_new_guessed_quantity(cls, object, qto_name, quantity_name, ): pass
|
||||
def get_related_cost_item_quantities(cls, product): pass
|
||||
def get_rounded_value(cls, new_quantity): pass
|
||||
def set_qto_result(cls, result): pass
|
||||
|
||||
|
||||
@interface
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell.util.element
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
@@ -46,19 +47,21 @@ class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj):
|
||||
def disable_editing(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMObjectAggregateProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
def enable_editing(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMObjectAggregateProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def get_container(cls, element):
|
||||
def get_container(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_container(element)
|
||||
|
||||
@classmethod
|
||||
def get_relating_object(cls, related_element):
|
||||
def get_relating_object(
|
||||
cls, related_element: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for rel in related_element.Decomposes:
|
||||
if rel.is_a("IfcRelAggregates"):
|
||||
return rel.RelatingObject
|
||||
|
||||
@@ -196,6 +196,21 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
return False
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def is_valid_data_block(cls, data_block: bpy.types.ID) -> bool:
|
||||
"""Check if Blender data-block is still valid.
|
||||
|
||||
If Blender data-block (e.g. an Object) is removed then it's
|
||||
python object gets invalidated and accessing any of it's attributes
|
||||
leads to ReferenceError: StructRNA of type Object has been removed.
|
||||
This method helps avoiding try / except ReferenceError constructions.
|
||||
"""
|
||||
try:
|
||||
data_block.bl_rna
|
||||
return True
|
||||
except ReferenceError:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def show_info_message(cls, text: str, message_type: Literal["INFO", "ERROR"] = "INFO") -> None:
|
||||
"""useful for showing error messages outside blender operators
|
||||
@@ -459,6 +474,13 @@ class Blender(blenderbim.core.tool.Blender):
|
||||
obj.select_set(False)
|
||||
context.view_layer.objects.active = active_object
|
||||
active_object.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def select_object(cls, obj: bpy.types.Object):
|
||||
try:
|
||||
obj.select_set(True)
|
||||
except RuntimeError: # Trying to select a hidden object throws an error
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def set_objects_selection(
|
||||
|
||||
@@ -40,7 +40,7 @@ import blenderbim.bim.import_ifc
|
||||
from math import radians, pi
|
||||
from mathutils import Vector, Matrix
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from typing import Union
|
||||
from typing import Union, Iterable
|
||||
|
||||
|
||||
class Geometry(blenderbim.core.tool.Geometry):
|
||||
@@ -755,21 +755,35 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@classmethod
|
||||
def reload_representation(cls, obj):
|
||||
"""reload `obj` active representation"""
|
||||
if not obj.data:
|
||||
return
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
)
|
||||
def reload_representation(cls, obj_or_objs: Union[bpy.types.Object, Iterable[bpy.types.Object]]) -> None:
|
||||
"""Reload object/objects active representation.
|
||||
|
||||
Ensures that same representations won't be reloaded multiple times.
|
||||
"""
|
||||
objs = obj_or_objs if isinstance(obj_or_objs, Iterable) else [obj_or_objs]
|
||||
|
||||
# Filter out unique meshes to avoid
|
||||
# reloading the same representation multiple times.
|
||||
meshes_to_objects: dict[bpy.types.Mesh, bpy.types.Object]
|
||||
meshes_to_objects = dict()
|
||||
for obj in objs:
|
||||
mesh = obj.data
|
||||
if not mesh:
|
||||
continue
|
||||
meshes_to_objects.setdefault(mesh, obj)
|
||||
|
||||
for obj in meshes_to_objects.values():
|
||||
representation = tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
blenderbim.core.geometry.switch_representation(
|
||||
tool.Ifc,
|
||||
tool.Geometry,
|
||||
obj=obj,
|
||||
representation=representation,
|
||||
should_reload=True,
|
||||
is_global=True,
|
||||
should_sync_changes_first=False,
|
||||
apply_openings=True,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def remove_representation_item(cls, representation_item):
|
||||
|
||||
@@ -19,43 +19,45 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.material
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
from typing import Union, Any
|
||||
|
||||
|
||||
class Material(blenderbim.core.tool.Material):
|
||||
@classmethod
|
||||
def add_default_material_object(cls, name):
|
||||
def add_default_material_object(cls, name: Union[str, None]) -> bpy.types.Material:
|
||||
return bpy.data.materials.new(name or "Default")
|
||||
|
||||
@classmethod
|
||||
def delete_object(cls, obj):
|
||||
def delete_object(cls, obj: bpy.types.Material) -> None:
|
||||
bpy.data.materials.remove(obj)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_materials(cls):
|
||||
def disable_editing_materials(cls) -> None:
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing_materials(cls):
|
||||
def enable_editing_materials(cls) -> None:
|
||||
bpy.context.scene.BIMMaterialProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def get_active_material_type(cls):
|
||||
def get_active_material_type(cls) -> str:
|
||||
return bpy.context.scene.BIMMaterialProperties.material_type
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_material(cls, material):
|
||||
def get_elements_by_material(cls, material: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, obj):
|
||||
def get_name(cls, obj: bpy.types.Material) -> str:
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
def import_material_definitions(cls, material_type):
|
||||
def import_material_definitions(cls, material_type: str) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
expanded_categories = {m.name for m in props.materials if m.is_expanded}
|
||||
props.materials.clear()
|
||||
@@ -89,11 +91,11 @@ class Material(blenderbim.core.tool.Material):
|
||||
new.total_elements = len(ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material))
|
||||
|
||||
@classmethod
|
||||
def is_editing_materials(cls):
|
||||
def is_editing_materials(cls) -> bool:
|
||||
return bpy.context.scene.BIMMaterialProperties.is_editing
|
||||
|
||||
@classmethod
|
||||
def is_material_used_in_sets(cls, material):
|
||||
def is_material_used_in_sets(cls, material: ifcopenshell.entity_instance) -> bool:
|
||||
for inverse in tool.Ifc.get().get_inverse(material):
|
||||
if inverse.is_a() in [
|
||||
"IfcMaterialProfile",
|
||||
@@ -105,48 +107,50 @@ class Material(blenderbim.core.tool.Material):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def load_material_attributes(cls, material):
|
||||
def load_material_attributes(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.material_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(material, props.material_attributes)
|
||||
|
||||
@classmethod
|
||||
def enable_editing_material(cls, material):
|
||||
def enable_editing_material(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = material.id()
|
||||
props.editing_material_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
def get_material_attributes(cls):
|
||||
def get_material_attributes(cls) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(bpy.context.scene.BIMMaterialProperties.material_attributes)
|
||||
|
||||
@classmethod
|
||||
def disable_editing_material(cls):
|
||||
def disable_editing_material(cls) -> None:
|
||||
props = bpy.context.scene.BIMMaterialProperties
|
||||
props.active_material_id = 0
|
||||
props.editing_material_type = ""
|
||||
|
||||
@classmethod
|
||||
def get_type(cls, element):
|
||||
def get_type(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_type(element)
|
||||
|
||||
@classmethod
|
||||
def get_active_object_material(cls):
|
||||
def get_active_object_material(cls) -> Union[str, None]:
|
||||
active_obj = bpy.context.active_object
|
||||
if not active_obj:
|
||||
return
|
||||
return active_obj.BIMObjectMaterialProperties.material_type
|
||||
|
||||
@classmethod
|
||||
def get_active_material(cls):
|
||||
def get_active_material(cls) -> ifcopenshell.entity_instance:
|
||||
return tool.Ifc.get().by_id(int(bpy.context.active_object.BIMObjectMaterialProperties.material))
|
||||
|
||||
@classmethod
|
||||
def get_material(cls, element, should_inherit=False):
|
||||
def get_material(
|
||||
cls, element: ifcopenshell.entity_instance, should_inherit: bool = False
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.element.get_material(element, should_inherit=should_inherit)
|
||||
|
||||
@classmethod
|
||||
def is_a_material_set(cls, material):
|
||||
def is_a_material_set(cls, material: ifcopenshell.entity_instance) -> bool:
|
||||
return material.is_a() in [
|
||||
"IfcMaterialConstituentSet",
|
||||
"IfcMaterialLayerSet",
|
||||
@@ -154,7 +158,9 @@ class Material(blenderbim.core.tool.Material):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def add_material_to_set(cls, material_set, material):
|
||||
def add_material_to_set(
|
||||
cls, material_set: ifcopenshell.entity_instance, material: ifcopenshell.entity_instance
|
||||
) -> None:
|
||||
if material_set.is_a("IfcMaterialConstituentSet"):
|
||||
if not material_set.MaterialConstituents:
|
||||
tool.Ifc.run(
|
||||
@@ -195,7 +201,7 @@ class Material(blenderbim.core.tool.Material):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def has_material_profile(cls, element):
|
||||
def has_material_profile(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
material = cls.get_material(element, should_inherit=False)
|
||||
inherited_material = cls.get_material(element, should_inherit=True)
|
||||
if material and "Profile" in material.is_a():
|
||||
@@ -205,11 +211,13 @@ class Material(blenderbim.core.tool.Material):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def is_a_flow_segment(cls, element):
|
||||
def is_a_flow_segment(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
return element.is_a("IfcFlowSegment")
|
||||
|
||||
@classmethod
|
||||
def replace_material_with_material_profile(cls, element):
|
||||
def replace_material_with_material_profile(
|
||||
cls, element: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
old_material = cls.get_material(element, should_inherit=False)
|
||||
old_inherited_material = cls.get_material(element, should_inherit=True)
|
||||
material = old_material if old_material and old_material.is_a("IfcMaterial") else None
|
||||
@@ -225,20 +233,14 @@ class Material(blenderbim.core.tool.Material):
|
||||
return material_profile
|
||||
|
||||
@classmethod
|
||||
def update_elements_using_material(cls, material):
|
||||
def update_elements_using_material(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
# update elements that are using this material
|
||||
elements = ifcopenshell.util.element.get_elements_by_material(tool.Ifc.get(), material)
|
||||
# filter only unique representations to avoid reloading same representations multiple times
|
||||
meshes_to_objects = dict()
|
||||
for e in elements:
|
||||
obj = tool.Ifc.get_object(e)
|
||||
mesh = obj.data
|
||||
meshes_to_objects[mesh] = obj
|
||||
for obj in meshes_to_objects.values():
|
||||
tool.Geometry.reload_representation(obj)
|
||||
objects = [tool.Ifc.get_object(e) for e in elements]
|
||||
tool.Geometry.reload_representation(objects)
|
||||
|
||||
@classmethod
|
||||
def sync_blender_material_name(cls, material):
|
||||
def sync_blender_material_name(cls, material: ifcopenshell.entity_instance) -> None:
|
||||
name = material.Name or "Unnamed"
|
||||
obj = tool.Ifc.get_object(material)
|
||||
if obj:
|
||||
@@ -251,7 +253,7 @@ class Material(blenderbim.core.tool.Material):
|
||||
obj.name = name
|
||||
|
||||
@classmethod
|
||||
def get_style(cls, material):
|
||||
def get_style(cls, material: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for material_representation in material.HasRepresentation:
|
||||
for representation in material_representation.Representations:
|
||||
for item in representation.Items:
|
||||
|
||||
@@ -17,18 +17,23 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import PIL.ImageDraw
|
||||
from blenderbim.bim.module.model.decorator import ProfileDecorator
|
||||
from typing import Union
|
||||
|
||||
|
||||
class Profile(blenderbim.core.tool.Profile):
|
||||
@classmethod
|
||||
def draw_image_for_ifc_profile(cls, draw, profile, size):
|
||||
def draw_image_for_ifc_profile(
|
||||
cls, draw: PIL.ImageDraw.ImageDraw, profile: ifcopenshell.entity_instance, size: float
|
||||
) -> None:
|
||||
"""generates image based on `profile` using `PIL.ImageDraw`"""
|
||||
settings = ifcopenshell.geom.settings()
|
||||
settings.set(settings.INCLUDE_CURVES, True)
|
||||
@@ -57,11 +62,11 @@ class Profile(blenderbim.core.tool.Profile):
|
||||
draw.line((tuple(grouped_verts[e[0]]), tuple(grouped_verts[e[1]])), fill="white", width=2)
|
||||
|
||||
@classmethod
|
||||
def is_editing_profile(cls):
|
||||
return ProfileDecorator.installed
|
||||
def is_editing_profile(cls) -> bool:
|
||||
return bool(ProfileDecorator.installed)
|
||||
|
||||
@classmethod
|
||||
def get_profile(cls, element):
|
||||
def get_profile(cls, element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
representations = element.Representation
|
||||
for representation in representations.Representations:
|
||||
if not representation.is_a("IfcShapeRepresentation"):
|
||||
@@ -74,7 +79,7 @@ class Profile(blenderbim.core.tool.Profile):
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def get_model_profiles(cls):
|
||||
def get_model_profiles(cls) -> list[ifcopenshell.entity_instance]:
|
||||
return tool.Ifc.get().by_type("IfcProfileDef")
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -23,7 +23,6 @@ import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.schema
|
||||
from typing import Union
|
||||
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
|
||||
|
||||
|
||||
class Pset(blenderbim.core.tool.Pset):
|
||||
@@ -177,7 +176,6 @@ class Pset(blenderbim.core.tool.Pset):
|
||||
metadata.is_null = data.get(prop_template.Name, None) is None
|
||||
metadata.is_optional = True
|
||||
metadata.is_uri = prop_template.PrimaryMeasureType == "IfcURIReference"
|
||||
metadata.has_calculator = bool(mapper.get(pset_template.Name, {}).get(prop_template.Name, None))
|
||||
metadata.data_type = cls.get_prop_template_primitive_type(prop_template)
|
||||
|
||||
special_type = ""
|
||||
|
||||
@@ -16,20 +16,17 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from types import ClassMethodDescriptorType
|
||||
import bpy
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.bim.schema
|
||||
import blenderbim.tool as tool
|
||||
import ifcopenshell
|
||||
from mathutils import Vector
|
||||
from ifcopenshell import util
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator, QuanityTypes
|
||||
from blenderbim.bim.module.pset.calc_quantity_function_mapper import mapper
|
||||
import blenderbim.bim.schema
|
||||
from mathutils import Vector
|
||||
from typing import Optional, Union, Literal
|
||||
|
||||
QuantityTypes = Literal["Q_LENGTH", "Q_AREA", "Q_VOLUME"]
|
||||
|
||||
class Qto(blenderbim.core.tool.Qto):
|
||||
@classmethod
|
||||
@@ -45,77 +42,17 @@ class Qto(blenderbim.core.tool.Qto):
|
||||
def set_qto_result(cls, result: float) -> None:
|
||||
bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
|
||||
@classmethod
|
||||
def add_object_base_qto(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
product = tool.Ifc.get_entity(obj)
|
||||
return cls.add_product_base_qto(product)
|
||||
|
||||
@classmethod
|
||||
def add_product_base_qto(cls, product: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
base_quantity_name = cls.get_applicable_base_quantity_name(product)
|
||||
if base_quantity_name:
|
||||
return tool.Ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name=base_quantity_name,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_applicable_quantity_names(cls, qto_name: str) -> list[str]:
|
||||
pset_template = blenderbim.bim.schema.ifc.psetqto.get_by_name(qto_name)
|
||||
return (
|
||||
[property.Name for property in pset_template.HasPropertyTemplates]
|
||||
if hasattr(pset_template, "HasPropertyTemplates")
|
||||
else []
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_applicable_base_quantity_name(
|
||||
cls, product: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> Union[str, None]:
|
||||
if not product:
|
||||
return
|
||||
applicable_qto_names = blenderbim.bim.schema.ifc.psetqto.get_applicable_names(
|
||||
product.is_a(), ifcopenshell.util.element.get_predefined_type(product), qto_only=True
|
||||
)
|
||||
return next((qto_name for qto_name in applicable_qto_names if "Qto_" in qto_name and "Base" in qto_name), None)
|
||||
|
||||
@classmethod
|
||||
def get_new_calculated_quantity(cls, qto_name: str, quantity_name: str, obj: bpy.types.Object) -> float:
|
||||
return QtoCalculator().calculate_quantity(qto_name, quantity_name, obj)
|
||||
|
||||
@classmethod
|
||||
def get_new_guessed_quantity(
|
||||
cls, obj: bpy.types.Object, quantity_name: str, alternative_prop_names: list[str]
|
||||
) -> Union[float, None]:
|
||||
return QtoCalculator().guess_quantity(quantity_name, alternative_prop_names, obj)
|
||||
|
||||
@classmethod
|
||||
def get_rounded_value(cls, new_quantity: float) -> float:
|
||||
return round(new_quantity, 3)
|
||||
|
||||
@classmethod
|
||||
def get_calculated_object_quantities(
|
||||
cls, calculator: QtoCalculator, qto_name: str, obj: bpy.types.Object
|
||||
) -> dict[str, float]:
|
||||
return {
|
||||
quantity_name: cls.get_rounded_value(value)
|
||||
for quantity_name in cls.get_applicable_quantity_names(qto_name) or []
|
||||
if cls.has_calculator(qto_name, quantity_name)
|
||||
and (value := calculator.calculate_quantity(qto_name, quantity_name, obj)) is not None
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def has_calculator(cls, qto_name: str, quantity_name: str) -> bool:
|
||||
return bool(mapper.get(qto_name, {}).get(quantity_name, None))
|
||||
|
||||
@classmethod
|
||||
def convert_to_project_units(
|
||||
cls,
|
||||
value: float,
|
||||
qto_name: Optional[str] = None,
|
||||
quantity_name: Optional[str] = None,
|
||||
quantity_type: Optional[QuanityTypes] = None,
|
||||
quantity_type: Optional[QuantityTypes] = None,
|
||||
) -> Union[float, None]:
|
||||
"""You can either specify `quantity_type` or provide `qto_name/quantity_name`
|
||||
to let method figure the `quantity_type` from the templates
|
||||
@@ -147,38 +84,27 @@ class Qto(blenderbim.core.tool.Qto):
|
||||
)
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def get_guessed_quantities(
|
||||
cls, obj: bpy.types.Object, pset_qto_properties: list[ifcopenshell.entity_instance]
|
||||
) -> dict[str, float]:
|
||||
calculated_quantities = {}
|
||||
for pset_qto_property in pset_qto_properties:
|
||||
quantity_name = pset_qto_property.get_info()["Name"]
|
||||
alternative_prop_names = [p.get_info()["Name"] for p in pset_qto_properties]
|
||||
|
||||
new_quantity = cls.get_new_guessed_quantity(obj, quantity_name, alternative_prop_names)
|
||||
|
||||
if not new_quantity:
|
||||
new_quantity = 0
|
||||
else:
|
||||
new_quantity = cls.get_rounded_value(new_quantity)
|
||||
|
||||
calculated_quantities[quantity_name] = new_quantity
|
||||
|
||||
return calculated_quantities
|
||||
|
||||
@classmethod
|
||||
def get_base_qto(cls, product: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not hasattr(product, "IsDefinedBy"):
|
||||
return
|
||||
base_qto_definition = None
|
||||
base_qto_definition_name: Union[str, None] = None
|
||||
for rel in product.IsDefinedBy or []:
|
||||
if not (
|
||||
rel.is_a("IfcRelDefinesByProperties")
|
||||
and "Base" in rel.RelatingPropertyDefinition.Name
|
||||
and "Qto_" in rel.RelatingPropertyDefinition.Name
|
||||
):
|
||||
definition = rel.RelatingPropertyDefinition
|
||||
if not rel.is_a("IfcRelDefinesByProperties"):
|
||||
continue
|
||||
return rel.RelatingPropertyDefinition
|
||||
definition = rel.RelatingPropertyDefinition
|
||||
definition_name = definition.Name
|
||||
if "Qto_" not in definition_name:
|
||||
continue
|
||||
if "Base" in definition_name:
|
||||
return definition
|
||||
if base_qto_definition and "BodyGeometryValidation" not in base_qto_definition_name:
|
||||
continue
|
||||
base_qto_definition = definition
|
||||
base_qto_definition_name = definition_name
|
||||
return base_qto_definition
|
||||
|
||||
@classmethod
|
||||
def get_related_cost_item_quantities(cls, product: ifcopenshell.entity_instance) -> list[dict]:
|
||||
|
||||
@@ -264,6 +264,7 @@ class Resource(blenderbim.core.tool.Resource):
|
||||
props = bpy.context.scene.BIMResourceProperties
|
||||
props.active_resource_id = resource.id()
|
||||
props.editing_resource_type = "QUANTITY"
|
||||
props.active_resource_class = resource.is_a()
|
||||
|
||||
@classmethod
|
||||
def enable_editing_resource_quantity(cls, resource_quantity: ifcopenshell.entity_instance) -> None:
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.placement
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.aggregate
|
||||
import blenderbim.core.geometry
|
||||
@@ -29,17 +30,17 @@ import blenderbim.core.style
|
||||
import blenderbim.tool as tool
|
||||
from mathutils import Vector
|
||||
from blenderbim.bim.module.model.opening import FilledOpeningGenerator
|
||||
from typing import Union, Optional
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
class Root(blenderbim.core.tool.Root):
|
||||
@classmethod
|
||||
def add_tracked_opening(cls, obj):
|
||||
def add_tracked_opening(cls, obj: bpy.types.Object) -> None:
|
||||
new = bpy.context.scene.BIMModelProperties.openings.add()
|
||||
new.obj = obj
|
||||
|
||||
@classmethod
|
||||
def assign_body_styles(cls, element, obj):
|
||||
def assign_body_styles(cls, element: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None:
|
||||
# Should this even be here? Should it be in the geometry tool?
|
||||
body = ifcopenshell.util.representation.get_representation(element, "Model", "Body", "MODEL_VIEW")
|
||||
if body:
|
||||
@@ -56,7 +57,7 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def copy_representation(cls, source, dest):
|
||||
def copy_representation(cls, source: ifcopenshell.entity_instance, dest: ifcopenshell.entity_instance) -> None:
|
||||
def exclude_callback(attribute):
|
||||
return attribute.is_a("IfcProfileDef") and attribute.ProfileName
|
||||
|
||||
@@ -80,11 +81,13 @@ class Root(blenderbim.core.tool.Root):
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def does_type_have_representations(cls, element):
|
||||
def does_type_have_representations(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
return bool(element.RepresentationMaps)
|
||||
|
||||
@classmethod
|
||||
def get_decomposition_relationships(cls, objs):
|
||||
def get_decomposition_relationships(
|
||||
cls, objs: list[bpy.types.Object]
|
||||
) -> dict[ifcopenshell.entity_instance, dict[str, Any]]:
|
||||
relationships = {}
|
||||
for obj in objs:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -96,7 +99,9 @@ class Root(blenderbim.core.tool.Root):
|
||||
return relationships
|
||||
|
||||
@classmethod
|
||||
def get_connection_relationships(cls, objs):
|
||||
def get_connection_relationships(
|
||||
cls, objs: list[bpy.types.Object]
|
||||
) -> dict[ifcopenshell.entity_instance, dict[str, Any]]:
|
||||
relationships = {}
|
||||
for obj in objs:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -119,7 +124,9 @@ class Root(blenderbim.core.tool.Root):
|
||||
return relationships
|
||||
|
||||
@classmethod
|
||||
def get_element_representation(cls, element, context):
|
||||
def get_element_representation(
|
||||
cls, element: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if context.is_a("IfcGeometricRepresentationSubContext"):
|
||||
return ifcopenshell.util.representation.get_representation(
|
||||
element,
|
||||
@@ -134,13 +141,13 @@ class Root(blenderbim.core.tool.Root):
|
||||
return ifcopenshell.util.element.get_type(element)
|
||||
|
||||
@classmethod
|
||||
def get_object_name(cls, obj):
|
||||
def get_object_name(cls, obj: bpy.types.Object) -> None:
|
||||
if "." in obj.name and obj.name.split(".")[-1].isnumeric():
|
||||
return ".".join(obj.name.split(".")[:-1])
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
def get_object_representation(cls, obj):
|
||||
def get_object_representation(cls, obj: bpy.types.Object) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.data and obj.data.BIMMeshProperties.ifc_definition_id:
|
||||
return tool.Ifc.get().by_id(obj.data.BIMMeshProperties.ifc_definition_id)
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -152,19 +159,21 @@ class Root(blenderbim.core.tool.Root):
|
||||
return element.Representation.Representations[0]
|
||||
|
||||
@classmethod
|
||||
def get_representation_context(cls, representation):
|
||||
def get_representation_context(cls, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
return representation.ContextOfItems
|
||||
|
||||
@classmethod
|
||||
def is_element_a(cls, element, ifc_class):
|
||||
def is_element_a(cls, element: ifcopenshell.entity_instance, ifc_class: str) -> bool:
|
||||
return element.is_a(ifc_class)
|
||||
|
||||
@classmethod
|
||||
def link_object_data(cls, source_obj, destination_obj):
|
||||
def link_object_data(cls, source_obj: bpy.types.Object, destination_obj: bpy.types.Object) -> None:
|
||||
destination_obj.data = source_obj.data
|
||||
|
||||
@classmethod
|
||||
def recreate_decompositions(cls, relationships, old_to_new):
|
||||
def recreate_decompositions(
|
||||
cls, relationships, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]
|
||||
) -> None:
|
||||
for subelement, data in relationships.items():
|
||||
new_subelements = old_to_new.get(subelement)
|
||||
new_elements = old_to_new.get(data["element"])
|
||||
@@ -227,7 +236,11 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def recreate_connections(cls, relationship, old_to_new):
|
||||
def recreate_connections(
|
||||
cls,
|
||||
relationship: dict[ifcopenshell.entity_instance, dict[str, Any]],
|
||||
old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]],
|
||||
) -> None:
|
||||
for element, data in relationship.items():
|
||||
try:
|
||||
new_relating_element = old_to_new.get(data["relating_element"])[0]
|
||||
@@ -244,7 +257,9 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def recreate_aggregate(cls, old_to_new):
|
||||
def recreate_aggregate(
|
||||
cls, old_to_new: dict[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]]
|
||||
) -> None:
|
||||
for old, new in old_to_new.items():
|
||||
old_aggregate = ifcopenshell.util.element.get_aggregate(old)
|
||||
if old_aggregate:
|
||||
@@ -301,7 +316,7 @@ class Root(blenderbim.core.tool.Root):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def set_object_name(cls, obj, element):
|
||||
def set_object_name(cls, obj: bpy.types.Object, element: ifcopenshell.entity_instance) -> None:
|
||||
# This disables the Blender name event handler
|
||||
obj.BIMObjectProperties.is_renaming = True
|
||||
name = getattr(element, "Name", getattr(element, "AxisTag", None))
|
||||
@@ -309,13 +324,13 @@ class Root(blenderbim.core.tool.Root):
|
||||
obj.BIMObjectProperties.is_renaming = False
|
||||
|
||||
@classmethod
|
||||
def unlink_object(cls, obj):
|
||||
def unlink_object(cls, obj: bpy.types.Object) -> None:
|
||||
tool.Ifc.unlink(obj=obj)
|
||||
if hasattr(obj.data, "BIMMeshProperties"):
|
||||
obj.data.BIMMeshProperties.ifc_definition_id = 0
|
||||
for material_slot in obj.material_slots:
|
||||
if material_slot.material:
|
||||
blenderbim.core.style.unlink_style(tool.Ifc, tool.Style, obj=material_slot.material)
|
||||
blenderbim.core.style.unlink_style(tool.Ifc, style=material_slot.material)
|
||||
blenderbim.core.material.unlink_material(tool.Ifc, obj=material_slot.material)
|
||||
if "Ifc" in obj.name and "/" in obj.name:
|
||||
obj.name = obj.name.split("/", 1)[1]
|
||||
|
||||
@@ -20,11 +20,12 @@ import bpy
|
||||
import numpy as np
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.representation
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.helper
|
||||
from mathutils import Color
|
||||
from typing import Union
|
||||
from typing import Union, Any, Optional
|
||||
|
||||
# fmt: off
|
||||
TEXTURE_MAPS_BY_METHODS = {
|
||||
@@ -45,19 +46,19 @@ STYLE_PROPS_MAP = {
|
||||
|
||||
class Style(blenderbim.core.tool.Style):
|
||||
@classmethod
|
||||
def can_support_rendering_style(cls, obj):
|
||||
def can_support_rendering_style(cls, obj: bpy.types.Material) -> bool:
|
||||
return obj.use_nodes and hasattr(obj.node_tree, "nodes")
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj):
|
||||
def disable_editing(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_external_style(cls, obj):
|
||||
def disable_editing_external_style(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing_external_style = False
|
||||
|
||||
@classmethod
|
||||
def disable_editing_styles(cls):
|
||||
def disable_editing_styles(cls) -> None:
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = False
|
||||
|
||||
@classmethod
|
||||
@@ -67,39 +68,40 @@ class Style(blenderbim.core.tool.Style):
|
||||
return new_style
|
||||
|
||||
@classmethod
|
||||
def enable_editing(cls, obj):
|
||||
def enable_editing(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_external_style(cls, obj):
|
||||
def enable_editing_external_style(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMStyleProperties.is_editing_external_style = True
|
||||
|
||||
@classmethod
|
||||
def enable_editing_styles(cls):
|
||||
def enable_editing_styles(cls) -> None:
|
||||
bpy.context.scene.BIMStylesProperties.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def export_surface_attributes(cls, obj):
|
||||
def export_surface_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.attributes)
|
||||
|
||||
@classmethod
|
||||
def export_external_style_attributes(cls, obj):
|
||||
def export_external_style_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]:
|
||||
return blenderbim.bim.helper.export_attributes(obj.BIMStyleProperties.external_style_attributes)
|
||||
|
||||
@classmethod
|
||||
def get_active_style_type(cls):
|
||||
def get_active_style_type(cls) -> str:
|
||||
return bpy.context.scene.BIMStylesProperties.style_type
|
||||
|
||||
# TODO: `obj` argument is unused?
|
||||
@classmethod
|
||||
def get_context(cls, obj):
|
||||
def get_context(cls, obj) -> Union[ifcopenshell.entity_instance, None]:
|
||||
return ifcopenshell.util.representation.get_context(tool.Ifc.get(), "Model", "Body", "MODEL_VIEW")
|
||||
|
||||
@classmethod
|
||||
def get_elements_by_style(cls, style):
|
||||
def get_elements_by_style(cls, style: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style)
|
||||
|
||||
@classmethod
|
||||
def get_name(cls, obj):
|
||||
def get_name(cls, obj: bpy.types.Material) -> str:
|
||||
return obj.name
|
||||
|
||||
@classmethod
|
||||
@@ -126,7 +128,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return style_elements
|
||||
|
||||
@classmethod
|
||||
def get_shading_style_data_from_props(cls) -> dict:
|
||||
def get_shading_style_data_from_props(cls) -> dict[str, Any]:
|
||||
"""returns style data from blender props in similar way to `Loader.surface_style_to_dict`
|
||||
to be compatible with `Loader.create_surface_style_rendering`"""
|
||||
surface_style_data = dict()
|
||||
@@ -154,7 +156,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return surface_style_data
|
||||
|
||||
@classmethod
|
||||
def get_texture_style_data_from_props(cls) -> list[dict]:
|
||||
def get_texture_style_data_from_props(cls) -> list[dict[str, Any]]:
|
||||
"""returns style data from blender props in similar way to `Loader.surface_texture_to_dict`
|
||||
to be compatible with `Loader.create_surface_style_with_textures`"""
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
@@ -174,7 +176,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return textures
|
||||
|
||||
@classmethod
|
||||
def set_surface_style_props(cls):
|
||||
def set_surface_style_props(cls) -> None:
|
||||
"""set blender style props based on currently edited IfcSurfaceStyle,
|
||||
reset unrelated props to default values"""
|
||||
|
||||
@@ -241,7 +243,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
props["update_graph"] = prev_update_graph_value
|
||||
|
||||
@classmethod
|
||||
def get_surface_rendering_attributes(cls, obj, verbose=False):
|
||||
def get_surface_rendering_attributes(cls, obj: bpy.types.Material, verbose: bool = False) -> dict[str, Any]:
|
||||
report = (lambda *x: print(*x)) if verbose else (lambda *x: None)
|
||||
|
||||
def color_to_ifc_format(color):
|
||||
@@ -403,22 +405,22 @@ class Style(blenderbim.core.tool.Style):
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def get_surface_rendering_style(cls, obj):
|
||||
def get_surface_rendering_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
style_elements = cls.get_style_elements(obj)
|
||||
return style_elements.get("IfcSurfaceStyleRendering", None)
|
||||
|
||||
@classmethod
|
||||
def get_texture_style(cls, obj):
|
||||
def get_texture_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
style_elements = cls.get_style_elements(obj)
|
||||
return style_elements.get("IfcSurfaceStyleWithTextures", None)
|
||||
|
||||
@classmethod
|
||||
def get_external_style(cls, obj):
|
||||
def get_external_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
style_elements = cls.get_style_elements(obj)
|
||||
return style_elements.get("IfcExternallyDefinedSurfaceStyle", None)
|
||||
|
||||
@classmethod
|
||||
def get_surface_shading_attributes(cls, obj):
|
||||
def get_surface_shading_attributes(cls, obj: bpy.types.Material) -> dict[str, Any]:
|
||||
data = {
|
||||
"SurfaceColour": {
|
||||
"Name": None,
|
||||
@@ -433,7 +435,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return data
|
||||
|
||||
@classmethod
|
||||
def get_surface_shading_style(cls, obj):
|
||||
def get_surface_shading_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
|
||||
items = [s for s in style.Styles if s.is_a() == "IfcSurfaceStyleShading"]
|
||||
@@ -441,7 +443,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return items[0]
|
||||
|
||||
@classmethod
|
||||
def get_surface_texture_style(cls, obj):
|
||||
def get_surface_texture_style(cls, obj: bpy.types.Material) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if obj.BIMMaterialProperties.ifc_style_id:
|
||||
style = tool.Ifc.get().by_id(obj.BIMMaterialProperties.ifc_style_id)
|
||||
items = [s for s in style.Styles if s.is_a("IfcSurfaceStyleWithTextures")]
|
||||
@@ -449,7 +451,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return items[0]
|
||||
|
||||
@classmethod
|
||||
def get_uv_maps(cls, representation):
|
||||
def get_uv_maps(cls, representation: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
items = []
|
||||
for item in representation.Items:
|
||||
if item.is_a("IfcMappedItem"):
|
||||
@@ -464,7 +466,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def get_style_ui_props_attributes(cls, style_type):
|
||||
def get_style_ui_props_attributes(cls, style_type: str) -> Union[bpy.types.PropertyGroup, None]:
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
if style_type == "IfcExternallyDefinedSurfaceStyle":
|
||||
return props.external_style_attributes
|
||||
@@ -474,7 +476,7 @@ class Style(blenderbim.core.tool.Style):
|
||||
return props.lighting_style_colours
|
||||
|
||||
@classmethod
|
||||
def import_presentation_styles(cls, style_type):
|
||||
def import_presentation_styles(cls, style_type: str) -> None:
|
||||
color_to_tuple = lambda x: (x.Red, x.Green, x.Blue)
|
||||
props = bpy.context.scene.BIMStylesProperties
|
||||
props.styles.clear()
|
||||
@@ -497,13 +499,13 @@ class Style(blenderbim.core.tool.Style):
|
||||
new.total_elements = len(ifcopenshell.util.element.get_elements_by_style(tool.Ifc.get(), style))
|
||||
|
||||
@classmethod
|
||||
def import_surface_attributes(cls, style, obj):
|
||||
def import_surface_attributes(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Material) -> None:
|
||||
attributes = obj.BIMStyleProperties.attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(style, attributes)
|
||||
|
||||
@classmethod
|
||||
def import_external_style_attributes(cls, style, obj):
|
||||
def import_external_style_attributes(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Material) -> None:
|
||||
attributes = obj.BIMStyleProperties.external_style_attributes
|
||||
attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes2(style, attributes)
|
||||
@@ -514,26 +516,26 @@ class Style(blenderbim.core.tool.Style):
|
||||
return bool(external_style and external_style.Location and external_style.Location.endswith(".blend"))
|
||||
|
||||
@classmethod
|
||||
def is_editing_styles(cls):
|
||||
def is_editing_styles(cls) -> bool:
|
||||
return bpy.context.scene.BIMStylesProperties.is_editing
|
||||
|
||||
@classmethod
|
||||
def record_shading(cls, obj):
|
||||
def record_shading(cls, obj: bpy.types.Material) -> None:
|
||||
obj.BIMMaterialProperties.shading_checksum = repr(np.array(obj.diffuse_color).tobytes())
|
||||
|
||||
@classmethod
|
||||
def select_elements(cls, elements):
|
||||
def select_elements(cls, elements: list[ifcopenshell.entity_instance]) -> None:
|
||||
for element in elements:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if obj:
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def change_current_style_type(cls, blender_material, style_type):
|
||||
def change_current_style_type(cls, blender_material: bpy.types.Material, style_type: str) -> None:
|
||||
blender_material.BIMStyleProperties.active_style_type = style_type
|
||||
|
||||
@classmethod
|
||||
def get_styled_items(cls, style):
|
||||
def get_styled_items(cls, style: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
inverses = list(ifc_file.get_inverse(style))
|
||||
@@ -554,13 +556,15 @@ class Style(blenderbim.core.tool.Style):
|
||||
return items
|
||||
|
||||
@classmethod
|
||||
def assign_style_to_object(cls, style, obj):
|
||||
def assign_style_to_object(cls, style: ifcopenshell.entity_instance, obj: bpy.types.Object) -> None:
|
||||
"""assigns `style` to `object` current representation"""
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
tool.Ifc.run("style.assign_representation_styles", shape_representation=representation, styles=[style])
|
||||
|
||||
@classmethod
|
||||
def assign_style_to_representation_item(cls, representation_item, style=None):
|
||||
def assign_style_to_representation_item(
|
||||
cls, representation_item: ifcopenshell.entity_instance, style: Optional[ifcopenshell.entity_instance] = None
|
||||
) -> None:
|
||||
ifc_file = tool.Ifc.get()
|
||||
if not representation_item.StyledByItem:
|
||||
if style is None:
|
||||
@@ -574,12 +578,14 @@ class Style(blenderbim.core.tool.Style):
|
||||
styled_item.Styles = (style,)
|
||||
|
||||
@classmethod
|
||||
def get_representation_item_style(cls, representation_item):
|
||||
def get_representation_item_style(
|
||||
cls, representation_item: ifcopenshell.entity_instance
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
for inverse in tool.Ifc.get().get_inverse(representation_item):
|
||||
if inverse.is_a("IfcStyledItem"):
|
||||
for style in inverse.Styles:
|
||||
return style
|
||||
|
||||
@classmethod
|
||||
def reload_material_from_ifc(cls, blender_material):
|
||||
def reload_material_from_ifc(cls, blender_material: bpy.types.Material) -> None:
|
||||
blender_material.BIMStyleProperties.active_style_type = blender_material.BIMStyleProperties.active_style_type
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import json
|
||||
import ifcopenshell.util.pset
|
||||
import ifcopenshell.util.type
|
||||
|
||||
|
||||
def order_dict(dictionary):
|
||||
# https://stackoverflow.com/questions/22721579/sorting-a-nested-ordereddict-by-key-recursively
|
||||
return {k: order_dict(v) if isinstance(v, dict) else v for k, v in sorted(dictionary.items())}
|
||||
|
||||
|
||||
results = {}
|
||||
|
||||
psetqto = ifcopenshell.util.pset.get_template("IFC4")
|
||||
for template in psetqto.templates:
|
||||
for pset_template in template.by_type("IfcPropertySetTemplate"):
|
||||
if not pset_template.Name.startswith("Qto_"):
|
||||
continue
|
||||
query = pset_template.ApplicableEntity
|
||||
results.setdefault(query, {})
|
||||
results[query].setdefault(pset_template.Name, {})
|
||||
for quantity in pset_template.HasPropertyTemplates:
|
||||
results[query][pset_template.Name][quantity.Name] = None
|
||||
|
||||
results = order_dict(results)
|
||||
print(results)
|
||||
with open("results.json", "w") as f:
|
||||
json.dump(results, f, indent=4)
|
||||
@@ -19,6 +19,7 @@
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.pset
|
||||
import test.bim.bootstrap
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.root
|
||||
@@ -65,13 +66,26 @@ class TestGetApplicableBaseQuantityName(test.bim.bootstrap.NewFile):
|
||||
wall = ifc.createIfcWall()
|
||||
assert subject.get_applicable_base_quantity_name(wall) == "Qto_WallBaseQuantities"
|
||||
|
||||
def test_no_base_quantity(self):
|
||||
def test_no_quantities(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject")
|
||||
product = ifc.by_type("IfcProject")[0]
|
||||
assert subject.get_applicable_base_quantity_name(product) == None
|
||||
|
||||
def test_anomaly_named_quantities(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
product = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBuildingElementProxy")
|
||||
# Prioritized over Qto_BodyGeometryValidation.
|
||||
assert subject.get_applicable_base_quantity_name(product) == "Qto_BuildingElementProxyQuantities"
|
||||
|
||||
def test_prioritize_base_over_other_qto(self):
|
||||
ifc = ifcopenshell.file(schema="IFC4X3")
|
||||
tool.Ifc.set(ifc)
|
||||
product = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
assert subject.get_applicable_base_quantity_name(product) == "Qto_WallBaseQuantities"
|
||||
|
||||
|
||||
class TestGetRoundedValue(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
@@ -213,7 +227,7 @@ class TestGetBaseQto(test.bim.bootstrap.NewFile):
|
||||
assert subject.get_base_qto(product).id() == pset_qto.get_info()["id"]
|
||||
assert subject.get_base_qto(product).Name == pset_qto.Name
|
||||
|
||||
def test_isempty(self):
|
||||
def test_no_quantities(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
@@ -222,6 +236,46 @@ class TestGetBaseQto(test.bim.bootstrap.NewFile):
|
||||
product = tool.Ifc.get_entity(wall_obj)
|
||||
assert not subject.get_base_qto(product) == True
|
||||
|
||||
def test_anomaly_named_quantities(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
product = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcBuildingElementProxy")
|
||||
tool.Ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name="EQto_BodyGeometryValidation",
|
||||
)
|
||||
tool.Ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name="Qto_BuildingElementProxyQuantities",
|
||||
)
|
||||
# Prioritized over Qto_BodyGeometryValidation.
|
||||
base_qto_name = subject.get_base_qto(product).Name
|
||||
assert base_qto_name == "Qto_BuildingElementProxyQuantities"
|
||||
# Ensure methods are in sync.
|
||||
assert base_qto_name == subject.get_applicable_base_quantity_name(product)
|
||||
|
||||
def test_prioritize_base_over_other_qto(self):
|
||||
ifc = ifcopenshell.file(schema="IFC4X3")
|
||||
tool.Ifc.set(ifc)
|
||||
product = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcWall")
|
||||
tool.Ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name="Qto_BodyGeometryValidation",
|
||||
)
|
||||
tool.Ifc.run(
|
||||
"pset.add_qto",
|
||||
product=product,
|
||||
name="Qto_WallBaseQuantities",
|
||||
)
|
||||
# Prioritized over Qto_BodyGeometryValidation.
|
||||
base_qto_name = subject.get_base_qto(product).Name
|
||||
assert base_qto_name == "Qto_WallBaseQuantities"
|
||||
# Ensure methods are in sync.
|
||||
assert base_qto_name == subject.get_applicable_base_quantity_name(product)
|
||||
|
||||
|
||||
class TestGetRelatedCostItemQuantities(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
|
||||
@@ -0,0 +1,637 @@
|
||||
{
|
||||
"name": "IFC4 Base Quantities - IfcOpenShell",
|
||||
"description": "This ruleset quantifies every single possible standardised base quantity in IFC4 using only IfcOpenShell as a geometry processor.",
|
||||
"calculators": {
|
||||
"IfcOpenShell": {
|
||||
"IfcActuator": {
|
||||
"Qto_ActuatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminal": {
|
||||
"Qto_AirTerminalBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Perimeter": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminalBox": {
|
||||
"Qto_AirTerminalBoxTypeBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirToAirHeatRecovery": {
|
||||
"Qto_AirToAirHeatRecoveryBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAlarm": {
|
||||
"Qto_AlarmBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAudioVisualAppliance": {
|
||||
"Qto_AudioVisualApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcBeam": {
|
||||
"Qto_BeamBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcBoiler": {
|
||||
"Qto_BoilerBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcBuilding": {
|
||||
"Qto_BuildingBaseQuantities": {
|
||||
"EavesHeight": null,
|
||||
"FootprintArea": null,
|
||||
"GrossFloorArea": null,
|
||||
"GrossVolume": null,
|
||||
"Height": null,
|
||||
"NetFloorArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBuildingElementProxy": {
|
||||
"Qto_BuildingElementProxyQuantities": {
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBuildingStorey": {
|
||||
"Qto_BuildingStoreyBaseQuantities": {
|
||||
"GrossFloorArea": null,
|
||||
"GrossHeight": null,
|
||||
"GrossPerimeter": null,
|
||||
"GrossVolume": null,
|
||||
"NetFloorArea": null,
|
||||
"NetHeigtht": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBurner": {
|
||||
"Qto_BurnerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierFitting": {
|
||||
"Qto_CableCarrierFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierSegment": {
|
||||
"Qto_CableCarrierSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcCableFitting": {
|
||||
"Qto_CableFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableSegment": {
|
||||
"Qto_CableSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcChiller": {
|
||||
"Qto_ChillerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcChimney": {
|
||||
"Qto_ChimneyBaseQuantities": {
|
||||
"Length": null
|
||||
}
|
||||
},
|
||||
"IfcCoil": {
|
||||
"Qto_CoilBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcColumn": {
|
||||
"Qto_ColumnBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcCommunicationsAppliance": {
|
||||
"Qto_CommunicationsApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCompressor": {
|
||||
"Qto_CompressorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCondenser": {
|
||||
"Qto_CondenserBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionEquipmentResource": {
|
||||
"Qto_ConstructionEquipmentResourceBaseQuantities": {
|
||||
"OperatingTime": null,
|
||||
"UsageTime": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionMaterialResource": {
|
||||
"Qto_ConstructionMaterialResourceBaseQuantities": {
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcController": {
|
||||
"Qto_ControllerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCooledBeam": {
|
||||
"Qto_CooledBeamBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCoolingTower": {
|
||||
"Qto_CoolingTowerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCovering": {
|
||||
"Qto_CoveringBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"NetArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcCurtainWall": {
|
||||
"Qto_CurtainWallQuantities": {
|
||||
"GrossSideArea": null,
|
||||
"Height": null,
|
||||
"Length": null,
|
||||
"NetSideArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcDamper": {
|
||||
"Qto_DamperBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcDistributionChamberElement": {
|
||||
"Qto_DistributionChamberElementBaseQuantities": {
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcDoor": {
|
||||
"Qto_DoorBaseQuantities": {
|
||||
"Area": null,
|
||||
"Height": null,
|
||||
"Perimeter": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcDuctFitting": {
|
||||
"Qto_DuctFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcDuctSegment": {
|
||||
"Qto_DuctSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcDuctSilencer": {
|
||||
"Qto_DuctSilencerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricAppliance": {
|
||||
"Qto_ElectricApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricDistributionBoard": {
|
||||
"Qto_ElectricDistributionBoardBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfCircuits": null
|
||||
}
|
||||
},
|
||||
"IfcElectricFlowStorageDevice": {
|
||||
"Qto_ElectricFlowStorageDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricGenerator": {
|
||||
"Qto_ElectricGeneratorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricMotor": {
|
||||
"Qto_ElectricMotorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricTimeControl": {
|
||||
"Qto_ElectricTimeControlBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporativeCooler": {
|
||||
"Qto_EvaporativeCoolerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporator": {
|
||||
"Qto_EvaporatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFan": {
|
||||
"Qto_FanBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFilter": {
|
||||
"Qto_FilterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFireSuppressionTerminal": {
|
||||
"Qto_FireSuppressionTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowInstrument": {
|
||||
"Qto_FlowInstrumentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowMeter": {
|
||||
"Qto_FlowMeterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFooting": {
|
||||
"Qto_FootingBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Height": null,
|
||||
"Length": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcHeatExchanger": {
|
||||
"Qto_HeatExchangerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcHumidifier": {
|
||||
"Qto_HumidifierBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcInterceptor": {
|
||||
"Qto_InterceptorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcJunctionBox": {
|
||||
"Qto_JunctionBoxBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfGangs": null
|
||||
}
|
||||
},
|
||||
"IfcLaborResource": {
|
||||
"Qto_LaborResourceBaseQuantities": {
|
||||
"OvertimeWork": null,
|
||||
"StandardWork": null
|
||||
}
|
||||
},
|
||||
"IfcLamp": {
|
||||
"Qto_LampBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcLightFixture": {
|
||||
"Qto_LightFixtureBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcMember": {
|
||||
"Qto_MemberBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetSurfaceArea": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcMotorConnection": {
|
||||
"Qto_MotorConnectionBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcOpeningElement": {
|
||||
"Qto_OpeningElementBaseQuantities": {
|
||||
"Area": null,
|
||||
"Depth": null,
|
||||
"Height": null,
|
||||
"Volume": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcOutlet": {
|
||||
"Qto_OutletBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPile": {
|
||||
"Qto_PileBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossSurfaceArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPipeFitting": {
|
||||
"Qto_PipeFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPipeSegment": {
|
||||
"Qto_PipeSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPlate": {
|
||||
"Qto_PlateBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"NetArea": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"Perimeter": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcProjectionElement": {
|
||||
"Qto_ProjectionElementBaseQuantities": {
|
||||
"Area": null,
|
||||
"Volume": null
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDevice": {
|
||||
"Qto_ProtectiveDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDeviceTrippingUnit": {
|
||||
"Qto_ProtectiveDeviceTrippingUnitBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPump": {
|
||||
"Qto_PumpBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcRailing": {
|
||||
"Qto_RailingBaseQuantities": {
|
||||
"Length": null
|
||||
}
|
||||
},
|
||||
"IfcRampFlight": {
|
||||
"Qto_RampFlightBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossVolume": null,
|
||||
"Length": null,
|
||||
"NetArea": null,
|
||||
"NetVolume": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcReinforcingElement": {
|
||||
"Qto_ReinforcingElementBaseQuantities": {
|
||||
"Count": null,
|
||||
"Length": null,
|
||||
"Weight": null
|
||||
}
|
||||
},
|
||||
"IfcRoof": {
|
||||
"Qto_RoofBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"NetArea": null,
|
||||
"ProjectedArea": null
|
||||
}
|
||||
},
|
||||
"IfcSanitaryTerminal": {
|
||||
"Qto_SanitaryTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSensor": {
|
||||
"Qto_SensorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSite": {
|
||||
"Qto_SiteBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossPerimeter": null
|
||||
}
|
||||
},
|
||||
"IfcSlab": {
|
||||
"Qto_SlabBaseQuantities": {
|
||||
"Depth": null,
|
||||
"GrossArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetArea": null,
|
||||
"NetVolume": null,
|
||||
"NetWeight": null,
|
||||
"Perimeter": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcSolarDevice": {
|
||||
"Qto_SolarDeviceBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSpace": {
|
||||
"Qto_SpaceBaseQuantities": {
|
||||
"FinishCeilingHeight": null,
|
||||
"FinishFloorHeight": null,
|
||||
"GrossCeilingArea": null,
|
||||
"GrossFloorArea": null,
|
||||
"GrossPerimeter": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWallArea": null,
|
||||
"Height": null,
|
||||
"NetCeilingArea": null,
|
||||
"NetFloorArea": null,
|
||||
"NetPerimeter": null,
|
||||
"NetVolume": null,
|
||||
"NetWallArea": null
|
||||
}
|
||||
},
|
||||
"IfcSpaceHeater": {
|
||||
"Qto_SpaceHeaterBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStackTerminal": {
|
||||
"Qto_StackTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStairFlight": {
|
||||
"Qto_StairFlightBaseQuantities": {
|
||||
"GrossVolume": null,
|
||||
"Length": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcSwitchingDevice": {
|
||||
"Qto_SwitchingDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTank": {
|
||||
"Qto_TankBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcTransformer": {
|
||||
"Qto_TransformerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTubeBundle": {
|
||||
"Qto_TubeBundleBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryControlElement": {
|
||||
"Qto_UnitaryControlElementBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryEquipment": {
|
||||
"Qto_UnitaryEquipmentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcValve": {
|
||||
"Qto_ValveBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcVibrationIsolator": {
|
||||
"Qto_VibrationIsolatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWall": {
|
||||
"Qto_WallBaseQuantities": {
|
||||
"GrossFootprintArea": null,
|
||||
"GrossSideArea": null,
|
||||
"GrossVolume": null,
|
||||
"GrossWeight": null,
|
||||
"Height": "net_get_z",
|
||||
"Length": "net_get_x",
|
||||
"NetFootprintArea": null,
|
||||
"NetSideArea": "net_get_side_area",
|
||||
"NetVolume": "net_get_volume",
|
||||
"NetWeight": null,
|
||||
"Width": "net_get_y"
|
||||
}
|
||||
},
|
||||
"IfcWasteTerminal": {
|
||||
"Qto_WasteTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWindow": {
|
||||
"Qto_WindowBaseQuantities": {
|
||||
"Area": null,
|
||||
"Height": null,
|
||||
"Perimeter": null,
|
||||
"Width": null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,637 @@
|
||||
{
|
||||
"name": "IFC4 Base Quantities - Blender",
|
||||
"description": "This ruleset quantifies every single possible standardised base quantity in IFC4 using Blender.",
|
||||
"calculators": {
|
||||
"Blender": {
|
||||
"IfcActuator": {
|
||||
"Qto_ActuatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminal": {
|
||||
"Qto_AirTerminalBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Perimeter": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcAirTerminalBox": {
|
||||
"Qto_AirTerminalBoxTypeBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAirToAirHeatRecovery": {
|
||||
"Qto_AirToAirHeatRecoveryBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAlarm": {
|
||||
"Qto_AlarmBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcAudioVisualAppliance": {
|
||||
"Qto_AudioVisualApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcBeam": {
|
||||
"Qto_BeamBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcBoiler": {
|
||||
"Qto_BoilerBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcBuilding": {
|
||||
"Qto_BuildingBaseQuantities": {
|
||||
"EavesHeight": null,
|
||||
"FootprintArea": null,
|
||||
"GrossFloorArea": null,
|
||||
"GrossVolume": null,
|
||||
"Height": null,
|
||||
"NetFloorArea": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBuildingElementProxy": {
|
||||
"Qto_BuildingElementProxyQuantities": {
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcBuildingStorey": {
|
||||
"Qto_BuildingStoreyBaseQuantities": {
|
||||
"GrossFloorArea": null,
|
||||
"GrossHeight": null,
|
||||
"GrossPerimeter": null,
|
||||
"GrossVolume": null,
|
||||
"NetFloorArea": null,
|
||||
"NetHeigtht": null,
|
||||
"NetVolume": null
|
||||
}
|
||||
},
|
||||
"IfcBurner": {
|
||||
"Qto_BurnerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierFitting": {
|
||||
"Qto_CableCarrierFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableCarrierSegment": {
|
||||
"Qto_CableCarrierSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcCableFitting": {
|
||||
"Qto_CableFittingBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCableSegment": {
|
||||
"Qto_CableSegmentBaseQuantities": {
|
||||
"CrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcChiller": {
|
||||
"Qto_ChillerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcChimney": {
|
||||
"Qto_ChimneyBaseQuantities": {
|
||||
"Length": "get_height"
|
||||
}
|
||||
},
|
||||
"IfcCoil": {
|
||||
"Qto_CoilBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcColumn": {
|
||||
"Qto_ColumnBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcCommunicationsAppliance": {
|
||||
"Qto_CommunicationsApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCompressor": {
|
||||
"Qto_CompressorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCondenser": {
|
||||
"Qto_CondenserBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionEquipmentResource": {
|
||||
"Qto_ConstructionEquipmentResourceBaseQuantities": {
|
||||
"OperatingTime": null,
|
||||
"UsageTime": null
|
||||
}
|
||||
},
|
||||
"IfcConstructionMaterialResource": {
|
||||
"Qto_ConstructionMaterialResourceBaseQuantities": {
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": null,
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcController": {
|
||||
"Qto_ControllerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCooledBeam": {
|
||||
"Qto_CooledBeamBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCoolingTower": {
|
||||
"Qto_CoolingTowerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcCovering": {
|
||||
"Qto_CoveringBaseQuantities": {
|
||||
"GrossArea": "get_covering_gross_area",
|
||||
"NetArea": "get_covering_net_area",
|
||||
"Width": "get_covering_width"
|
||||
}
|
||||
},
|
||||
"IfcCurtainWall": {
|
||||
"Qto_CurtainWallQuantities": {
|
||||
"GrossSideArea": null,
|
||||
"Height": null,
|
||||
"Length": null,
|
||||
"NetSideArea": null,
|
||||
"Width": null
|
||||
}
|
||||
},
|
||||
"IfcDamper": {
|
||||
"Qto_DamperBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcDistributionChamberElement": {
|
||||
"Qto_DistributionChamberElementBaseQuantities": {
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcDoor": {
|
||||
"Qto_DoorBaseQuantities": {
|
||||
"Area": "get_net_side_area",
|
||||
"Height": "get_height",
|
||||
"Perimeter": "get_rectangular_perimeter",
|
||||
"Width": "get_length"
|
||||
}
|
||||
},
|
||||
"IfcDuctFitting": {
|
||||
"Qto_DuctFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcDuctSegment": {
|
||||
"Qto_DuctSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"NetCrossSectionArea": null,
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcDuctSilencer": {
|
||||
"Qto_DuctSilencerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricAppliance": {
|
||||
"Qto_ElectricApplianceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricDistributionBoard": {
|
||||
"Qto_ElectricDistributionBoardBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfCircuits": null
|
||||
}
|
||||
},
|
||||
"IfcElectricFlowStorageDevice": {
|
||||
"Qto_ElectricFlowStorageDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricGenerator": {
|
||||
"Qto_ElectricGeneratorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricMotor": {
|
||||
"Qto_ElectricMotorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcElectricTimeControl": {
|
||||
"Qto_ElectricTimeControlBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporativeCooler": {
|
||||
"Qto_EvaporativeCoolerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcEvaporator": {
|
||||
"Qto_EvaporatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFan": {
|
||||
"Qto_FanBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFilter": {
|
||||
"Qto_FilterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFireSuppressionTerminal": {
|
||||
"Qto_FireSuppressionTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowInstrument": {
|
||||
"Qto_FlowInstrumentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFlowMeter": {
|
||||
"Qto_FlowMeterBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcFooting": {
|
||||
"Qto_FootingBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Height": "get_height",
|
||||
"Length": "get_length",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcHeatExchanger": {
|
||||
"Qto_HeatExchangerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcHumidifier": {
|
||||
"Qto_HumidifierBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcInterceptor": {
|
||||
"Qto_InterceptorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcJunctionBox": {
|
||||
"Qto_JunctionBoxBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NumberOfGangs": null
|
||||
}
|
||||
},
|
||||
"IfcLaborResource": {
|
||||
"Qto_LaborResourceBaseQuantities": {
|
||||
"OvertimeWork": null,
|
||||
"StandardWork": null
|
||||
}
|
||||
},
|
||||
"IfcLamp": {
|
||||
"Qto_LampBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcLightFixture": {
|
||||
"Qto_LightFixtureBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcMember": {
|
||||
"Qto_MemberBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetSurfaceArea": "get_net_surface_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcMotorConnection": {
|
||||
"Qto_MotorConnectionBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcOpeningElement": {
|
||||
"Qto_OpeningElementBaseQuantities": {
|
||||
"Area": "get_opening_mapping_area",
|
||||
"Depth": "get_opening_depth",
|
||||
"Height": "get_opening_height",
|
||||
"Volume": "get_net_volume",
|
||||
"Width": "get_length"
|
||||
}
|
||||
},
|
||||
"IfcOutlet": {
|
||||
"Qto_OutletBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPile": {
|
||||
"Qto_PileBaseQuantities": {
|
||||
"CrossSectionArea": "get_cross_section_area",
|
||||
"GrossSurfaceArea": "get_gross_surface_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcPipeFitting": {
|
||||
"Qto_PipeFittingBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": null,
|
||||
"Length": null,
|
||||
"NetCrossSectionArea": null,
|
||||
"NetWeight": null,
|
||||
"OuterSurfaceArea": null
|
||||
}
|
||||
},
|
||||
"IfcPipeSegment": {
|
||||
"Qto_PipeSegmentBaseQuantities": {
|
||||
"GrossCrossSectionArea": null,
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetCrossSectionArea": "get_cross_section_area",
|
||||
"NetWeight": "get_net_weight",
|
||||
"OuterSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcPlate": {
|
||||
"Qto_PlateBaseQuantities": {
|
||||
"GrossArea": "get_gross_footprint_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"NetArea": "get_net_footprint_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Perimeter": "get_gross_perimeter",
|
||||
"Width": "get_height"
|
||||
}
|
||||
},
|
||||
"IfcProjectionElement": {
|
||||
"Qto_ProjectionElementBaseQuantities": {
|
||||
"Area": "get_net_side_area",
|
||||
"Volume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDevice": {
|
||||
"Qto_ProtectiveDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcProtectiveDeviceTrippingUnit": {
|
||||
"Qto_ProtectiveDeviceTrippingUnitBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcPump": {
|
||||
"Qto_PumpBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcRailing": {
|
||||
"Qto_RailingBaseQuantities": {
|
||||
"Length": "get_length"
|
||||
}
|
||||
},
|
||||
"IfcRampFlight": {
|
||||
"Qto_RampFlightBaseQuantities": {
|
||||
"GrossArea": "get_gross_stair_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"Length": "get_stair_length",
|
||||
"NetArea": "get_net_stair_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcReinforcingElement": {
|
||||
"Qto_ReinforcingElementBaseQuantities": {
|
||||
"Count": null,
|
||||
"Length": "get_length",
|
||||
"Weight": null
|
||||
}
|
||||
},
|
||||
"IfcRoof": {
|
||||
"Qto_RoofBaseQuantities": {
|
||||
"GrossArea": "get_gross_top_area",
|
||||
"NetArea": "get_net_top_area",
|
||||
"ProjectedArea": null
|
||||
}
|
||||
},
|
||||
"IfcSanitaryTerminal": {
|
||||
"Qto_SanitaryTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSensor": {
|
||||
"Qto_SensorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSite": {
|
||||
"Qto_SiteBaseQuantities": {
|
||||
"GrossArea": "get_gross_footprint_area",
|
||||
"GrossPerimeter": "get_gross_perimeter"
|
||||
}
|
||||
},
|
||||
"IfcSlab": {
|
||||
"Qto_SlabBaseQuantities": {
|
||||
"Depth": "get_height",
|
||||
"GrossArea": "get_gross_footprint_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Length": "get_length",
|
||||
"NetArea": "get_net_footprint_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Perimeter": "get_gross_perimeter",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcSolarDevice": {
|
||||
"Qto_SolarDeviceBaseQuantities": {
|
||||
"GrossArea": null,
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcSpace": {
|
||||
"Qto_SpaceBaseQuantities": {
|
||||
"FinishCeilingHeight": "get_finish_ceiling_height",
|
||||
"FinishFloorHeight": "get_finish_floor_height",
|
||||
"GrossCeilingArea": "get_gross_ceiling_area",
|
||||
"GrossFloorArea": "get_gross_footprint_area",
|
||||
"GrossPerimeter": "get_gross_perimeter",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWallArea": null,
|
||||
"Height": "get_height",
|
||||
"NetCeilingArea": "get_net_ceiling_area",
|
||||
"NetFloorArea": "get_net_floor_area",
|
||||
"NetPerimeter": null,
|
||||
"NetVolume": "get_space_net_volume",
|
||||
"NetWallArea": null
|
||||
}
|
||||
},
|
||||
"IfcSpaceHeater": {
|
||||
"Qto_SpaceHeaterBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"Length": "get_length",
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStackTerminal": {
|
||||
"Qto_StackTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcStairFlight": {
|
||||
"Qto_StairFlightBaseQuantities": {
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"Length": "get_stair_length",
|
||||
"NetVolume": "get_net_volume"
|
||||
}
|
||||
},
|
||||
"IfcSwitchingDevice": {
|
||||
"Qto_SwitchingDeviceBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTank": {
|
||||
"Qto_TankBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null,
|
||||
"TotalSurfaceArea": "get_outer_surface_area"
|
||||
}
|
||||
},
|
||||
"IfcTransformer": {
|
||||
"Qto_TransformerBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcTubeBundle": {
|
||||
"Qto_TubeBundleBaseQuantities": {
|
||||
"GrossWeight": null,
|
||||
"NetWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryControlElement": {
|
||||
"Qto_UnitaryControlElementBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcUnitaryEquipment": {
|
||||
"Qto_UnitaryEquipmentBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcValve": {
|
||||
"Qto_ValveBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcVibrationIsolator": {
|
||||
"Qto_VibrationIsolatorBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWall": {
|
||||
"Qto_WallBaseQuantities": {
|
||||
"GrossFootprintArea": "get_gross_footprint_area",
|
||||
"GrossSideArea": "get_gross_side_area",
|
||||
"GrossVolume": "get_gross_volume",
|
||||
"GrossWeight": "get_gross_weight",
|
||||
"Height": "get_height",
|
||||
"Length": "get_length",
|
||||
"NetFootprintArea": "get_net_footprint_area",
|
||||
"NetSideArea": "get_net_side_area",
|
||||
"NetVolume": "get_net_volume",
|
||||
"NetWeight": "get_net_weight",
|
||||
"Width": "get_width"
|
||||
}
|
||||
},
|
||||
"IfcWasteTerminal": {
|
||||
"Qto_WasteTerminalBaseQuantities": {
|
||||
"GrossWeight": null
|
||||
}
|
||||
},
|
||||
"IfcWindow": {
|
||||
"Qto_WindowBaseQuantities": {
|
||||
"Area": "get_net_side_area",
|
||||
"Height": "get_height",
|
||||
"Perimeter": "get_rectangular_perimeter",
|
||||
"Width": "get_length"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import ifcopenshell
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.cost
|
||||
import ifcopenshell.util.date
|
||||
from typing import Union, Optional
|
||||
from typing import Union, Optional, Any
|
||||
|
||||
|
||||
class IfcDataGetter:
|
||||
@@ -41,17 +41,17 @@ class IfcDataGetter:
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def canonicalise_time(time):
|
||||
def canonicalise_time(time: Union[datetime.datetime, None]) -> str:
|
||||
if not time:
|
||||
return "-"
|
||||
return time.strftime("%d/%m/%y")
|
||||
|
||||
@staticmethod
|
||||
def get_root_costs(cost_schedule):
|
||||
def get_root_costs(cost_schedule: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
return [obj for rel in cost_schedule.Controls or [] for obj in rel.RelatedObjects or []]
|
||||
|
||||
@staticmethod
|
||||
def get_cost_item_values(cost_item=None):
|
||||
def get_cost_item_values(cost_item: Union[ifcopenshell.entity_instance, None]) -> Union[list[dict[str, Any]], None]:
|
||||
if not cost_item:
|
||||
return None
|
||||
values = []
|
||||
@@ -71,14 +71,14 @@ class IfcDataGetter:
|
||||
return values
|
||||
|
||||
@staticmethod
|
||||
def process_categories(cost_item, categories):
|
||||
def process_categories(cost_item: ifcopenshell.entity_instance, categories: set[str]) -> set[str]:
|
||||
for cost_value in cost_item.CostValues or []:
|
||||
if cost_value.Category:
|
||||
categories.add("{}{}".format(cost_value.Category, " Cost"))
|
||||
return categories
|
||||
|
||||
@staticmethod
|
||||
def process_cost_item_categories(cost_item, categories):
|
||||
def process_cost_item_categories(cost_item: ifcopenshell.entity_instance, categories: set[str]) -> set[str]:
|
||||
IfcDataGetter.process_categories(cost_item, categories)
|
||||
for rel in cost_item.IsNestedBy or []:
|
||||
for child in rel.RelatedObjects or []:
|
||||
@@ -86,14 +86,20 @@ class IfcDataGetter:
|
||||
return categories
|
||||
|
||||
@staticmethod
|
||||
def get_cost_rates_categories(schedule):
|
||||
def get_cost_rates_categories(schedule: ifcopenshell.entity_instance) -> set[str]:
|
||||
categories = set()
|
||||
for cost_item in IfcDataGetter.get_root_costs(schedule):
|
||||
IfcDataGetter.process_cost_item_categories(cost_item, categories)
|
||||
return categories
|
||||
|
||||
@staticmethod
|
||||
def process_cost_data(file, cost_item, cost_items_data, index, hierarchy="1"):
|
||||
def process_cost_data(
|
||||
file: ifcopenshell.file,
|
||||
cost_item: ifcopenshell.entity_instance,
|
||||
cost_items_data: list[dict[str, Any]],
|
||||
index: int,
|
||||
hierarchy: str = "1",
|
||||
) -> None:
|
||||
def listToString(s):
|
||||
return ", ".join([str(i) for i in s])
|
||||
|
||||
@@ -134,7 +140,7 @@ class IfcDataGetter:
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_cost_items_data(file, schedule):
|
||||
def get_cost_items_data(file: ifcopenshell.file, schedule: ifcopenshell.entity_instance) -> list[dict[str, Any]]:
|
||||
cost_items_data = []
|
||||
index = 0
|
||||
for cost_item in IfcDataGetter.get_root_costs(schedule):
|
||||
@@ -142,7 +148,7 @@ class IfcDataGetter:
|
||||
return cost_items_data
|
||||
|
||||
@staticmethod
|
||||
def format_unit(unit):
|
||||
def format_unit(unit: ifcopenshell.entity_instance) -> str:
|
||||
if unit.is_a("IfcContextDependentUnit"):
|
||||
return f"{unit.UnitType} / {unit.Name}"
|
||||
else:
|
||||
@@ -152,7 +158,7 @@ class IfcDataGetter:
|
||||
return f"{unit.UnitType} / {name}"
|
||||
|
||||
@staticmethod
|
||||
def get_cost_value_unit(cost_value=None):
|
||||
def get_cost_value_unit(cost_value: Optional[ifcopenshell.entity_instance] = None) -> Union[str, None]:
|
||||
if not cost_value:
|
||||
return None
|
||||
unit = cost_value.UnitBasis
|
||||
@@ -161,9 +167,9 @@ class IfcDataGetter:
|
||||
return IfcDataGetter.format_unit(unit.UnitComponent)
|
||||
|
||||
@staticmethod
|
||||
def get_cost_item_quantity(file, cost_item=None):
|
||||
def get_cost_item_quantity(file: ifcopenshell.file, cost_item: ifcopenshell.entity_instance) -> dict[str, Any]:
|
||||
# TODO: handle multiple quantities, THOSE WHHICH ARE JUYST ASSIGNED TO THE COST ITEM DIRECTLY, NOT THROUGH OBJECTS.
|
||||
def add_quantity(quantity, take_off_name):
|
||||
def add_quantity(quantity: ifcopenshell.entity_instance, take_off_name: str) -> float:
|
||||
accounted_for.append(quantity)
|
||||
if take_off_name == "":
|
||||
take_off_name = quantity[0]
|
||||
@@ -171,26 +177,25 @@ class IfcDataGetter:
|
||||
take_off_name = "mixed-takeoff-quantities"
|
||||
return quantity[3]
|
||||
|
||||
if not cost_item:
|
||||
return None
|
||||
take_off_name = ""
|
||||
has_changed_name = False
|
||||
total_cost_quantity = 0
|
||||
accounted_for = []
|
||||
for rel in cost_item.Controls or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
qtos = ifcopenshell.util.element.get_psets(related_object, qtos_only=True)
|
||||
for quantities in qtos.values() or []:
|
||||
qto = file.by_id(quantities["id"])
|
||||
for quantity in qto.Quantities:
|
||||
if not quantity in cost_item.CostQuantities:
|
||||
continue
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
accounted_for.append(quantity)
|
||||
cost_item_quantities = cost_item.CostQuantities
|
||||
if cost_item_quantities:
|
||||
for rel in cost_item.Controls or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
qtos = ifcopenshell.util.element.get_psets(related_object, qtos_only=True)
|
||||
for quantities in qtos.values() or []:
|
||||
qto = file.by_id(quantities["id"])
|
||||
for quantity in qto.Quantities:
|
||||
if quantity not in cost_item_quantities:
|
||||
continue
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
accounted_for.append(quantity)
|
||||
|
||||
for quantity in cost_item.CostQuantities or []:
|
||||
if not quantity in accounted_for:
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
for quantity in cost_item_quantities:
|
||||
if not quantity in accounted_for:
|
||||
total_cost_quantity += add_quantity(quantity, take_off_name)
|
||||
|
||||
return {
|
||||
"id": cost_item.id(),
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
# Ifc5D - IFC costing utility
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of Ifc5D.
|
||||
#
|
||||
# Ifc5D is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ifc5D is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with Ifc5D. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import os
|
||||
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
|
||||
|
||||
|
||||
Function = namedtuple("Function", ["measure", "name", "description"])
|
||||
rules = {}
|
||||
|
||||
cwd = os.path.dirname(os.path.realpath(__file__))
|
||||
for name in ("IFC4QtoBaseQuantities", "IFC4QtoBaseQuantitiesBlender"):
|
||||
with open(os.path.join(cwd, name + ".json"), "r") as f:
|
||||
rules[name] = json.load(f)
|
||||
|
||||
|
||||
def quantify(ifc_file: ifcopenshell.file, elements: set[ifcopenshell.entity_instance], rules: dict) -> dict:
|
||||
results = {}
|
||||
for calculator, queries in rules["calculators"].items():
|
||||
calculator = calculators[calculator]
|
||||
for query, qtos in queries.items():
|
||||
filtered_elements = ifcopenshell.util.selector.filter_elements(ifc_file, query, elements)
|
||||
if filtered_elements:
|
||||
calculator.calculate(ifc_file, filtered_elements, qtos, results)
|
||||
return results
|
||||
|
||||
|
||||
def edit_qtos(ifc_file, results) -> None:
|
||||
for element, qtos in results.items():
|
||||
for name, quantities in qtos.items():
|
||||
qto = ifcopenshell.util.element.get_pset(element, name, should_inherit=False)
|
||||
if qto:
|
||||
qto = ifc_file.by_id(qto["id"])
|
||||
else:
|
||||
qto = ifcopenshell.api.pset.add_qto(ifc_file, element, name)
|
||||
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."""
|
||||
|
||||
raw_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"),
|
||||
}
|
||||
|
||||
functions = {}
|
||||
for k, v in raw_functions.items():
|
||||
functions[f"gross_{k}"] = Function(v.measure, f"Gross {v.name}", v.description)
|
||||
functions[f"net_{k}"] = Function(v.measure, f"Net {v.name}", v.description)
|
||||
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file,
|
||||
elements: set[ifcopenshell.entity_instance],
|
||||
qtos: dict,
|
||||
results: dict,
|
||||
) -> None:
|
||||
import ifcopenshell
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.shape
|
||||
|
||||
formula_functions = {}
|
||||
|
||||
gross_settings = ifcopenshell.geom.settings()
|
||||
gross_settings.set(gross_settings.DISABLE_OPENING_SUBTRACTIONS, True)
|
||||
net_settings = ifcopenshell.geom.settings()
|
||||
|
||||
gross_qtos = {}
|
||||
net_qtos = {}
|
||||
|
||||
for name, quantities in qtos.items():
|
||||
for quantity, formula in quantities.items():
|
||||
if not formula:
|
||||
continue
|
||||
if formula.startswith("gross_"):
|
||||
formula = formula[6:]
|
||||
gross_qtos.setdefault(name, {})[quantity] = formula
|
||||
formula_functions[formula] = getattr(ifcopenshell.util.shape, formula)
|
||||
elif formula.startswith("net_"):
|
||||
formula = formula[4:]
|
||||
net_qtos.setdefault(name, {})[quantity] = formula
|
||||
formula_functions[formula] = getattr(ifcopenshell.util.shape, formula)
|
||||
|
||||
tasks = []
|
||||
|
||||
if gross_qtos:
|
||||
tasks.append((IfcOpenShell.create_iterator(ifc_file, gross_settings, list(elements)), gross_qtos))
|
||||
|
||||
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:
|
||||
shape = iterator.get()
|
||||
element = ifc_file.by_id(shape.id)
|
||||
results.setdefault(element, {})
|
||||
for name, quantities in qtos.items():
|
||||
results[element].setdefault(name, {})
|
||||
for quantity, formula in quantities.items():
|
||||
results[element][name][quantity] = unit_converter.convert(
|
||||
formula_functions[formula](shape.geometry), IfcOpenShell.raw_functions[formula].measure
|
||||
)
|
||||
if not iterator.next():
|
||||
break
|
||||
|
||||
@staticmethod
|
||||
def create_iterator(
|
||||
ifc_file: ifcopenshell.file, settings: ifcopenshell.geom.settings, elements: list[ifcopenshell.entity_instance]
|
||||
) -> ifcopenshell.geom.iterator:
|
||||
return ifcopenshell.geom.iterator(settings, ifc_file, multiprocessing.cpu_count(), include=elements)
|
||||
|
||||
|
||||
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
|
||||
) -> None:
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.qto.calculator as calculator
|
||||
|
||||
unit_converter = SI2ProjectUnitConverter(ifc_file)
|
||||
formula_functions = {}
|
||||
|
||||
for element in elements:
|
||||
obj = tool.Ifc.get_object(element)
|
||||
if not obj or obj.type != "MESH":
|
||||
continue
|
||||
results.setdefault(element, {})
|
||||
for name, quantities in qtos.items():
|
||||
results[element].setdefault(name, {})
|
||||
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] = unit_converter.convert(
|
||||
formula_function(obj), Blender.functions[formula].measure
|
||||
)
|
||||
|
||||
|
||||
calculators = {"Blender": Blender, "IfcOpenShell": IfcOpenShell}
|
||||
@@ -1,35 +1,4 @@
|
||||
# IFCCityJSON
|
||||
Converter for CityJSON files and IFC. Currently only supports one-way conversion from CityJSON to IFC.
|
||||
|
||||
## Dependencies
|
||||
- [IfcOpenShell](https://github.com/IfcOpenShell/IfcOpenShell) (also IfcOpenShell api is needed)
|
||||
- [CJIO](https://github.com/cityjson/cjio) (>=0.8, <1.0)
|
||||
|
||||
## Usage of IFCCityJSON
|
||||
An extended ifccityjson tutorial can be found on [the OSARCH wiki](https://wiki.osarch.org/index.php?title=Ifccityjson)
|
||||
Following command will execute a conversion from CityJSON to IFC
|
||||
|
||||
python ifccityjson.py [-i input file] [-o output file] [-n name of identification attribute]
|
||||
|
||||
The example file that could be used is example/3D_BAG_example.json
|
||||
|
||||
python ifccityjson.py -i example/3DBAG_example.json -o example/3DBAG_example.ifc -n identificatie
|
||||
|
||||
## Implemented geometries
|
||||
- [x] "MultiPoint"
|
||||
- [x] "MultiLineString"
|
||||
- [x] "MultiSurface"
|
||||
- [x] "CompositeSurface"
|
||||
- [x] "Solid": exterior shell
|
||||
- [ ] "Solid": interior shell
|
||||
- [x] "MultiSolid"
|
||||
- [x] "CompositeSolid"
|
||||
- [ ] "GeometryInstance"
|
||||
|
||||
## TODO
|
||||
- [x] CityJSON Attributes as IFC properties in 'CityJSON_attributes' pset
|
||||
- [x] Implement georeferencing
|
||||
- [x] Do not use template IFC for new IFC file, but make IFC file from scratch
|
||||
- [x] Create mapping to IFC for all CityJSON object types & semantic surfaces
|
||||
- [ ] Implement conversion of all CitYJSON geometries
|
||||
- [x] Implement conversion of all LODs instead of only the most detailed
|
||||
IfcCityJSON is a converter for CityJSON files and IFC. It currently only
|
||||
supports one-way conversion from CityJSON to IFC.
|
||||
|
||||
+2
-2
@@ -18,7 +18,7 @@
|
||||
|
||||
import argparse
|
||||
from cjio import cityjson
|
||||
from cityjson2ifc.cityjson2ifc import Cityjson2ifc
|
||||
from .cityjson2ifc import Cityjson2ifc
|
||||
|
||||
def cmdline():
|
||||
# Example:
|
||||
@@ -51,4 +51,4 @@ def cmdline():
|
||||
converter.convert(city_model)
|
||||
|
||||
if __name__ == '__main__':
|
||||
cmdline()
|
||||
cmdline()
|
||||
@@ -146,7 +146,7 @@ class Cityjson2ifc:
|
||||
# Georeferencing
|
||||
self.properties["local_translation"] = None
|
||||
self.properties["local_scale"] = None
|
||||
if not self.city_model.is_transformed:
|
||||
if self.city_model.is_transformed:
|
||||
self.properties["local_scale"] = self.city_model.transform["scale"]
|
||||
local_translation = self.city_model.transform["translate"]
|
||||
self.properties["local_translation"] = {
|
||||
|
||||
@@ -265,7 +265,7 @@ config = {
|
||||
"AreaUnit",
|
||||
"Phase",
|
||||
],
|
||||
"colours": "ppppreeeeesss",
|
||||
"colours": "ppprrreeeeesss",
|
||||
"sort": [{"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_facilities,
|
||||
"get_element_data": get_facility_data,
|
||||
@@ -283,7 +283,7 @@ config = {
|
||||
"ModelID",
|
||||
"Elevation",
|
||||
],
|
||||
"colours": "ppreeees",
|
||||
"colours": "prrreeees",
|
||||
"sort": [{"name": "Elevation", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_storeys,
|
||||
"get_element_data": get_storey_data,
|
||||
@@ -304,7 +304,7 @@ config = {
|
||||
"GrossFloorArea",
|
||||
"NetFloorArea",
|
||||
],
|
||||
"colours": "ppprreeess",
|
||||
"colours": "pprrrreeesss",
|
||||
"sort": [{"name": "StoreyName", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_spaces,
|
||||
"get_element_data": get_space_data,
|
||||
@@ -336,7 +336,7 @@ config = {
|
||||
"PointOfContact",
|
||||
"WarrantyPeriod",
|
||||
],
|
||||
"colours": "pppreeeeesssss",
|
||||
"colours": "pprrreeeeesssss",
|
||||
"sort": [{"name": "ModelObject", "order": "ASC"}, {"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_element_types,
|
||||
"get_element_data": get_element_type_data,
|
||||
@@ -379,7 +379,7 @@ config = {
|
||||
"ModelSoftware",
|
||||
"ModelID",
|
||||
],
|
||||
"colours": "pppreee",
|
||||
"colours": "pprrreee",
|
||||
"sort": [{"name": "Name", "order": "ASC"}],
|
||||
"get_category_elements": get_systems,
|
||||
"get_element_data": get_system_data,
|
||||
|
||||
@@ -3,3 +3,62 @@ IfcCityJSON
|
||||
|
||||
IfcCityJSON is a converter for CityJSON files and IFC. It currently only
|
||||
supports one-way conversion from CityJSON to IFC.
|
||||
|
||||
Source installation
|
||||
-------------------
|
||||
|
||||
1. :doc:`Install IfcOpenShell <ifcopenshell-python/installation>`
|
||||
2. `Clone the source code <https://github.com/IfcOpenShell/IfcOpenShell/tree/v0.7.0/src/ifccityjson>`_.
|
||||
3. ``cd /path/to/IfcOpenShell/src/ifccityjson``
|
||||
4. ``pip install .``
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
An extended ifccityjson tutorial can be found on `the OSARCH wiki
|
||||
<https://wiki.osarch.org/index.php?title=Ifccityjson>`_. Here's the built-in
|
||||
documentation:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ python -m ifccityjson -h
|
||||
|
||||
usage: __main__.py [-h] -i INPUT [-o OUTPUT] [-n NAME] [--split-lod] [--no-split-lod] [--lod LOD]
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
-i INPUT, --input INPUT
|
||||
input CityJSON file
|
||||
-o OUTPUT, --output OUTPUT
|
||||
output IFC file. Standard is output.ifc
|
||||
-n NAME, --name NAME Attribute containing the name
|
||||
--split-lod Split the file in multiple LoDs
|
||||
--no-split-lod Do not split the file in multiple LoDs
|
||||
--lod LOD extract LOD value (example: 1.2)
|
||||
|
||||
The example file that could be used is example/3D_BAG_example.json
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
python ifccityjson.py -i example/geometries.json -o output.ifc -n identificatie
|
||||
|
||||
The following geometries are implemented:
|
||||
|
||||
- [x] "MultiPoint"
|
||||
- [x] "MultiLineString"
|
||||
- [x] "MultiSurface"
|
||||
- [x] "CompositeSurface"
|
||||
- [x] "Solid": exterior shell
|
||||
- [ ] "Solid": interior shell
|
||||
- [x] "MultiSolid"
|
||||
- [x] "CompositeSolid"
|
||||
- [ ] "GeometryInstance"
|
||||
|
||||
TODO:
|
||||
|
||||
- [x] CityJSON Attributes as IFC properties in 'CityJSON_attributes' pset
|
||||
- [x] Implement georeferencing
|
||||
- [x] Do not use template IFC for new IFC file, but make IFC file from scratch
|
||||
- [x] Create mapping to IFC for all CityJSON object types & semantic surfaces
|
||||
- [ ] Implement conversion of all CitYJSON geometries
|
||||
- [x] Implement conversion of all LODs instead of only the most detailed
|
||||
|
||||
@@ -63,3 +63,21 @@ with this data structure that IfcFM supports:
|
||||
installation. As expected, the majority of these are already referenced in
|
||||
named standards. "Vanilla" IFC offers a "specification agnostic" approach
|
||||
towards facility management data collection.
|
||||
|
||||
COBie 2.4 vs COBie 2.4 Legacy
|
||||
-----------------------------
|
||||
|
||||
In collaboration with leading UK consultancy BuildData Group (formerly Bond
|
||||
Bryan Digital), and inventor of COBie 2.4 Bill East (Prarie Sky Consulting), an
|
||||
effort was made to preserve the original IFC mapping, as well as modernise the
|
||||
mapping with the following goals:
|
||||
|
||||
1. For anyone delivering **COBie 2.4** data using best practices from graphical
|
||||
BIM software, there must be *no difference* between **COBie 2.4** and
|
||||
**COBie 2.4 Legacy**. It must not contradict any specifications in the
|
||||
official NBIMS-US and BS standards.
|
||||
2. Update compatibility to IFC4X3.
|
||||
3. Prioritise organisational data instead of personal data with discourage PII.
|
||||
4. Prioritise bSI standardised property sets over custom ones.
|
||||
5. Prevent needless repetition of data / fallback defaults that may result in
|
||||
invalid or unexpected data in obscure edge cases.
|
||||
|
||||
@@ -228,6 +228,7 @@ Here is a simple example in Python:
|
||||
if iterator.initialize():
|
||||
while True:
|
||||
shape = iterator.get()
|
||||
element = ifc_file.by_id(shape.id)
|
||||
matrix = shape.transformation.matrix.data
|
||||
faces = shape.geometry.faces
|
||||
edges = shape.geometry.edges
|
||||
|
||||
@@ -235,6 +235,6 @@ nest formulas, for example ``concat(title("foo"), lower("Bar"))`` will produce
|
||||
"``title({{value}})``", "``title(""foo"")``", "``Foo``", "Titlecases a string."
|
||||
"``concat({{value}}[, {{value2}}]*)``", "``concat(""foo"", ""bar"")``", "``foobar``", "Concatenates two or more strings."
|
||||
"``round({{value}}, {{precision}})``", "``round(3.123, 0.1)``", "``3.1``", "Rounds ``{{value}}`` to the nearest ``{{precision}}``."
|
||||
"``number({{value}}[, {{decimal_separator}}[, {{thousands_separator}}]])``", "``number(1234.56, "","", ""."")``", "1.234,56", "Formats {{value}} with an optional custom {{decimal_separator}} and {{thousands_separator}}. The default separators are ``.`` and ``,``."
|
||||
"``number({{value}}[, {{decimal_separator}}[, {{thousands_separator}}]])``", "``number(1234.56, "","", ""."")``", "``1.234,56``", "Formats {{value}} with an optional custom {{decimal_separator}} and {{thousands_separator}}. The default separators are ``.`` and ``,``."
|
||||
"``metric_length({{value}}, {{precision}}, {{decimals}})``", "``metric_length(3.123, 0.1, 2)``", "``3.10``", "Rounds ``{{value}}`` to the nearest ``{{precision}}`` then displays using a certain amount of decimal places."
|
||||
"``imperial_length({{value}}, {{precision}}, {{input_unit}}, {{output_unit}})``", "``imperial_length(3.22, 4, ""foot"")``", "``3' - 3 3/4""``", "``The {{value}}`` may be specified either as ``foot`` or ``inch`` depending on ``{{input_unit}}``. The ``{{value}}`` is then rounded to the nearest ``1/{{precision}}`` inch then formatted using fractional feet and inches if ``{{output_unit}}`` is set to ``foot`` or just inches if ``{{output_unit}}`` is set to ``inch``."
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
import datetime
|
||||
from re import findall
|
||||
from dateutil import parser
|
||||
from typing import Literal, Union, Any
|
||||
from typing import Literal, Union, Any, overload
|
||||
|
||||
try:
|
||||
import isodate
|
||||
@@ -105,8 +105,11 @@ def readable_ifc_duration(string):
|
||||
return final_string
|
||||
|
||||
|
||||
@overload
|
||||
def datetime2ifc(dt: None, ifc_type: Any) -> None: ...
|
||||
@overload
|
||||
def datetime2ifc(
|
||||
dt: Union[datetime.date, str],
|
||||
dt: Union[datetime.date, str, None],
|
||||
ifc_type: Literal[
|
||||
"IfcDuration",
|
||||
"IfcTimeStamp",
|
||||
@@ -116,7 +119,19 @@ def datetime2ifc(
|
||||
"IfcCalendarDate",
|
||||
"IfcLocalTime",
|
||||
],
|
||||
) -> Union[int, str, dict[str, Any]]:
|
||||
) -> Union[int, str, dict[str, Any], None]: ...
|
||||
def datetime2ifc(
|
||||
dt: Union[datetime.date, str, None],
|
||||
ifc_type: Literal[
|
||||
"IfcDuration",
|
||||
"IfcTimeStamp",
|
||||
"IfcDateTime",
|
||||
"IfcDate",
|
||||
"IfcTime",
|
||||
"IfcCalendarDate",
|
||||
"IfcLocalTime",
|
||||
],
|
||||
) -> Union[int, str, dict[str, Any], None]:
|
||||
if isinstance(dt, str):
|
||||
if ifc_type == "IfcDuration":
|
||||
return dt
|
||||
@@ -124,6 +139,8 @@ def datetime2ifc(
|
||||
dt = datetime.datetime.fromisoformat(dt)
|
||||
except:
|
||||
dt = datetime.time.fromisoformat(dt)
|
||||
elif dt is None:
|
||||
return
|
||||
|
||||
if ifc_type == "IfcDuration":
|
||||
return isodate.duration_isoformat(dt)
|
||||
@@ -157,7 +174,8 @@ def datetime2ifc(
|
||||
"MinuteComponent": dt.minute,
|
||||
"SecondComponent": dt.second,
|
||||
}
|
||||
raise TypeError(f"Unsupported ifc_type for conversion from datetime.datetime = {ifc_type}.")
|
||||
|
||||
raise TypeError(f"Unsupported ifc_type for conversion from datetime.datetime = {ifc_type}, value = {dt}")
|
||||
|
||||
|
||||
def string_to_date(string):
|
||||
|
||||
@@ -833,7 +833,7 @@ def get_layers(
|
||||
|
||||
def get_container(
|
||||
element: ifcopenshell.entity_instance, should_get_direct: bool = False, ifc_class: Optional[str] = None
|
||||
) -> ifcopenshell.entity_instance:
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""
|
||||
Retrieves the spatial structure container of an element.
|
||||
|
||||
@@ -849,7 +849,7 @@ def get_container(
|
||||
example, you may be after the storey, not a space.
|
||||
:type ifc_class: str, optional
|
||||
:return: The direct or indirect container of the element or None.
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
RUN_FROM_DEV_REPO = False
|
||||
|
||||
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.guid
|
||||
import ifcopenshell.util.attribute
|
||||
@@ -33,9 +34,9 @@ if not RUN_FROM_DEV_REPO:
|
||||
import shutil
|
||||
|
||||
BASE_MODULE_PATH = Path(__file__).parent
|
||||
IFC4x3_HTML_LOCATION = BASE_MODULE_PATH / "IFC4.3-html-iso-release"
|
||||
|
||||
if not RUN_FROM_DEV_REPO:
|
||||
IFC4x3_HTML_ZIP_LOCATION = BASE_MODULE_PATH / "annex-a-psd.zip"
|
||||
IFC4x3_OUTPUT_PATH = BASE_MODULE_PATH / "schema/Pset_IFC4X3.ifc"
|
||||
else:
|
||||
IFC4x3_PSD_LOCATION = BASE_MODULE_PATH / "../output/psd"
|
||||
@@ -67,20 +68,18 @@ class PsetTemplatesGenerator:
|
||||
print("Starting parsing data for IFC4X3...")
|
||||
|
||||
if not RUN_FROM_DEV_REPO:
|
||||
if not IFC4x3_HTML_LOCATION.is_dir():
|
||||
if not IFC4x3_HTML_ZIP_LOCATION.is_file():
|
||||
raise Exception(
|
||||
f'ISO release for Ifc4.3.0.1 expected to be in folder "{IFC4x3_HTML_LOCATION.resolve()}\\"\n'
|
||||
f'ISO release for Ifc4.3.2.0 expected to be located in "{IFC4x3_HTML_ZIP_LOCATION.resolve()}"\n'
|
||||
"For generating ifc pset library please either setup docs as described above \n"
|
||||
"or change IFC4x3_HTML_LOCATION in the script accordingly.\n"
|
||||
"or change IFC4x3_HTML_ZIP_LOCATION in the script accordingly.\n"
|
||||
"You can download docs from the repository: \n"
|
||||
"https://github.com/buildingSMART/IFC4.3-html/releases/tag/sep-13-release"
|
||||
"https://standards.buildingsmart.org/IFC/RELEASE/IFC4_3/HTML/annex-a-psd.zip"
|
||||
)
|
||||
# unzip the data
|
||||
if not RUN_FROM_DEV_REPO:
|
||||
pset_data_zip = IFC4x3_HTML_LOCATION / "IFC/RELEASE/IFC4x3/HTML/annex-a-psd.zip"
|
||||
pset_data_location = BASE_MODULE_PATH / "temp/annex-a-psd"
|
||||
with zipfile.ZipFile(pset_data_zip, "r") as fi_zip:
|
||||
fi_zip.extractall(pset_data_location)
|
||||
pset_data_location = BASE_MODULE_PATH / "temp/annex-a-psd"
|
||||
with zipfile.ZipFile(IFC4x3_HTML_ZIP_LOCATION, "r") as fi_zip:
|
||||
fi_zip.extractall(pset_data_location)
|
||||
else:
|
||||
if not IFC4x3_PSD_LOCATION.is_dir():
|
||||
raise Exception(
|
||||
@@ -100,7 +99,7 @@ class PsetTemplatesGenerator:
|
||||
print("Starting parsing data for IFC2X3...")
|
||||
if not IFC2x3_HTML_ZIP_LOCATION.is_file():
|
||||
raise Exception(
|
||||
f'ISO release for IFC2x3 TC1 expected to be in folder "{IFC2x3_HTML_ZIP_LOCATION.resolve()}\\"\n'
|
||||
f'ISO release for IFC2x3 TC1 expected to be located in "{IFC2x3_HTML_ZIP_LOCATION.resolve()}"\n'
|
||||
"For doc extraction please either setup docs as described above \n"
|
||||
"or change IFC2x3_HTML_LOCATION in the script accordingly.\n"
|
||||
"You can download docs from the url: \n"
|
||||
@@ -176,8 +175,12 @@ class PsetTemplatesGenerator:
|
||||
TemplateType=root_xml.get("templatetype"),
|
||||
Name=pset_name,
|
||||
Description=root_xml.find("Definition").text,
|
||||
ApplicableEntity=",".join(applicable_entities),
|
||||
ApplicableEntity=",".join(applicable_entities).strip(),
|
||||
)
|
||||
if project_name.startswith("IFC2X3"):
|
||||
pset.TemplateType = self.get_pset_template_type_ifc2x3(pset)
|
||||
else:
|
||||
pset.TemplateType = root_xml.get("templatetype")
|
||||
|
||||
# NOTE: there is also Applicability tag
|
||||
# but it's seems always empty in ifc4 and ifc4x3
|
||||
@@ -369,6 +372,43 @@ class PsetTemplatesGenerator:
|
||||
|
||||
pset_property.PrimaryMeasureType = primary_measure_type
|
||||
|
||||
def get_pset_template_type_ifc2x3(self, pset_template: ifcopenshell.entity_instance) -> str:
|
||||
def declaration_is_a(declaration: ifcopenshell_wrapper.declaration, ifc_class: str) -> bool:
|
||||
if declaration.name() == ifc_class:
|
||||
return True
|
||||
super_type = declaration.supertype()
|
||||
if not super_type:
|
||||
return False
|
||||
return declaration_is_a(super_type, ifc_class)
|
||||
|
||||
name = pset_template.Name
|
||||
applicability = pset_template.ApplicableEntity
|
||||
schema = ifcopenshell.schema_by_name("IFC2X3")
|
||||
|
||||
if "PHistory" in name:
|
||||
return "PSET_PERFORMANCEDRIVEN"
|
||||
applicable_types = applicability.replace(", ", ",").split(",")
|
||||
for applicable_type in applicable_types:
|
||||
if not applicable_type:
|
||||
continue
|
||||
parts = applicable_type.split("/")
|
||||
assert 3 > len(parts) > 0
|
||||
if parts[0].isupper(): # IFC2X3 thing
|
||||
applicable_type = parts[1]
|
||||
else:
|
||||
applicable_type = parts[0]
|
||||
applicable_type = applicable_type.strip()
|
||||
|
||||
declaration = schema.declaration_by_name(applicable_type)
|
||||
if declaration_is_a(declaration, "IfcTypeObject"):
|
||||
return "PSET_TYPEDRIVENOVERRIDE"
|
||||
# ifc4x3+
|
||||
elif declaration_is_a(declaration, "IfcProfileDef"):
|
||||
return "PSET_PROFILEDRIVEN"
|
||||
elif declaration_is_a(declaration, "IfcMaterialDefinition"):
|
||||
return "PSET_MATERIALDRIVEN"
|
||||
return "PSET_OCCURRENCEDRIVEN"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
templates_generator = PsetTemplatesGenerator()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -291,9 +291,10 @@ def filter_elements(
|
||||
:type ifc_file: ifcopenshell.file
|
||||
:param query: Query to execute
|
||||
:type query: str
|
||||
:param elements: Base set of IFC elements for the query.
|
||||
If provided, new elements found for the current query will be added to `elements`.
|
||||
Elements explicitly excluded in the `query` will also be excluded from `elements`
|
||||
:param elements: Base set of IFC elements for the query. If not provided,
|
||||
all elements in the IFC are queried. If provided, the query will be
|
||||
applied to this set of elements, so the result will be a subset of
|
||||
elements.
|
||||
:type elements: set[ifcopenshell.entity_instance], optional
|
||||
:param edit_in_place: If `True`, mutate the provided `elements` in place. Defaults to `False`
|
||||
:type edit_in_place: bool
|
||||
@@ -497,11 +498,17 @@ def set_element_value(
|
||||
f"Failed to set value for element '{original_element}' with query '{query}' (invalid or unsupported query)."
|
||||
)
|
||||
|
||||
|
||||
class FacetTransformer(lark.Transformer):
|
||||
def __init__(self, ifc_file: ifcopenshell.file, elements: Optional[set[ifcopenshell.entity_instance]] = None):
|
||||
self.file = ifc_file
|
||||
self.results = []
|
||||
self.elements = set() if elements is None else elements
|
||||
if elements is None:
|
||||
self.base_elements = None
|
||||
self.elements = set()
|
||||
else:
|
||||
self.base_elements = elements.copy()
|
||||
self.elements = set()
|
||||
self.container_parents = {}
|
||||
self.container_trees = {}
|
||||
|
||||
@@ -517,28 +524,44 @@ class FacetTransformer(lark.Transformer):
|
||||
self.elements = set()
|
||||
|
||||
def instance(self, args):
|
||||
if args[0].data == "globalid":
|
||||
try:
|
||||
self.elements.add(self.file.by_guid(args[0].children[0].value))
|
||||
except:
|
||||
pass
|
||||
if self.base_elements is None:
|
||||
if args[0].data == "globalid":
|
||||
try:
|
||||
self.elements.add(self.file.by_guid(args[0].children[0].value))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self.elements.remove(self.file.by_guid(args[1].children[0].value))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self.elements.remove(self.file.by_guid(args[1].children[0].value))
|
||||
except:
|
||||
pass
|
||||
if args[0].data == "globalid":
|
||||
self.elements |= {
|
||||
e for e in self.base_elements if getattr(e, "GlobalId", None) == args[0].children[0].value
|
||||
}
|
||||
else:
|
||||
self.elements -= {
|
||||
e for e in self.base_elements if getattr(e, "GlobalId", None) == args[1].children[0].value
|
||||
}
|
||||
|
||||
def entity(self, args):
|
||||
if args[0].data == "ifc_class":
|
||||
try:
|
||||
self.elements |= set(self.file.by_type(args[0].children[0].value))
|
||||
except:
|
||||
pass
|
||||
if self.base_elements is None:
|
||||
if args[0].data == "ifc_class":
|
||||
try:
|
||||
self.elements |= set(self.file.by_type(args[0].children[0].value))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self.elements -= set(self.file.by_type(args[1].children[0].value))
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
self.elements -= set(self.file.by_type(args[1].children[0].value))
|
||||
except:
|
||||
pass
|
||||
if args[0].data == "ifc_class":
|
||||
self.elements |= {e for e in self.base_elements if e.is_a(args[0].children[0].value)}
|
||||
else:
|
||||
self.elements -= {e for e in self.base_elements if e.is_a(args[1].children[0].value)}
|
||||
|
||||
def attribute(self, args):
|
||||
name, comparison, value = args
|
||||
|
||||
@@ -117,6 +117,28 @@ def get_z(geometry) -> float:
|
||||
return max(z_values) - min(z_values)
|
||||
|
||||
|
||||
def get_max_xyz(geometry) -> float:
|
||||
"""Gets the maximum X, Y, or Z length of the geometry
|
||||
|
||||
:param geometry: Geometry output calculated by IfcOpenShell
|
||||
:type geometry: geometry
|
||||
:return: The maximum possible value out of the X, Y, and Z dimension
|
||||
:rtype: float
|
||||
"""
|
||||
return max(get_x(geometry), get_y(geometry), get_z(geometry))
|
||||
|
||||
|
||||
def get_min_xyz(geometry) -> float:
|
||||
"""Gets the minimum X, Y, or Z length of the geometry
|
||||
|
||||
:param geometry: Geometry output calculated by IfcOpenShell
|
||||
:type geometry: geometry
|
||||
:return: The minimum possible value out of the X, Y, and Z dimension
|
||||
:rtype: float
|
||||
"""
|
||||
return min(get_x(geometry), get_y(geometry), get_z(geometry))
|
||||
|
||||
|
||||
def get_shape_matrix(shape) -> MatrixType:
|
||||
"""Formats the transformation matrix of a shape as a 4x4 numpy array
|
||||
|
||||
@@ -486,6 +508,19 @@ def get_side_area(
|
||||
return get_area_vf(vertices, filtered_faces)
|
||||
|
||||
|
||||
def get_max_side_area(geometry) -> float:
|
||||
"""Returns the maximum X, Y, or Z side area
|
||||
|
||||
See :func:`get_side_area` for how side area is calculated.
|
||||
|
||||
:param geometry: Geometry output calculated by IfcOpenShell
|
||||
:type geometry: geometry
|
||||
:return: The maximum surface area from either the X, Y, or Z axis.
|
||||
:rtype: float
|
||||
"""
|
||||
return max(get_side_area(geometry, axis="X"), get_side_area(geometry, axis="Y"), get_side_area(geometry, axis="Z"))
|
||||
|
||||
|
||||
def get_footprint_area(
|
||||
geometry,
|
||||
axis: AXIS_LITERAL = "Z",
|
||||
|
||||
@@ -421,8 +421,8 @@ def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcop
|
||||
:param unit_type: The type of unit, taken from the list of IFC unit types,
|
||||
such as "LENGTHUNIT".
|
||||
:type unit_type: str
|
||||
:return: The IFC unit entity, or nothing if there is no default project unit
|
||||
defined.
|
||||
:return: The IFC unit entity, or nothing if there is no default project
|
||||
unit defined.
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
"""
|
||||
unit_assignment = get_unit_assignment(ifc_file)
|
||||
@@ -435,6 +435,20 @@ def get_project_unit(ifc_file: ifcopenshell.file, unit_type: str) -> Union[ifcop
|
||||
def get_property_unit(
|
||||
prop: ifcopenshell.entity_instance, ifc_file: ifcopenshell.file
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Gets the unit definition of a property or quantity
|
||||
|
||||
Properties and quantities in psets and qtos can be associated with a unit.
|
||||
This unit may be defined at the property itself explicitly, or if not
|
||||
specified, fallback to the project default.
|
||||
|
||||
:param prop: The property instance. You can fetch this via the instance ID
|
||||
if doing :func:`ifcopenshell.util.element.get_psets` with
|
||||
``verbose=True``.
|
||||
:param ifc_file: The IFC file being used. This is necessary to check
|
||||
default project units.
|
||||
:return: The IFC unit entity, or nothing if there is no default project
|
||||
unit defined.
|
||||
"""
|
||||
unit = getattr(prop, "Unit", None)
|
||||
if unit:
|
||||
return unit
|
||||
@@ -488,6 +502,16 @@ def get_property_unit(
|
||||
|
||||
|
||||
def get_unit_measure_class(unit_type: str) -> MEASURE_CLASS:
|
||||
"""Get the IFC measure class for a unit type.
|
||||
|
||||
IFC has specific classes used to measure different units. An example of an
|
||||
IFC measure class is ``IfcLengthMeasure``. An example of the correlating
|
||||
unit type (i.e. the IfcUnitEnum) is ``LENGTHUNIT``.
|
||||
|
||||
The inverse function of this is :func:`get_measure_unit_type`
|
||||
|
||||
:param unit_type: A string chosen from IfcUnitEnum, such as LENGTHUNIT
|
||||
"""
|
||||
if unit_type == "USERDEFINED":
|
||||
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/71
|
||||
return "IfcNumericMeasure"
|
||||
@@ -495,6 +519,20 @@ def get_unit_measure_class(unit_type: str) -> MEASURE_CLASS:
|
||||
|
||||
|
||||
def get_measure_unit_type(measure_class: MEASURE_CLASS) -> str:
|
||||
"""Get the unit type of an IFC measure class
|
||||
|
||||
IFC has different unit types which can be associated with units (e.g. SI
|
||||
units, imperial units, derived units, etc). An example of a unit type (i.e.
|
||||
an IfcUnitEnum) is ``LENGTHUNIT``. An example of the correlating measure
|
||||
class used to store length data is ``IfcLengthMeasure``.
|
||||
|
||||
The inverse fucntion of this is :func:`get_unit_measure_class`
|
||||
|
||||
:param measure_class: The measure class, such as ``IfcLengthMeasure``. If
|
||||
you have an ``IfcPropertySingleValue``, you can get this using
|
||||
``prop.NominalValue.is_a()``.
|
||||
:return: The unit type, as an uppercase value of IfcUnitEnum.
|
||||
"""
|
||||
if measure_class == "IfcNumericMeasure":
|
||||
# See https://github.com/buildingSMART/IFC4.3.x-development/issues/71
|
||||
return "USERDEFINED"
|
||||
|
||||
@@ -25,41 +25,41 @@ import ifcopenshell.util.pset
|
||||
import numpy as np
|
||||
|
||||
|
||||
class TestFormat():
|
||||
class TestFormat:
|
||||
def test_no_formatting(self):
|
||||
assert subject.format("123") == "123"
|
||||
assert subject.format('\"123\"') == "123"
|
||||
assert subject.format('\"foo\"') == "foo"
|
||||
assert subject.format('"123"') == "123"
|
||||
assert subject.format('"foo"') == "foo"
|
||||
|
||||
def test_string_formatting(self):
|
||||
assert subject.format('upper(\"fOo\")') == "FOO"
|
||||
assert subject.format('lower(\"fOo\")') == "foo"
|
||||
assert subject.format('title(\"fOo\")') == "Foo"
|
||||
assert subject.format('concat(\"fOo\", \"bar\")') == "fOobar"
|
||||
assert subject.format('upper(concat(\"fOo\", \"bar\"))') == "FOOBAR"
|
||||
assert subject.format('substr(\"foobar\", 3)') == "bar"
|
||||
assert subject.format('substr(\"foobar\", 1, 2)') == "o"
|
||||
assert subject.format('substr(\"foobar\", 1, -1)') == "ooba"
|
||||
assert subject.format('upper("fOo")') == "FOO"
|
||||
assert subject.format('lower("fOo")') == "foo"
|
||||
assert subject.format('title("fOo")') == "Foo"
|
||||
assert subject.format('concat("fOo", "bar")') == "fOobar"
|
||||
assert subject.format('upper(concat("fOo", "bar"))') == "FOOBAR"
|
||||
assert subject.format('substr("foobar", 3)') == "bar"
|
||||
assert subject.format('substr("foobar", 1, 2)') == "o"
|
||||
assert subject.format('substr("foobar", 1, -1)') == "ooba"
|
||||
|
||||
def test_number_formatting(self):
|
||||
assert subject.format("round(123, 5)") == "125"
|
||||
assert subject.format('round(\"123\", 5)') == "125"
|
||||
assert subject.format('number(123)') == "123"
|
||||
assert subject.format('number(1234.56)') == "1,234.56"
|
||||
assert subject.format('round("123", 5)') == "125"
|
||||
assert subject.format("number(123)") == "123"
|
||||
assert subject.format("number(1234.56)") == "1,234.56"
|
||||
assert subject.format('number(123, ".")') == "123"
|
||||
assert subject.format('number(\"123\", ".")') == "123"
|
||||
assert subject.format('number("123", ".")') == "123"
|
||||
assert subject.format('number(123.12, ".")') == "123.12"
|
||||
assert subject.format('number(123.12, ",")') == "123,12"
|
||||
assert subject.format('number(1234.12, ",", ".")') == "1.234,12"
|
||||
assert subject.format('metric_length(123, 5, 2)') == "125.00"
|
||||
assert subject.format('metric_length(123.123, 0.1, 2)') == "123.10"
|
||||
assert subject.format('metric_length(\"123\", 5, 2)') == "125.00"
|
||||
assert subject.format('imperial_length(1, 1)') == "1'"
|
||||
assert subject.format('imperial_length(3.123, 1)') == "3' - 1\""
|
||||
assert subject.format('imperial_length(3.123, 2)') == "3' - 1 1/2\""
|
||||
assert subject.format('imperial_length(\"3.123\", 2)') == "3' - 1 1/2\""
|
||||
assert subject.format('imperial_length(\"123.123\", 2, \"inch\", \"foot\")') == "10' - 3\""
|
||||
assert subject.format('imperial_length(\"123.123\", 2, \"inch\", \"inch\")') == "123\""
|
||||
assert subject.format("metric_length(123, 5, 2)") == "125.00"
|
||||
assert subject.format("metric_length(123.123, 0.1, 2)") == "123.10"
|
||||
assert subject.format('metric_length("123", 5, 2)') == "125.00"
|
||||
assert subject.format("imperial_length(1, 1)") == "1'"
|
||||
assert subject.format("imperial_length(3.123, 1)") == "3' - 1\""
|
||||
assert subject.format("imperial_length(3.123, 2)") == "3' - 1 1/2\""
|
||||
assert subject.format('imperial_length("3.123", 2)') == "3' - 1 1/2\""
|
||||
assert subject.format('imperial_length("123.123", 2, "inch", "foot")') == "10' - 3\""
|
||||
assert subject.format('imperial_length("123.123", 2, "inch", "inch")') == '123"'
|
||||
|
||||
|
||||
class TestGetElementValue(test.bootstrap.IFC4):
|
||||
@@ -269,17 +269,19 @@ class TestFilterElements(test.bootstrap.IFC4):
|
||||
def test_using_elements_argument(self):
|
||||
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
slab = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
||||
door = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcDoor")
|
||||
elements = {wall, slab}
|
||||
results = subject.filter_elements(self.file, "IfcWall", {wall, slab})
|
||||
assert results != elements
|
||||
assert results == {wall}
|
||||
elements = {wall, slab, door}
|
||||
assert subject.filter_elements(self.file, "IfcWall, IfcSlab", elements) == {wall, slab}
|
||||
|
||||
# keep elements unaffected by expression
|
||||
assert subject.filter_elements(self.file, "IfcWall", {slab}) == {wall, slab}
|
||||
|
||||
# filter out excluded elements
|
||||
assert subject.filter_elements(self.file, "IfcWall, ! IfcSlab", {slab}) == {wall}
|
||||
|
||||
# edit_in_place to update original set
|
||||
original_set = set()
|
||||
def test_editing_in_place(self):
|
||||
wall = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
original_set = {wall}
|
||||
new_set = subject.filter_elements(self.file, "IfcWall", original_set, edit_in_place=True)
|
||||
assert new_set == original_set
|
||||
assert new_set == original_set == {wall}
|
||||
|
||||
|
||||
class TestSetElementValue(test.bootstrap.IFC4):
|
||||
|
||||
@@ -46,10 +46,10 @@ class Patcher:
|
||||
.. code:: python
|
||||
|
||||
# Convert to millimeters
|
||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertLengthUnit", "arguments": ["MILLIMETER"]})
|
||||
model = ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertLengthUnit", "arguments": ["MILLIMETER"]})
|
||||
|
||||
# Convert to feet
|
||||
ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertLengthUnit", "arguments": ["FOOT"]})
|
||||
model = ifcpatch.execute({"input": "input.ifc", "file": model, "recipe": "ConvertLengthUnit", "arguments": ["FOOT"]})
|
||||
"""
|
||||
self.src = src
|
||||
self.file = file
|
||||
|
||||
Reference in New Issue
Block a user