mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 01:11:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6247fb3cdf |
@@ -142,7 +142,6 @@ classes = [
|
||||
ui.BIM_PT_tabs,
|
||||
# Project overview
|
||||
ui.BIM_PT_tab_project_info,
|
||||
ui.BIM_PT_tab_spatial_decomposition,
|
||||
ui.BIM_PT_tab_project_setup,
|
||||
ui.BIM_PT_tab_geometry,
|
||||
ui.BIM_PT_tab_stakeholders,
|
||||
|
||||
Binary file not shown.
@@ -37,7 +37,6 @@ import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.shape
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.spatial
|
||||
import ifcopenshell.ifcopenshell_wrapper as ifcopenshell_wrapper
|
||||
from itertools import chain, accumulate
|
||||
from blenderbim.bim.ifc import IfcStore, IFC_CONNECTED_TYPE
|
||||
@@ -302,7 +301,6 @@ class IfcImporter:
|
||||
self.setup_viewport_camera()
|
||||
self.setup_arrays()
|
||||
self.profile_code("Setup arrays")
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
self.update_progress(100)
|
||||
bpy.context.window_manager.progress_end()
|
||||
|
||||
@@ -1704,7 +1702,6 @@ class IfcImporter:
|
||||
if aggregate["element"].is_a("IfcElementType"):
|
||||
self.type_collection.children.link(aggregate["collection"])
|
||||
continue
|
||||
self.project["blender"].children.link(aggregate["collection"])
|
||||
|
||||
def create_materials(self) -> None:
|
||||
for material in self.file.by_type("IfcMaterial"):
|
||||
|
||||
@@ -36,6 +36,7 @@ class AggregateData:
|
||||
"relating_object_label": cls.get_relating_object_label(),
|
||||
"has_related_objects": cls.has_related_objects(),
|
||||
"total_parts": cls.total_parts(),
|
||||
"ifc_class": cls.ifc_class(),
|
||||
"total_linked_aggregate": cls.total_linked_aggregate(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
@@ -67,6 +68,12 @@ class AggregateData:
|
||||
def has_related_objects(cls) -> bool:
|
||||
return bool(cls.get_related_objects())
|
||||
|
||||
@classmethod
|
||||
def ifc_class(cls) -> str:
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if element:
|
||||
return element.is_a()
|
||||
|
||||
@classmethod
|
||||
def total_linked_aggregate(cls):
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
|
||||
@@ -55,24 +55,21 @@ class BIM_OT_aggregate_assign_object(bpy.types.Operator, Operator):
|
||||
if not relating_obj:
|
||||
return
|
||||
|
||||
for obj in tool.Blender.get_selected_objects():
|
||||
for obj in bpy.context.selected_objects + [bpy.context.active_object]:
|
||||
if obj == relating_obj:
|
||||
continue
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
try:
|
||||
core.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=relating_obj,
|
||||
related_obj=obj,
|
||||
)
|
||||
except core.IncompatibleAggregateError:
|
||||
self.report({"ERROR"}, f"Cannot aggregate {obj.name} to {relating_obj.name}")
|
||||
except core.AggregateRepresentationError:
|
||||
self.report({"ERROR"}, f"Cannot aggregate to {relating_obj.name} with a body representation")
|
||||
result = core.assign_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
relating_obj=relating_obj,
|
||||
related_obj=obj,
|
||||
)
|
||||
if not result:
|
||||
self.report({"ERROR"}, f" Cannot aggregate {obj.name} to {relating_obj.name}")
|
||||
|
||||
|
||||
class BIM_OT_aggregate_unassign_object(bpy.types.Operator, Operator):
|
||||
@@ -275,23 +272,23 @@ class BIM_OT_add_part_to_object(bpy.types.Operator, Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
part_class: bpy.props.StringProperty(name="Class", options={"HIDDEN"})
|
||||
part_name: bpy.props.StringProperty(name="Name")
|
||||
element: bpy.props.IntProperty(options={"HIDDEN"})
|
||||
obj: bpy.props.StringProperty(options={"HIDDEN"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.part_name = "My " + self.part_class.lstrip("Ifc")
|
||||
return context.window_manager.invoke_props_dialog(self)
|
||||
|
||||
def _execute(self, context):
|
||||
obj = bpy.data.objects.get(self.obj) or context.active_object
|
||||
core.add_part_to_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
tool.Blender,
|
||||
obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.element)) if self.element else context.active_object,
|
||||
obj=obj,
|
||||
part_class=self.part_class,
|
||||
part_name=self.part_name,
|
||||
)
|
||||
tool.Spatial.load_container_manager()
|
||||
|
||||
|
||||
class BIM_OT_break_link_to_other_aggregates(bpy.types.Operator, Operator):
|
||||
|
||||
@@ -99,6 +99,19 @@ class BIM_PT_aggregate(Panel):
|
||||
op = row.operator("bim.select_parts", icon="RESTRICT_SELECT_OFF", text="")
|
||||
op.obj = context.active_object.name
|
||||
|
||||
ifc_class = AggregateData.data["ifc_class"]
|
||||
part_class = ""
|
||||
if ifc_class == "IfcBuilding":
|
||||
part_class = "IfcBuildingStorey"
|
||||
elif ifc_class == "IfcSite":
|
||||
part_class = "IfcBuilding"
|
||||
elif ifc_class == "IfcProject":
|
||||
part_class = "IfcSite"
|
||||
if part_class != "":
|
||||
op = layout.operator("bim.add_part_to_object", text="Add " + part_class.lstrip("Ifc"))
|
||||
op.part_class = part_class
|
||||
op.obj = context.active_object.name
|
||||
|
||||
|
||||
class BIM_PT_linked_aggregate(Panel):
|
||||
bl_label = "Linked Aggregates"
|
||||
|
||||
@@ -29,9 +29,13 @@ def register():
|
||||
if not bpy.app.background:
|
||||
bpy.utils.register_tool(workspace.CoveringTool, after={"bim.structural_tool"}, separator=False, group=False)
|
||||
bpy.types.Scene.BIMCoveringProperties = bpy.props.PointerProperty(type=prop.BIMCoveringProperties)
|
||||
# bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
|
||||
# bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
if not bpy.app.background:
|
||||
bpy.utils.unregister_tool(workspace.CoveringTool)
|
||||
del bpy.types.Scene.BIMCoveringProperties
|
||||
# del bpy.types.Object.BIMObjectSpatialProperties
|
||||
# del bpy.types.Scene.BIMSpatialManagerProperties
|
||||
|
||||
@@ -168,15 +168,12 @@ class BaseDecorator:
|
||||
def get_camera_width_mm(self):
|
||||
# Horrific prototype code to ensure bgl draws at drawing scales
|
||||
# https://blender.stackexchange.com/questions/16493/is-there-a-way-to-fit-the-viewport-to-the-current-field-of-view
|
||||
def is_landscape(render):
|
||||
return render.resolution_x > render.resolution_y
|
||||
|
||||
|
||||
camera = bpy.context.scene.camera
|
||||
render = bpy.context.scene.render
|
||||
if is_landscape(render):
|
||||
camera_width_model = camera.data.ortho_scale
|
||||
else:
|
||||
camera_width_model = camera.data.ortho_scale / render.resolution_y * render.resolution_x
|
||||
camera_width_model = camera.data.ortho_scale
|
||||
|
||||
|
||||
scale = tool.Drawing.get_scale_ratio(tool.Drawing.get_diagram_scale(camera)["Scale"])
|
||||
camera_width_mm = scale * camera_width_model
|
||||
@@ -415,14 +412,12 @@ class BaseDecorator:
|
||||
factor = self.camera_zoom_to_factor(context.space_data.region_3d.view_camera_zoom)
|
||||
camera_width_px = factor * context.region.width
|
||||
mm_to_px = camera_width_px / self.get_camera_width_mm()
|
||||
# magic_font_scale's default of (0.004118616) is a magic constant number I visually discovered to get the right number.
|
||||
# 0.004118616 is a magic constant number I visually discovered to get the right number.
|
||||
# In particular it works only for the OpenGOST font and produces a 2.5mm font size.
|
||||
# It probably should be dynamically calculated using system.dpi or something.
|
||||
# font_size = 16 <-- this is a good default
|
||||
# TODO: need to synchronize it better with svg
|
||||
|
||||
magic_font_scale = bpy.context.scene.DocProperties.magic_font_scale
|
||||
font_size_px = int(magic_font_scale * mm_to_px) * font_size_mm / 2.5
|
||||
font_size_px = int(0.004118616 * mm_to_px) * font_size_mm / 2.5
|
||||
pos = pos - line_no * font_size_px * rotation_matrix[1]
|
||||
|
||||
blf.size(font_id, font_size_px)
|
||||
|
||||
@@ -29,7 +29,6 @@ import subprocess
|
||||
import numpy as np
|
||||
import multiprocessing
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.ifcopenshell_wrapper
|
||||
import ifcopenshell.geom
|
||||
import ifcopenshell.util.selector
|
||||
@@ -1519,48 +1518,47 @@ class ActivateDrawing(bpy.types.Operator):
|
||||
bl_idname = "bim.activate_drawing"
|
||||
bl_label = "Activate Drawing"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_description = "Activates the selected drawing view.\n\n" + "ALT+CLICK to keep the viewport position.\n\n" + "SHIFT+CLICK activiate drawing without turning objects on/off."
|
||||
bl_description = "Activates the selected drawing view.\n\n" + "ALT+CLICK to keep the viewport position"
|
||||
|
||||
drawing: bpy.props.IntProperty()
|
||||
camera_view_point: bpy.props.BoolProperty(name="Camera View Point", default=True, options={"SKIP_SAVE"})
|
||||
switch_camera_only: bpy.props.BoolProperty(name="Only Changes Camera View", default=False, options={"SKIP_SAVE"})
|
||||
|
||||
def invoke(self, context, event):
|
||||
# keep the viewport position on alt+click
|
||||
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
|
||||
if event.type == "LEFTMOUSE" and event.alt:
|
||||
self.camera_view_point = False
|
||||
# Only activates the camera view on alt+shift. Does not turn on/off objects in scene
|
||||
if event.type == "LEFTMOUSE" and event.shift:
|
||||
self.switch_camera_only = True
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
drawing = tool.Ifc.get().by_id(self.drawing)
|
||||
dprops = bpy.context.scene.DocProperties
|
||||
camera = tool.Drawing.import_drawing(drawing)
|
||||
|
||||
if self.switch_camera_only:
|
||||
tool.Blender.activate_camera(camera)
|
||||
else:
|
||||
if not self.camera_view_point:
|
||||
viewport_position = tool.Blender.get_viewport_position()
|
||||
if not self.camera_view_point:
|
||||
viewport_position = tool.Blender.get_viewport_position()
|
||||
|
||||
try:
|
||||
core.activate_drawing_view(tool.Ifc, tool.Blender, tool.Drawing, drawing=drawing)
|
||||
except core.CameraNotAvailableError:
|
||||
self.report(
|
||||
{"ERROR"},
|
||||
"The drawing view is not available. Ensure you have not excluded it in the active view layer.",
|
||||
)
|
||||
return {"CANCELLED"}
|
||||
|
||||
if not self.camera_view_point:
|
||||
tool.Blender.set_viewport_position(viewport_position)
|
||||
|
||||
if not self.camera_view_point:
|
||||
tool.Blender.set_viewport_position(viewport_position)
|
||||
|
||||
dprops.active_drawing_id = self.drawing
|
||||
# reset DrawingsData to reload_drawing_styles work correctly
|
||||
DrawingsData.is_loaded = False
|
||||
dprops.drawing_styles.clear()
|
||||
if ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing", "HasUnderlay"):
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
CutDecorator.install(context)
|
||||
tool.Drawing.show_decorations()
|
||||
dprops.active_drawing_id = self.drawing
|
||||
# reset DrawingsData to reload_drawing_styles work correctly
|
||||
DrawingsData.is_loaded = False
|
||||
dprops.drawing_styles.clear()
|
||||
if ifcopenshell.util.element.get_pset(drawing, "EPset_Drawing", "HasUnderlay"):
|
||||
bpy.ops.bim.reload_drawing_styles()
|
||||
bpy.ops.bim.activate_drawing_style()
|
||||
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||
CutDecorator.install(context)
|
||||
tool.Drawing.show_decorations()
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1640,7 +1638,7 @@ class ReloadDrawingStyles(bpy.types.Operator):
|
||||
if not DrawingsData.is_loaded:
|
||||
DrawingsData.load()
|
||||
drawing_pset_data = DrawingsData.data["active_drawing_pset_data"]
|
||||
camera_props = context.scene.camera.data.BIMCameraProperties
|
||||
camera_props = context.active_object.data.BIMCameraProperties
|
||||
|
||||
# added this part as a temporary fallback
|
||||
# TODO: should remove it a bit later when projects get more accommodated
|
||||
|
||||
@@ -370,7 +370,6 @@ class DocProperties(PropertyGroup):
|
||||
)
|
||||
shadingstyle_default: StringProperty(default="Blender Default", name="Default Shading Style")
|
||||
drawing_font: StringProperty(default="OpenGost Type B TT.ttf", name="Drawing Font")
|
||||
magic_font_scale: bpy.props.FloatProperty(default=0.004118616, name="Font Scale Factor")
|
||||
|
||||
|
||||
class BIMCameraProperties(PropertyGroup):
|
||||
|
||||
@@ -596,7 +596,6 @@ class EditMaterialSetItemProfile(bpy.types.Operator, tool.Ifc.Operator):
|
||||
attributes = blenderbim.bim.helper.export_attributes(self.props.material_set_item_profile_attributes)
|
||||
profile = tool.Ifc.get().by_id(self.material_set_item).Profile
|
||||
ifcopenshell.api.run("profile.edit_profile", tool.Ifc.get(), profile=profile, attributes=attributes)
|
||||
self.props.active_material_set_item_id = 0
|
||||
self.props.material_set_item_profile_attributes.clear()
|
||||
model_profile.DumbProfileRegenerator().regenerate_from_profile_def(profile)
|
||||
|
||||
@@ -703,7 +702,7 @@ class EditMaterialSetItem(bpy.types.Operator, tool.Ifc.Operator):
|
||||
class ExpandMaterialCategory(bpy.types.Operator):
|
||||
bl_idname = "bim.expand_material_category"
|
||||
bl_label = "Expand Material Category"
|
||||
bl_description = "SHIFT+CLICK to expand all material categories"
|
||||
bl_description = "Expand material category.\n\nSHIFT+CLICK to expand all material categories"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
category: bpy.props.StringProperty()
|
||||
expand_all: bpy.props.BoolProperty(name="Expand All", default=False, options={"SKIP_SAVE"})
|
||||
@@ -732,7 +731,7 @@ class ExpandMaterialCategory(bpy.types.Operator):
|
||||
class ContractMaterialCategory(bpy.types.Operator):
|
||||
bl_idname = "bim.contract_material_category"
|
||||
bl_label = "Contract Material Category"
|
||||
bl_description = "SHIFT+CLICK to contract all material categories"
|
||||
bl_description = "Contract material category.\n\nSHIFT+CLICK to contract all material categories"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
category: bpy.props.StringProperty()
|
||||
contract_all: bpy.props.BoolProperty(name="Contract All", default=False, options={"SKIP_SAVE"})
|
||||
|
||||
@@ -110,7 +110,7 @@ class BIM_PT_materials(Panel):
|
||||
elif self.props.editing_material_type == "STYLE":
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "contexts", text="")
|
||||
prop_with_search(row, self.props, "styles", text="")
|
||||
row.prop(self.props, "styles", text="")
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.edit_material_style", text="Assign Style", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_material", text="", icon="CANCEL")
|
||||
|
||||
@@ -121,16 +121,12 @@ class UpdateIfcPatchArguments(bpy.types.Operator):
|
||||
for arg_name in inputs:
|
||||
arg_info = inputs[arg_name]
|
||||
new_attr = patch_args.add()
|
||||
data_type = arg_info.get("type", "str")
|
||||
if isinstance(data_type, list):
|
||||
data_type = [dt for dt in data_type if dt != "NoneType"][0]
|
||||
new_attr.data_type = {
|
||||
"Literal": "string",
|
||||
"str": "string",
|
||||
"float": "float",
|
||||
"int": "integer",
|
||||
"bool": "boolean",
|
||||
}[data_type]
|
||||
}[arg_info.get("type", "str")]
|
||||
new_attr.name = arg_name
|
||||
new_attr.set_value(arg_info.get("default", new_attr.get_value_default()))
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -36,7 +36,6 @@ import blenderbim.tool as tool
|
||||
import blenderbim.core.project as core
|
||||
import blenderbim.core.context
|
||||
import blenderbim.core.owner
|
||||
import blenderbim.core.spatial
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.ui import IFCFileSelector
|
||||
from blenderbim.bim import import_ifc
|
||||
@@ -123,7 +122,6 @@ class CreateProject(bpy.types.Operator):
|
||||
for mat in bpy.data.materials:
|
||||
bpy.data.materials.remove(mat)
|
||||
core.create_project(tool.Ifc, tool.Project, schema=props.export_schema, template=template)
|
||||
blenderbim.core.spatial.import_spatial_decomposition(tool.Spatial)
|
||||
tool.Blender.register_toolbar()
|
||||
|
||||
def rollback(self, data):
|
||||
|
||||
@@ -23,7 +23,6 @@ classes = (
|
||||
operator.CalculateCircleRadius,
|
||||
operator.CalculateEdgeLengths,
|
||||
operator.CalculateFaceAreas,
|
||||
operator.CalculateFormworkArea,
|
||||
operator.CalculateObjectVolumes,
|
||||
operator.CalculateSingleQuantity,
|
||||
operator.PerformQuantityTakeOff,
|
||||
|
||||
@@ -84,21 +84,6 @@ class CalculateObjectVolumes(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CalculateFormworkArea(bpy.types.Operator):
|
||||
bl_idname = "bim.calculate_formwork_area"
|
||||
bl_label = "Calculate Formwork Area"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.selected_objects and context.active_object
|
||||
|
||||
def execute(self, context):
|
||||
result = helper.calculate_formwork_area([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class CalculateSingleQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.calculate_single_quantity"
|
||||
bl_label = "Calculate Single Quantity"
|
||||
|
||||
@@ -87,16 +87,14 @@ class BIM_PT_qto_simple(bpy.types.Panel):
|
||||
row = layout.row()
|
||||
row.prop(props, "qto_result", text="Results")
|
||||
|
||||
row = layout.row()
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_circle_radius")
|
||||
row = layout.row()
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_edge_lengths")
|
||||
row = layout.row()
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_face_areas")
|
||||
row = layout.row()
|
||||
row = layout.row(align=True)
|
||||
row.operator("bim.calculate_object_volumes")
|
||||
row = layout.row()
|
||||
row.operator("bim.calculate_formwork_area")
|
||||
|
||||
|
||||
class BIM_PT_qto_cost(bpy.types.Panel):
|
||||
|
||||
@@ -41,7 +41,20 @@ quantitytypes_enum = {}
|
||||
|
||||
|
||||
def setup_quantity_types_enum():
|
||||
resources = ifcopenshell.util.resource.RESOURCES_TO_QUANTITIES
|
||||
# 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]
|
||||
|
||||
|
||||
@@ -22,32 +22,31 @@ from . import ui, prop, operator, workspace
|
||||
classes = (
|
||||
operator.AssignContainer,
|
||||
operator.ChangeSpatialLevel,
|
||||
operator.ContractContainer,
|
||||
operator.CopyToContainer,
|
||||
operator.DeleteContainer,
|
||||
operator.DereferenceStructure,
|
||||
operator.DisableEditingContainer,
|
||||
operator.EditContainerAttributes,
|
||||
operator.EnableEditingContainer,
|
||||
operator.ExpandContainer,
|
||||
operator.ImportSpatialDecomposition,
|
||||
operator.ReferenceStructure,
|
||||
operator.RemoveContainer,
|
||||
operator.SelectContainer,
|
||||
operator.SelectDecomposedElements,
|
||||
operator.SelectProduct,
|
||||
operator.SelectSimilarContainer,
|
||||
operator.SelectProduct,
|
||||
operator.LoadContainerManager,
|
||||
operator.EditContainerAttributes,
|
||||
operator.AddBuildingStorey,
|
||||
operator.ContractContainer,
|
||||
operator.ExpandContainer,
|
||||
operator.DeleteContainer,
|
||||
operator.SelectDecomposedElements,
|
||||
prop.SpatialElement,
|
||||
prop.Element,
|
||||
prop.BIMSpatialProperties,
|
||||
prop.BIMObjectSpatialProperties,
|
||||
prop.BIMContainer,
|
||||
prop.BIMSpatialDecompositionProperties,
|
||||
prop.BIMSpatialManagerProperties,
|
||||
ui.BIM_PT_spatial,
|
||||
ui.BIM_UL_containers,
|
||||
ui.BIM_UL_containers_manager,
|
||||
ui.BIM_UL_elements,
|
||||
ui.BIM_PT_spatial_decomposition,
|
||||
ui.BIM_PT_SpatialManager,
|
||||
workspace.Hotkey,
|
||||
)
|
||||
|
||||
@@ -57,7 +56,7 @@ def register():
|
||||
bpy.utils.register_tool(workspace.SpatialTool, after={"bim.annotation_tool"}, separator=False, group=False)
|
||||
bpy.types.Scene.BIMSpatialProperties = bpy.props.PointerProperty(type=prop.BIMSpatialProperties)
|
||||
bpy.types.Object.BIMObjectSpatialProperties = bpy.props.PointerProperty(type=prop.BIMObjectSpatialProperties)
|
||||
bpy.types.Scene.BIMSpatialDecompositionProperties = bpy.props.PointerProperty(type=prop.BIMSpatialDecompositionProperties)
|
||||
bpy.types.Scene.BIMSpatialManagerProperties = bpy.props.PointerProperty(type=prop.BIMSpatialManagerProperties)
|
||||
|
||||
|
||||
def unregister():
|
||||
@@ -65,4 +64,4 @@ def unregister():
|
||||
bpy.utils.unregister_tool(workspace.SpatialTool)
|
||||
del bpy.types.Scene.BIMSpatialProperties
|
||||
del bpy.types.Object.BIMObjectSpatialProperties
|
||||
del bpy.types.Scene.BIMSpatialDecompositionProperties
|
||||
del bpy.types.Scene.BIMSpatialManagerProperties
|
||||
|
||||
@@ -23,7 +23,6 @@ import ifcopenshell.util.element
|
||||
|
||||
def refresh():
|
||||
SpatialData.is_loaded = False
|
||||
SpatialDecompositionData.is_loaded = False
|
||||
|
||||
|
||||
class SpatialData:
|
||||
@@ -32,28 +31,28 @@ class SpatialData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"parent_container_id": cls.parent_container_id(),
|
||||
"is_directly_contained": cls.is_directly_contained(),
|
||||
"label": cls.label(),
|
||||
"references": cls.references(),
|
||||
"containers": cls.containers(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
cls.data["poll"] = cls.poll()
|
||||
if cls.data["poll"]:
|
||||
cls.data.update(
|
||||
{
|
||||
"parent_container_id": cls.parent_container_id(),
|
||||
"is_directly_contained": cls.is_directly_contained(),
|
||||
"label": cls.label(),
|
||||
"references": cls.references(),
|
||||
}
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls):
|
||||
if not bpy.context.active_object:
|
||||
return False
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if not element:
|
||||
return False
|
||||
if element.is_a("IfcElement") or element.is_a("IfcAnnotation") or element.is_a("IfcGrid"):
|
||||
return True
|
||||
return False
|
||||
def containers(cls):
|
||||
results = {}
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialStructureElement")
|
||||
else:
|
||||
spatial_elements = tool.Ifc.get().by_type("IfcSpatialElement")
|
||||
for container in spatial_elements:
|
||||
results[container.id()] = {
|
||||
"type": container.is_a(),
|
||||
"id": container.id(),
|
||||
}
|
||||
return results
|
||||
|
||||
@classmethod
|
||||
def parent_container_id(cls):
|
||||
@@ -82,81 +81,3 @@ class SpatialData:
|
||||
@classmethod
|
||||
def is_directly_contained(cls):
|
||||
return bool(getattr(tool.Ifc.get_entity(bpy.context.active_object), "ContainedInStructure", False))
|
||||
|
||||
|
||||
class SpatialDecompositionData:
|
||||
data = {}
|
||||
is_loaded = False
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.is_loaded = True
|
||||
cls.data = {
|
||||
"subelement_class": cls.subelement_class(),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def subelement_class(cls):
|
||||
results = []
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
if not (container := props.active_container):
|
||||
return results
|
||||
container_class = tool.Ifc.get().by_id(container.ifc_definition_id).is_a()
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
results = {
|
||||
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
|
||||
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
|
||||
"IfcProject": ["IfcBuilding", "IfcSite", "IfcSpace"],
|
||||
"IfcSite": ["IfcBuilding", "IfcSite", "IfcSpace"],
|
||||
"IfcSpace": ["IfcSpace"],
|
||||
}[container_class]
|
||||
elif tool.Ifc.get_schema() == "IFC4":
|
||||
results = {
|
||||
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
|
||||
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
|
||||
"IfcExternalSpatialElement": ["IfcExternalSpatialElement"],
|
||||
"IfcProject": ["IfcBuilding", "IfcExternalSpatialElement", "IfcSite", "IfcSpace"],
|
||||
"IfcSite": ["IfcBuilding", "IfcExternalSpatialElement", "IfcSite", "IfcSpace"],
|
||||
"IfcSpace": ["IfcSpace"],
|
||||
}[container_class]
|
||||
elif tool.Ifc.get_schema() == "IFC4X3":
|
||||
results = {
|
||||
"IfcBridge": ["IfcBridge", "IfcBridgePart", "IfcSpace"],
|
||||
"IfcBridgePart": ["IfcSpace"],
|
||||
"IfcBuilding": ["IfcBuilding", "IfcBuildingStorey", "IfcSpace"],
|
||||
"IfcBuildingStorey": ["IfcBuildingStorey", "IfcSpace"],
|
||||
"IfcExternalSpatialElement": ["IfcExternalSpatialElement"],
|
||||
"IfcFacility": ["IfcFacility", "IfcFacilityPartCommon", "IfcSpace"],
|
||||
"IfcFacilityPartCommon": ["IfcSpace"],
|
||||
"IfcMarineFacility": ["IfcMarineFacility", "IfcMarinePart", "IfcSpace"],
|
||||
"IfcMarinePart": ["IfcSpace"],
|
||||
"IfcProject": [
|
||||
"IfcBridge",
|
||||
"IfcBuilding",
|
||||
"IfcExternalSpatialElement",
|
||||
"IfcFacility",
|
||||
"IfcMarineFacility",
|
||||
"IfcRailway",
|
||||
"IfcRoad",
|
||||
"IfcSite",
|
||||
"IfcSpace",
|
||||
],
|
||||
"IfcRailway": ["IfcRailway", "IfcRailwayPart", "IfcSpace"],
|
||||
"IfcRailwayPart": ["IfcSpace"],
|
||||
"IfcRoad": ["IfcRoad", "IfcRoadPart", "IfcSpace"],
|
||||
"IfcRoadPart": ["IfcSpace"],
|
||||
"IfcSite": [
|
||||
"IfcBridge",
|
||||
"IfcBuilding",
|
||||
"IfcExternalSpatialElement",
|
||||
"IfcFacility",
|
||||
"IfcMarineFacility",
|
||||
"IfcRailway",
|
||||
"IfcRoad",
|
||||
"IfcSite",
|
||||
"IfcSpace",
|
||||
],
|
||||
"IfcSpace": ["IfcSpace"],
|
||||
}[container_class]
|
||||
|
||||
return [(r, r, "") for r in results]
|
||||
|
||||
@@ -175,13 +175,13 @@ class SelectProduct(bpy.types.Operator):
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class ImportSpatialDecomposition(bpy.types.Operator):
|
||||
bl_idname = "bim.import_spatial_decomposition"
|
||||
class LoadContainerManager(bpy.types.Operator):
|
||||
bl_idname = "bim.load_container_manager"
|
||||
bl_label = "Load Container Manager"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
core.import_spatial_decomposition(tool.Spatial)
|
||||
core.load_container_manager(tool.Spatial)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -227,6 +227,26 @@ class DeleteContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
core.delete_container(tool.Ifc, tool.Spatial, tool.Geometry, container=tool.Ifc.get().by_id(self.container))
|
||||
|
||||
class AddBuildingStorey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.add_building_storey"
|
||||
bl_label = "Add Storey"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
part_class: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
active_container = tool.Spatial.get_active_container()
|
||||
obj = tool.Ifc.get_object(active_container)
|
||||
blenderbim.core.aggregate.add_part_to_object(
|
||||
tool.Ifc,
|
||||
tool.Aggregate,
|
||||
tool.Collector,
|
||||
tool.Blender,
|
||||
obj=obj,
|
||||
part_class=self.part_class,
|
||||
part_name="Unnamed",
|
||||
)
|
||||
core.load_container_manager(tool.Spatial)
|
||||
|
||||
|
||||
class SelectDecomposedElements(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_decomposed_elements"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import bpy
|
||||
from blenderbim.bim.prop import StrProperty, Attribute
|
||||
from blenderbim.bim.module.spatial.data import SpatialDecompositionData
|
||||
from blenderbim.bim.module.spatial.data import SpatialData
|
||||
from bpy.types import PropertyGroup
|
||||
from bpy.props import (
|
||||
PointerProperty,
|
||||
@@ -31,32 +31,32 @@ from bpy.props import (
|
||||
CollectionProperty,
|
||||
)
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.geometry
|
||||
import ifcopenshell
|
||||
|
||||
|
||||
def get_subelement_class(self, context):
|
||||
if not SpatialDecompositionData.is_loaded:
|
||||
SpatialDecompositionData.load()
|
||||
return SpatialDecompositionData.data["subelement_class"]
|
||||
|
||||
|
||||
def update_elevation(self, context):
|
||||
if ifc_definition_id := self.ifc_definition_id:
|
||||
entity = tool.Ifc.get().by_id(ifc_definition_id)
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
if not obj:
|
||||
return
|
||||
obj.location.z = self.elevation
|
||||
|
||||
|
||||
def update_name(self, context):
|
||||
if ifc_definition_id := self.ifc_definition_id:
|
||||
tool.Spatial.edit_container_name(tool.Ifc.get().by_id(ifc_definition_id), self.name)
|
||||
entity = tool.Ifc.get().by_id(self.active_container_id)
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
if not obj:
|
||||
return
|
||||
obj.location.z = self.elevation
|
||||
|
||||
|
||||
def update_active_container_index(self, context):
|
||||
SpatialDecompositionData.data["subelement_class"] = SpatialDecompositionData.subelement_class()
|
||||
tool.Spatial.load_contained_elements()
|
||||
if self.active_container_index < 0:
|
||||
return
|
||||
self.active_container_id = self.containers[self.active_container_index].ifc_definition_id
|
||||
self.container_name = self.containers[self.active_container_index].name
|
||||
self.elevation = self.containers[self.active_container_index].elevation
|
||||
|
||||
|
||||
def updateContainerName(self, context):
|
||||
props = context.scene.BIMSpatialManagerProperties
|
||||
if not props.is_container_update_enabled or self.name == "Unnamed":
|
||||
return
|
||||
tool.Spatial.edit_container_name(tool.Ifc.get().by_id(props.active_container_id), self.name)
|
||||
props.container_name = self.name
|
||||
|
||||
|
||||
def update_relating_container_from_object(self, context):
|
||||
@@ -104,35 +104,20 @@ class BIMObjectSpatialProperties(PropertyGroup):
|
||||
|
||||
|
||||
class BIMContainer(PropertyGroup):
|
||||
name: StringProperty(name="Name", update=update_name)
|
||||
ifc_class: StringProperty(name="IFC Class")
|
||||
description: StringProperty(name="Description")
|
||||
long_name: StringProperty(name="Long Name")
|
||||
elevation: FloatProperty(name="Elevation", subtype="DISTANCE", update=update_elevation)
|
||||
name: StringProperty(name="Name", update=updateContainerName)
|
||||
elevation: FloatProperty(name="Elevation", subtype="DISTANCE")
|
||||
level_index: IntProperty(name="Level Index")
|
||||
has_children: BoolProperty(name="Has Children")
|
||||
is_expanded: BoolProperty(name="Is Expanded")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
|
||||
class Element(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
is_class: BoolProperty(name="Is Class", default=False)
|
||||
is_type: BoolProperty(name="Is Type", default=False)
|
||||
total: IntProperty(name="Total")
|
||||
|
||||
|
||||
class BIMSpatialDecompositionProperties(PropertyGroup):
|
||||
class BIMSpatialManagerProperties(PropertyGroup):
|
||||
containers: CollectionProperty(name="Containers", type=BIMContainer)
|
||||
contracted_containers: StringProperty(name="Contracted containers", default="[]")
|
||||
expanded_containers: StringProperty(name="Expanded containers", default="[]")
|
||||
active_container_index: IntProperty(name="Active Container Index", update=update_active_container_index)
|
||||
elements: CollectionProperty(name="Elements", type=Element)
|
||||
active_element_index: IntProperty(name="Active Element Index")
|
||||
total_elements: IntProperty(name="Total Elements")
|
||||
subelement_class: bpy.props.EnumProperty(items=get_subelement_class, name="Subelement Class")
|
||||
|
||||
@property
|
||||
def active_container(self):
|
||||
if self.containers and self.active_container_index < len(self.containers):
|
||||
return self.containers[self.active_container_index]
|
||||
active_container_id: IntProperty(name="Active Container Id")
|
||||
container_name: StringProperty(name="Container Name")
|
||||
elevation: FloatProperty(name="Elevation", update=update_elevation, subtype="DISTANCE")
|
||||
is_container_update_enabled: BoolProperty(name="Is Container Update Enabled", default=True) # TODO:review
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from bpy.types import Panel, UIList
|
||||
from blenderbim.bim.module.spatial.data import SpatialData, SpatialDecompositionData
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.spatial.data import SpatialData
|
||||
import blenderbim.tool as tool
|
||||
|
||||
|
||||
@@ -31,9 +32,14 @@ class BIM_PT_spatial(Panel):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not SpatialData.is_loaded:
|
||||
SpatialData.load()
|
||||
return SpatialData.data["poll"]
|
||||
if not context.active_object:
|
||||
return False
|
||||
oprops = context.active_object.BIMObjectProperties
|
||||
if not oprops.ifc_definition_id:
|
||||
return False
|
||||
if not IfcStore.get_element(oprops.ifc_definition_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
def draw(self, context):
|
||||
if not SpatialData.is_loaded:
|
||||
@@ -91,49 +97,34 @@ class BIM_UL_containers(UIList):
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_spatial_decomposition(Panel):
|
||||
bl_label = "Spatial Decomposition"
|
||||
bl_idname = "BIM_PT_spatial_decomposition"
|
||||
class BIM_PT_SpatialManager(Panel):
|
||||
bl_label = "Spatial Manager"
|
||||
bl_idname = "BIM_PT_SpatialManager"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_parent_id = "BIM_PT_tab_spatial_decomposition"
|
||||
bl_options = {"HIDE_HEADER"}
|
||||
bl_options = {"DEFAULT_CLOSED"}
|
||||
bl_parent_id = "BIM_PT_tab_project_setup"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Ifc.get()
|
||||
return tool.Ifc.get() and tool.Ifc.schema().name() != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
if not SpatialDecompositionData.is_loaded:
|
||||
SpatialDecompositionData.load()
|
||||
self.props = context.scene.BIMSpatialDecompositionProperties
|
||||
|
||||
if self.props.active_container:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(
|
||||
text=f"Active: {self.props.active_container.name}",
|
||||
icon="OUTLINER_COLLECTION",
|
||||
)
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
|
||||
if self.props.active_container.ifc_class != "IfcProject":
|
||||
row = self.layout.row(align=True)
|
||||
row.operator("bim.select_decomposed_elements", icon="HIDE_OFF", text=f"Isolate {self.props.active_container.ifc_class}")
|
||||
row.operator("bim.delete_container", icon="X", text="").container = (
|
||||
self.props.active_container.ifc_definition_id
|
||||
)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "subelement_class", text="")
|
||||
op = row.operator("bim.add_part_to_object", icon="ADD", text="")
|
||||
op.element = self.props.active_container.ifc_definition_id
|
||||
op.part_class = self.props.subelement_class
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="Warning: No Active Container", icon="ERROR")
|
||||
row.operator("bim.import_spatial_decomposition", icon="FILE_REFRESH", text="")
|
||||
|
||||
if not SpatialData.is_loaded:
|
||||
SpatialData.load()
|
||||
self.props = context.scene.BIMSpatialManagerProperties
|
||||
row = self.layout.row()
|
||||
row.operator("bim.load_container_manager", icon="FILE_REFRESH", text="Load Spatial Structure")
|
||||
if 0 <= self.props.active_container_index < len(self.props.containers):
|
||||
ifc_definition_id = self.props.containers[self.props.active_container_index].ifc_definition_id
|
||||
row = self.layout.row()
|
||||
row.alignment = "RIGHT"
|
||||
row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="Select Children")
|
||||
spatial_data = SpatialData.data["containers"].get(ifc_definition_id, None)
|
||||
if spatial_data and spatial_data["type"] in ["IfcBuildingStorey", "IfcBuilding"]:
|
||||
row.operator("bim.add_building_storey", icon="ADD", text="Add storey").part_class = "IfcBuildingStorey"
|
||||
row.operator("bim.delete_container", icon="X", text="Delete").container = ifc_definition_id
|
||||
self.layout.template_list(
|
||||
"BIM_UL_containers_manager",
|
||||
"",
|
||||
@@ -141,29 +132,13 @@ class BIM_PT_spatial_decomposition(Panel):
|
||||
"containers",
|
||||
self.props,
|
||||
"active_container_index",
|
||||
rows=10,
|
||||
)
|
||||
|
||||
if not self.props.active_container:
|
||||
return
|
||||
|
||||
if not self.props.total_elements:
|
||||
row = self.layout.row()
|
||||
row.label(text="No Contained Elements", icon="FILE_3D")
|
||||
return
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{self.props.total_elements} Contained Elements", icon="FILE_3D")
|
||||
row.operator("bim.select_decomposed_elements", icon="RESTRICT_SELECT_OFF", text="")
|
||||
|
||||
self.layout.template_list(
|
||||
"BIM_UL_elements",
|
||||
"",
|
||||
self.props,
|
||||
"elements",
|
||||
self.props,
|
||||
"active_element_index",
|
||||
)
|
||||
row = self.layout.row()
|
||||
if 0 <= self.props.active_container_index < len(self.props.containers):
|
||||
row.prop(self.props, "container_name", text="")
|
||||
row.prop(self.props, "elevation", text="")
|
||||
op = row.operator("bim.edit_container_attributes", icon="CHECKMARK", text="Apply")
|
||||
op.container = self.props.containers[self.props.active_container_index].ifc_definition_id
|
||||
|
||||
|
||||
class BIM_UL_containers_manager(UIList):
|
||||
@@ -171,57 +146,22 @@ class BIM_UL_containers_manager(UIList):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
self.draw_hierarchy(row, item)
|
||||
row.prop(item, "name", emboss=False, text="")
|
||||
if item.long_name:
|
||||
row.prop(item, "long_name", emboss=False, text="")
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.prop(item, "elevation", emboss=False, text="")
|
||||
split1 = row.split(factor=0.7)
|
||||
split1.prop(item, "name", emboss=False, text="")
|
||||
split2 = row.split(factor=1)
|
||||
split2.label(icon="BLANK1", text=tool.Unit.blender_format_unit(item.elevation))
|
||||
|
||||
def draw_hierarchy(self, row, item):
|
||||
for i in range(0, item.level_index):
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.has_children:
|
||||
if item.is_expanded:
|
||||
row.operator("bim.contract_container", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN").container = (
|
||||
item.ifc_definition_id
|
||||
)
|
||||
row.operator(
|
||||
"bim.contract_container", text="", emboss=False, icon="DISCLOSURE_TRI_DOWN"
|
||||
).container = item.ifc_definition_id
|
||||
else:
|
||||
row.operator("bim.expand_container", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT").container = (
|
||||
item.ifc_definition_id
|
||||
)
|
||||
row.operator(
|
||||
"bim.expand_container", text="", emboss=False, icon="DISCLOSURE_TRI_RIGHT"
|
||||
).container = item.ifc_definition_id
|
||||
else:
|
||||
row.label(text="", icon="BLANK1")
|
||||
if item.ifc_class == "IfcProject":
|
||||
row.label(text="", icon="FILE")
|
||||
elif item.ifc_class == "IfcSite":
|
||||
row.label(text="", icon="WORLD")
|
||||
elif item.ifc_class == "IfcBuilding":
|
||||
row.label(text="", icon="HOME")
|
||||
elif item.ifc_class == "IfcBuildingStorey":
|
||||
row.label(text="", icon="LINENUMBERS_OFF")
|
||||
elif item.ifc_class == "IfcSpace":
|
||||
row.label(text="", icon="ANTIALIASED")
|
||||
elif "Part" in item.ifc_class:
|
||||
row.label(text="", icon="MOD_FLUID")
|
||||
else:
|
||||
row.label(text="", icon="META_PLANE")
|
||||
|
||||
|
||||
class BIM_UL_elements(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
if item.is_class:
|
||||
row.label(text="", icon="DISCLOSURE_TRI_DOWN")
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
elif item.is_type:
|
||||
row.label(text="", icon="BLANK1")
|
||||
row.label(text="", icon="DISCLOSURE_TRI_DOWN")
|
||||
row.label(text=item.name)
|
||||
col = row.column()
|
||||
col.alignment = "RIGHT"
|
||||
col.label(text=str(item.total))
|
||||
row.label(text="", icon="DOT")
|
||||
|
||||
@@ -64,10 +64,16 @@ def add_layout_hotkey(layout, text, hotkey, description):
|
||||
tool.Blender.add_layout_hotkey_operator(*args)
|
||||
|
||||
|
||||
# NOTES before adding new operators:
|
||||
# - add scene.BIMSpatialProperties
|
||||
# - add SpatialData
|
||||
|
||||
|
||||
class SpatialToolUI:
|
||||
@classmethod
|
||||
def draw(cls, context, layout):
|
||||
cls.layout = layout
|
||||
# cls.props = context.scene.BIMSpatialProperties
|
||||
cls.model_props = context.scene.BIMModelProperties
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
@@ -145,10 +151,12 @@ class Hotkey(bpy.types.Operator, Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
# self.props = context.scene.BIMSpatialProperties
|
||||
getattr(self, f"hotkey_{self.hotkey}")()
|
||||
|
||||
def invoke(self, context, event):
|
||||
# https://blender.stackexchange.com/questions/276035/how-do-i-make-operators-remember-their-property-values-when-called-from-a-hotkey
|
||||
# self.props = context.scene.BIMSpatialProperties
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
@@ -24,7 +24,6 @@ classes = (
|
||||
operator.AddPresentationStyle,
|
||||
operator.AddStyle,
|
||||
operator.AddSurfaceTexture,
|
||||
operator.AssignStyleToSelected,
|
||||
operator.BrowseExternalStyle,
|
||||
operator.RemoveTextureMap,
|
||||
operator.ChooseTextureMapPath,
|
||||
|
||||
@@ -23,7 +23,6 @@ import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.core.style as core
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.style
|
||||
import ifcopenshell.util.representation
|
||||
from blenderbim.bim.module.style.prop import switch_shading
|
||||
from pathlib import Path
|
||||
@@ -162,6 +161,13 @@ class UpdateCurrentStyle(bpy.types.Operator):
|
||||
update_all: bpy.props.BoolProperty(name="Update All", default=False, options={"SKIP_SAVE"})
|
||||
style_id: bpy.props.IntProperty(default=0, options={"SKIP_SAVE"})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.selected_objects:
|
||||
cls.poll_message_set("No objects selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def invoke(self, context, event):
|
||||
# updating all styles on shift+click
|
||||
# make sure to use SKIP_SAVE on property, otherwise it might get stuck
|
||||
@@ -952,49 +958,3 @@ class SaveUVToStyle(bpy.types.Operator, tool.Ifc.Operator):
|
||||
self.report({"INFO"}, f"UV saved to the style {style.Name}")
|
||||
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class AssignStyleToSelected(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_style_to_selected"
|
||||
bl_label = "Assign Style To Selected"
|
||||
bl_description = "Assign style to the selected objects' active representations"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
style_id: bpy.props.IntProperty(name="Style ID")
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
if not context.selected_objects:
|
||||
cls.poll_message_set("No objects selected")
|
||||
return False
|
||||
return True
|
||||
|
||||
def _execute(self, context):
|
||||
if self.style_id == 0:
|
||||
self.report({"ERROR"}, "No style provided")
|
||||
return {"CANCELLED"}
|
||||
ifc_file = tool.Ifc.get()
|
||||
style = ifc_file.by_id(self.style_id)
|
||||
|
||||
representations: dict[ifcopenshell.entity_instance, bpy.types.Object] = {}
|
||||
for obj in context.selected_objects:
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
representations.setdefault(representation, obj)
|
||||
|
||||
if not representations:
|
||||
self.report({"INFO"}, "No IFC objects with representations selected.")
|
||||
return {"FINISHED"}
|
||||
|
||||
for representation in representations:
|
||||
ifcopenshell.api.style.assign_representation_styles(
|
||||
ifc_file,
|
||||
shape_representation=representation,
|
||||
styles=[style],
|
||||
should_use_presentation_style_assignment=tool.Geometry.should_use_presentation_style_assignment(),
|
||||
)
|
||||
|
||||
tool.Geometry.reload_representation(representations.values())
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -65,8 +65,6 @@ class BIM_PT_styles(Panel):
|
||||
|
||||
row.operator("bim.duplicate_style", text="", icon="DUPLICATE").style = style.ifc_definition_id
|
||||
row.operator("bim.select_by_style", text="", icon="RESTRICT_SELECT_OFF").style = style.ifc_definition_id
|
||||
op = row.operator("bim.assign_style_to_selected", text="", icon="BRUSH_DATA")
|
||||
op.style_id = style.ifc_definition_id
|
||||
op = row.operator("bim.unlink_style", text="", icon="UNLINKED")
|
||||
op.style = style.ifc_definition_id
|
||||
op = row.operator("bim.enable_editing_style", text="", icon="GREASEPENCIL")
|
||||
|
||||
@@ -328,7 +328,6 @@ def get_tab(self, context):
|
||||
("SCHEDULING", "Costing and Scheduling", "", "NLA", 6),
|
||||
("FM", "Facility Management", "", "PACKAGE", 7),
|
||||
("QUALITY", "Quality and Coordination", "", "COMMUNITY", 8),
|
||||
None,
|
||||
("BLENDER", "Blender Properties", "", "BLENDER", 9),
|
||||
]
|
||||
|
||||
|
||||
@@ -202,7 +202,9 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
default=True,
|
||||
description="If disabled, the toolbar will only load when an IFC model is active",
|
||||
)
|
||||
should_play_chaching_sound: BoolProperty(name="Play A Cha-Ching Sound When Project Costs Updates", default=False)
|
||||
should_play_chaching_sound: BoolProperty(
|
||||
name="Play A Cha-Ching Sound When Project Costs Updates", default=False
|
||||
)
|
||||
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(
|
||||
@@ -341,7 +343,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
|
||||
row.prop(context.scene.DocProperties, "shadingstyle_default")
|
||||
row = self.layout.row()
|
||||
row.prop(context.scene.DocProperties, "drawing_font")
|
||||
row.prop(context.scene.DocProperties, "magic_font_scale")
|
||||
|
||||
|
||||
# Scene panel groups
|
||||
@@ -362,7 +363,6 @@ class BIM_PT_tabs(Panel):
|
||||
aprops = context.screen.BIMTabProperties
|
||||
|
||||
row = self.layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.operator(
|
||||
"bim.set_tab",
|
||||
text="",
|
||||
@@ -378,11 +378,11 @@ class BIM_PT_tabs(Panel):
|
||||
self.draw_tab_entry(row, "NLA", "SCHEDULING", is_ifc_project, aprops.tab == "SCHEDULING")
|
||||
self.draw_tab_entry(row, "PACKAGE", "FM", True, aprops.tab == "FM")
|
||||
self.draw_tab_entry(row, "COMMUNITY", "QUALITY", True, aprops.tab == "QUALITY")
|
||||
self.draw_tab_entry(row, "BLENDER", "BLENDER", True, aprops.tab == "BLENDER")
|
||||
row.operator("bim.switch_tab", text="", emboss=False, icon="UV_SYNC_SELECT")
|
||||
|
||||
# Yes, that's right.
|
||||
row = self.layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.scale_y = 0.2
|
||||
for tab in [
|
||||
"PROJECT",
|
||||
@@ -394,6 +394,7 @@ class BIM_PT_tabs(Panel):
|
||||
"SCHEDULING",
|
||||
"FM",
|
||||
"QUALITY",
|
||||
"BLENDER",
|
||||
"SWITCH",
|
||||
]:
|
||||
if aprops.tab == tab:
|
||||
@@ -406,7 +407,7 @@ class BIM_PT_tabs(Panel):
|
||||
|
||||
if blenderbim.last_error:
|
||||
box = self.layout.box()
|
||||
box.alert = True
|
||||
box.alert=True
|
||||
row = box.row(align=True)
|
||||
row.label(text="BlenderBIM experienced an error :(", icon="ERROR")
|
||||
row.operator("bim.close_error", text="", icon="CANCEL")
|
||||
@@ -441,20 +442,6 @@ class BIM_PT_tab_project_info(Panel):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_spatial_decomposition(Panel):
|
||||
bl_label = "Spatial Decomposition"
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return tool.Blender.is_tab(context, "PROJECT") and tool.Ifc.get()
|
||||
|
||||
def draw(self, context):
|
||||
pass
|
||||
|
||||
|
||||
class BIM_PT_tab_project_setup(Panel):
|
||||
bl_label = "Project Setup"
|
||||
bl_space_type = "PROPERTIES"
|
||||
@@ -503,7 +490,7 @@ class BIM_PT_tab_grouping_and_filtering(Panel):
|
||||
bl_space_type = "PROPERTIES"
|
||||
bl_region_type = "WINDOW"
|
||||
bl_context = "scene"
|
||||
bl_options = {"DEFAULT_CLOSED", "HEADER_LAYOUT_EXPAND"}
|
||||
bl_options = {"DEFAULT_CLOSED","HEADER_LAYOUT_EXPAND"}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -516,9 +503,8 @@ class BIM_PT_tab_grouping_and_filtering(Panel):
|
||||
# Draws help button on the right
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text="") # empty text occupies the left of the row
|
||||
row.operator("bim.open_uri", text="", icon="HELP").uri = (
|
||||
row.operator("bim.open_uri", text="", icon="HELP").uri = \
|
||||
"https://docs.ifcopenshell.org/ifcopenshell-python/selector_syntax.html"
|
||||
)
|
||||
|
||||
|
||||
class BIM_PT_tab_geometry(Panel):
|
||||
|
||||
@@ -41,12 +41,9 @@ def assign_object(
|
||||
related_obj: Optional[bpy.types.Object] = None,
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
if not aggregator.can_aggregate(relating_obj, related_obj):
|
||||
raise IncompatibleAggregateError
|
||||
relating_object = ifc.get_entity(relating_obj)
|
||||
if aggregator.has_physical_body_representation(relating_object):
|
||||
raise AggregateRepresentationError
|
||||
return
|
||||
rel = ifc.run(
|
||||
"aggregate.assign_object", products=[ifc.get_entity(related_obj)], relating_object=relating_object
|
||||
"aggregate.assign_object", products=[ifc.get_entity(related_obj)], relating_object=ifc.get_entity(relating_obj)
|
||||
)
|
||||
collector.assign(relating_obj)
|
||||
collector.assign(related_obj)
|
||||
@@ -87,11 +84,3 @@ def add_part_to_object(
|
||||
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)
|
||||
|
||||
|
||||
class IncompatibleAggregateError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class AggregateRepresentationError(Exception):
|
||||
pass
|
||||
|
||||
@@ -484,4 +484,12 @@ def activate_drawing_view(
|
||||
drawing_tool.import_annotations_in_group(drawing_tool.get_drawing_group(drawing))
|
||||
blender.activate_camera(camera)
|
||||
drawing_tool.isolate_camera_collection(camera)
|
||||
try:
|
||||
blender.set_active_object(camera)
|
||||
except:
|
||||
raise CameraNotAvailableError()
|
||||
drawing_tool.activate_drawing(camera)
|
||||
|
||||
|
||||
class CameraNotAvailableError(Exception):
|
||||
pass
|
||||
|
||||
@@ -120,28 +120,28 @@ def select_product(spatial, product):
|
||||
spatial.select_products([product])
|
||||
|
||||
|
||||
def import_spatial_decomposition(spatial):
|
||||
spatial.import_spatial_decomposition()
|
||||
def load_container_manager(spatial):
|
||||
spatial.load_container_manager()
|
||||
|
||||
|
||||
def edit_container_attributes(spatial, entity=None):
|
||||
spatial.edit_container_attributes(entity)
|
||||
spatial.import_spatial_decomposition()
|
||||
spatial.load_container_manager()
|
||||
|
||||
|
||||
def contract_container(spatial, container=None):
|
||||
spatial.contract_container(container)
|
||||
spatial.import_spatial_decomposition()
|
||||
spatial.load_container_manager()
|
||||
|
||||
|
||||
def expand_container(spatial, container=None):
|
||||
spatial.expand_container(container)
|
||||
spatial.import_spatial_decomposition()
|
||||
spatial.load_container_manager()
|
||||
|
||||
|
||||
def delete_container(ifc, spatial, geometry, container=None):
|
||||
geometry.delete_ifc_object(ifc.get_object(container))
|
||||
spatial.import_spatial_decomposition()
|
||||
spatial.load_container_manager()
|
||||
|
||||
|
||||
def select_decomposed_elements(spatial):
|
||||
|
||||
@@ -831,7 +831,7 @@ class Spatial:
|
||||
def can_reference(cls, structure, element): pass
|
||||
def contract_container(cls, container): pass
|
||||
def copy_xy(cls, src_obj, destination_obj): pass
|
||||
def import_spatial_structure(cls, element, level_index): pass
|
||||
def create_new_storey_li(cls, element, level_index): pass
|
||||
def deselect_objects(cls): pass
|
||||
def disable_editing(cls, obj): pass
|
||||
def duplicate_object_and_data(cls, obj): pass
|
||||
@@ -848,7 +848,7 @@ class Spatial:
|
||||
def get_selected_product_types(cls): pass
|
||||
def get_selected_products(cls): pass
|
||||
def import_containers(cls, parent=None): pass
|
||||
def import_spatial_decomposition(cls): pass
|
||||
def load_container_manager(cls): pass
|
||||
def run_root_copy_class(cls, obj=None): pass
|
||||
def run_spatial_assign_container(cls, structure_obj=None, element_obj=None): pass
|
||||
def select_object(cls, obj): pass
|
||||
|
||||
@@ -30,9 +30,9 @@ class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
related_object = tool.Ifc.get_entity(related_obj)
|
||||
if not relating_object or not related_object:
|
||||
return False
|
||||
if (relating_object.is_a("IfcElement") or relating_object.is_a("IfcElementType")) and related_object.is_a(
|
||||
"IfcElement"
|
||||
):
|
||||
if (relating_object.is_a("IfcElement") or relating_object.is_a("IfcElementType")) and related_object.is_a("IfcElement"):
|
||||
if relating_obj.data: # See #3973
|
||||
return False
|
||||
return True
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
if relating_object.is_a("IfcSpatialStructureElement") and related_object.is_a("IfcSpatialStructureElement"):
|
||||
@@ -46,13 +46,6 @@ class Aggregate(blenderbim.core.tool.Aggregate):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def has_physical_body_representation(cls, element: ifcopenshell.entity_instance) -> bool:
|
||||
if element.is_a("IfcElement") or element.is_a("IfcElementType"): # See 3973
|
||||
if ifcopenshell.util.representation.get_representation(element, "Model", "Body"):
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj: bpy.types.Object) -> None:
|
||||
obj.BIMObjectAggregateProperties.is_editing = False
|
||||
|
||||
@@ -95,12 +95,9 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
# NOTE: won't allow assigning IfcElements to the IfcProject directly
|
||||
# and some elements might get missing in other viewers if they're don't support displaying
|
||||
# elements without hierarchy
|
||||
try:
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=parent_obj, related_obj=obj
|
||||
)
|
||||
except blenderbim.core.aggregate.IncompatibleAggregateError:
|
||||
pass
|
||||
blenderbim.core.aggregate.assign_object(
|
||||
tool.Ifc, tool.Aggregate, tool.Collector, relating_obj=parent_obj, related_obj=obj
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def assign(cls, obj: bpy.types.Object) -> None:
|
||||
|
||||
@@ -1752,8 +1752,6 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
|
||||
@classmethod
|
||||
def activate_drawing(cls, camera: bpy.types.Object) -> None:
|
||||
selected_objects_before = bpy.context.selected_objects
|
||||
|
||||
# Sync viewport objects visibility with selectors from EPset_Drawing/Include and /Exclude
|
||||
drawing = tool.Ifc.get_entity(camera)
|
||||
|
||||
@@ -1841,10 +1839,6 @@ class Drawing(blenderbim.core.tool.Drawing):
|
||||
|
||||
cls.import_camera_props(drawing, camera)
|
||||
|
||||
for obj in selected_objects_before:
|
||||
obj.hide_set(False)
|
||||
obj.select_set(True)
|
||||
|
||||
@classmethod
|
||||
def get_elements_in_camera_view(
|
||||
cls, camera: bpy.types.Object, objs: list[ifcopenshell.entity_instance]
|
||||
|
||||
@@ -725,11 +725,11 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def should_use_presentation_style_assignment(cls) -> bool:
|
||||
def should_use_presentation_style_assignment(cls):
|
||||
return bpy.context.scene.BIMGeometryProperties.should_use_presentation_style_assignment
|
||||
|
||||
@classmethod
|
||||
def get_model_representations(cls) -> list[ifcopenshell.entity_instance]:
|
||||
def get_model_representations(cls):
|
||||
return tool.Ifc.get().by_type("IfcShapeRepresentation")
|
||||
|
||||
@classmethod
|
||||
@@ -761,25 +761,16 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
Ensures that same representations won't be reloaded multiple times.
|
||||
"""
|
||||
objs = obj_or_objs if isinstance(obj_or_objs, Iterable) else [obj_or_objs]
|
||||
ifc_file = tool.Ifc.get()
|
||||
|
||||
# Find all objects that use the same representation
|
||||
# as there are possibility that some of them have openings
|
||||
# (each representation with opening has a unique Mesh)
|
||||
# and therefore reloading Mesh of it's type or occurrence
|
||||
# might not be enough.
|
||||
elements = set()
|
||||
for obj in objs:
|
||||
representation = tool.Geometry.get_active_representation(obj)
|
||||
if not representation:
|
||||
continue
|
||||
representation = tool.Geometry.resolve_mapped_representation(representation)
|
||||
elements.update(ifcopenshell.util.element.get_elements_by_representation(ifc_file, representation))
|
||||
|
||||
# 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 = {(obj:=tool.Ifc.get_object(element)).data: obj for element in elements}
|
||||
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)
|
||||
|
||||
@@ -207,73 +207,45 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
src_obj.location = (destination_obj.location[0], destination_obj.location[1], z)
|
||||
|
||||
@classmethod
|
||||
def load_contained_elements(cls):
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
props.elements.clear()
|
||||
if not (container := props.active_container):
|
||||
return
|
||||
def load_container_manager(cls):
|
||||
cls.props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
previous_container_index = cls.props.active_container_index
|
||||
cls.props.containers.clear()
|
||||
cls.contracted_containers = json.loads(cls.props.contracted_containers)
|
||||
cls.props.is_container_update_enabled = False
|
||||
parent = tool.Ifc.get().by_type("IfcProject")[0]
|
||||
|
||||
container = tool.Ifc.get().by_id(container.ifc_definition_id)
|
||||
|
||||
results = {}
|
||||
for element in ifcopenshell.util.element.get_contained(container):
|
||||
element_type = ifcopenshell.util.element.get_type(element)
|
||||
ifc_class = element.is_a()
|
||||
type_name = element_type.Name or "Unnamed" if element_type else "Untyped"
|
||||
results.setdefault(ifc_class, {}).setdefault(type_name, 0)
|
||||
results[ifc_class][type_name] += 1
|
||||
|
||||
total_elements = 0
|
||||
for ifc_class in sorted(results.keys()):
|
||||
new = props.elements.add()
|
||||
new.name = ifc_class
|
||||
new.is_class = True
|
||||
total = 0
|
||||
for type_name in sorted(results[ifc_class].keys()):
|
||||
new2 = props.elements.add()
|
||||
new2.name = type_name
|
||||
new2.is_type = True
|
||||
new2.total = results[ifc_class][type_name]
|
||||
total += new2.total
|
||||
new.total = total
|
||||
total_elements += total
|
||||
|
||||
props.total_elements = total_elements
|
||||
for object in ifcopenshell.util.element.get_parts(parent) or []:
|
||||
if object.is_a("IfcSpatialElement") or object.is_a("IfcSpatialStructureElement"):
|
||||
cls.create_new_storey_li(object, 0)
|
||||
cls.props.is_container_update_enabled = True
|
||||
# triggers spatial manager props setup
|
||||
cls.props.active_container_index = min(previous_container_index, len(cls.props.containers) - 1)
|
||||
|
||||
@classmethod
|
||||
def import_spatial_decomposition(cls):
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
previous_container_index = props.active_container_index
|
||||
props.containers.clear()
|
||||
cls.contracted_containers = json.loads(props.contracted_containers)
|
||||
cls.import_spatial_element(tool.Ifc.get().by_type("IfcProject")[0], 0)
|
||||
props.active_container_index = min(previous_container_index, len(props.containers) - 1)
|
||||
|
||||
@classmethod
|
||||
def import_spatial_element(cls, element, level_index):
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
new = props.containers.add()
|
||||
new.ifc_class = element.is_a()
|
||||
def create_new_storey_li(cls, element, level_index):
|
||||
si_conversion = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
||||
new = cls.props.containers.add()
|
||||
new.name = element.Name or "Unnamed"
|
||||
new.description = element.Description or ""
|
||||
new.long_name = element.LongName or ""
|
||||
if not element.is_a("IfcProject"):
|
||||
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element)
|
||||
new.has_decomposition = bool(element.IsDecomposedBy)
|
||||
new.ifc_definition_id = element.id()
|
||||
new.elevation = ifcopenshell.util.placement.get_storey_elevation(element) * si_conversion
|
||||
|
||||
new.is_expanded = element.id() not in cls.contracted_containers
|
||||
new.level_index = level_index
|
||||
children = ifcopenshell.util.element.get_parts(element)
|
||||
new.has_children = bool(children)
|
||||
new.ifc_definition_id = element.id()
|
||||
if new.is_expanded:
|
||||
for child in children or []:
|
||||
cls.import_spatial_element(child, level_index + 1)
|
||||
if new.has_decomposition:
|
||||
new.has_children = True
|
||||
if new.is_expanded:
|
||||
for related_object in ifcopenshell.util.element.get_parts(element) or []:
|
||||
if related_object.is_a("IfcSpatialElement") or related_object.is_a("IfcSpatialStructureElement"):
|
||||
cls.create_new_storey_li(related_object, level_index + 1)
|
||||
|
||||
@classmethod
|
||||
def edit_container_attributes(cls, entity):
|
||||
# TODO
|
||||
obj = tool.Ifc.get_object(entity)
|
||||
blenderbim.core.geometry.edit_object_placement(tool.Ifc, tool.Geometry, tool.Surveyor, obj=obj)
|
||||
name = bpy.context.scene.BIMSpatialDecompositionProperties.container_name
|
||||
name = bpy.context.scene.BIMSpatialManagerProperties.container_name
|
||||
if name != entity.Name:
|
||||
cls.edit_container_name(entity, name)
|
||||
|
||||
@@ -283,21 +255,21 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
|
||||
@classmethod
|
||||
def get_active_container(cls):
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
if props.active_container_index < len(props.containers):
|
||||
container = tool.Ifc.get().by_id(props.containers[props.active_container_index].ifc_definition_id)
|
||||
return container
|
||||
|
||||
@classmethod
|
||||
def contract_container(cls, container):
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
contracted_containers = json.loads(props.contracted_containers)
|
||||
contracted_containers.append(container.id())
|
||||
props.contracted_containers = json.dumps(contracted_containers)
|
||||
|
||||
@classmethod
|
||||
def expand_container(cls, container):
|
||||
props = bpy.context.scene.BIMSpatialDecompositionProperties
|
||||
props = bpy.context.scene.BIMSpatialManagerProperties
|
||||
contracted_containers = json.loads(props.contracted_containers)
|
||||
contracted_containers.remove(container.id())
|
||||
props.contracted_containers = json.dumps(contracted_containers)
|
||||
@@ -322,36 +294,6 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
space_polygon = shapely.force_3d(polygon)
|
||||
return space_polygon
|
||||
|
||||
@classmethod
|
||||
def debug_shape(cls, foo):
|
||||
coords = [(p[0], p[1], 0) for p in foo.exterior.coords]
|
||||
mesh = bpy.data.meshes.new(name="NewMesh")
|
||||
bm = bmesh.new()
|
||||
for coord in coords:
|
||||
bm.verts.new(coord)
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.faces.new(bm.verts)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
obj = bpy.data.objects.new("NewObject", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@classmethod
|
||||
def debug_line(cls, start, end):
|
||||
coords = [start, end]
|
||||
mesh = bpy.data.meshes.new(name="NewMesh")
|
||||
bm = bmesh.new()
|
||||
for coord in coords:
|
||||
bm.verts.new(coord)
|
||||
bm.verts.ensure_lookup_table()
|
||||
bm.edges.new(bm.verts)
|
||||
bm.to_mesh(mesh)
|
||||
bm.free()
|
||||
obj = bpy.data.objects.new("NewLine", mesh)
|
||||
bpy.context.collection.objects.link(obj)
|
||||
bpy.context.view_layer.update()
|
||||
|
||||
@classmethod
|
||||
def get_boundary_lines_from_context_visible_objects(cls):
|
||||
calculation_rl = bpy.context.scene.BIMModelProperties.rl3
|
||||
|
||||
@@ -36,7 +36,7 @@ Some of these add-ons are not shipped with Blender:
|
||||
import GIS data, grab elevation data from the web, and generate TINs from
|
||||
survey points and contours.
|
||||
- `Ladybug Tools for Blender
|
||||
<https://github.com/ladybug-tools/ladybug-blender/releases/download/ladybug-blender-240529/ladybug-blender-240529.zip>`__ - Ladybug Tools
|
||||
<https://github.com/Andrej730/ladybug-blender/releases/download/ladybug-blender-240419/ladybug-blender-240419.zip>`__ - Ladybug Tools
|
||||
is an extension of Sverchok for environmental analysis and building physics
|
||||
simulation. It allows analysis of solar, daylight, energy, and CFD.
|
||||
- `Topologic <https://topologic.app/>`__ - Perform spatial and topological
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.root
|
||||
import ifcopenshell.api.unit
|
||||
import ifcopenshell.api.context
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.tool as tool
|
||||
from test.bim.bootstrap import NewFile
|
||||
@@ -95,24 +92,16 @@ class TestCanAggregate(NewFile):
|
||||
subelement_obj = bpy.data.objects.new("Object", None)
|
||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
||||
|
||||
|
||||
class TestHasPhysicalBodyRepresentation(NewFile):
|
||||
def test_run(self):
|
||||
def test_aggregates_with_meshes_are_invalid(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
element = ifc.createIfcElementAssembly()
|
||||
assert subject.has_physical_body_representation(element) is False
|
||||
ifcopenshell.api.root.create_entity(ifc, ifc_class="IfcProject")
|
||||
ifcopenshell.api.unit.assign_unit(ifc)
|
||||
context = ifcopenshell.api.context.add_context(ifc, context_type="Model")
|
||||
body = ifcopenshell.api.context.add_context(
|
||||
ifc, context_type="Model", context_identifier="Body", target_view="MODEL_VIEW", parent=context
|
||||
)
|
||||
builder = ifcopenshell.util.shape_builder.ShapeBuilder(ifc)
|
||||
origin = builder.create_axis2_placement_3d()
|
||||
block = ifc.createIfcCsgSolid(ifc.createIfcBlock(origin, 200, 200, 200))
|
||||
rep = builder.get_representation(context=body, items=[block])
|
||||
ifcopenshell.api.geometry.assign_representation(ifc, product=element, representation=rep)
|
||||
assert subject.has_physical_body_representation(element) is True
|
||||
element_obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||
tool.Ifc.link(element, element_obj)
|
||||
subelement = ifc.createIfcBeam()
|
||||
subelement_obj = bpy.data.objects.new("Object", None)
|
||||
tool.Ifc.link(subelement, subelement_obj)
|
||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
||||
|
||||
|
||||
class TestDisableEditing(NewFile):
|
||||
|
||||
@@ -24,8 +24,8 @@ import test.bim.bootstrap
|
||||
import blenderbim.core.tool
|
||||
import blenderbim.core.root
|
||||
import blenderbim.tool as tool
|
||||
import blenderbim.bim.module.qto.calculator as calculator
|
||||
from blenderbim.tool.qto import Qto as subject
|
||||
from blenderbim.bim.module.pset.qto_calculator import QtoCalculator
|
||||
|
||||
|
||||
class TestImplementsTool(test.bim.bootstrap.NewFile):
|
||||
@@ -45,6 +45,48 @@ class TestSetQtoResult(test.bim.bootstrap.NewFile):
|
||||
assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457"
|
||||
|
||||
|
||||
class TestGetApplicableQuantityNames(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
schema = ifc.schema
|
||||
properties_templates = (
|
||||
ifcopenshell.util.pset.PsetQto(schema)
|
||||
.get_by_name("Qto_WallBaseQuantities")
|
||||
.get_info()["HasPropertyTemplates"]
|
||||
)
|
||||
applicable_quantity_names = [a.Name for a in properties_templates]
|
||||
assert subject.get_applicable_quantity_names("Qto_WallBaseQuantities") == applicable_quantity_names
|
||||
|
||||
|
||||
class TestGetApplicableBaseQuantityName(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
assert subject.get_applicable_base_quantity_name(wall) == "Qto_WallBaseQuantities"
|
||||
|
||||
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):
|
||||
quantity = 1.2345
|
||||
@@ -55,14 +97,12 @@ class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
|
||||
def setup_file(self):
|
||||
self.ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(self.ifc)
|
||||
ifcopenshell.api.run("root.create_entity", self.ifc, ifc_class="IfcProject", name="My Project")
|
||||
project = ifcopenshell.api.run("root.create_entity", self.ifc, ifc_class="IfcProject", name="My Project")
|
||||
|
||||
def setup_units(self, units):
|
||||
ifcopenshell.api.run("unit.assign_unit", self.ifc, **units)
|
||||
|
||||
def calculate_quantities(self, obj):
|
||||
import ifc5d.qto
|
||||
|
||||
context = ifcopenshell.api.run("context.add_context", self.ifc, context_type="Model")
|
||||
bpy.ops.mesh.primitive_cube_add(location=(0.0, 0.0, 0.0), size=2)
|
||||
obj = bpy.context.active_object
|
||||
@@ -75,32 +115,12 @@ class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
|
||||
predefined_type="ELEMENTEDWALL",
|
||||
context=context,
|
||||
)
|
||||
|
||||
rules = {
|
||||
"calculators": {
|
||||
"Blender": {
|
||||
"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",
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ifc_file = tool.Ifc.get()
|
||||
results = ifc5d.qto.quantify(ifc_file, {element}, rules)
|
||||
return {k: round(v, 3) for k, v in results[element]["Qto_WallBaseQuantities"].items() if v is not None}
|
||||
calculator = QtoCalculator()
|
||||
base_qto = ifcopenshell.api.run("pset.add_qto", self.ifc, product=element, name="Qto_WallBaseQuantities")
|
||||
quantities = subject.get_calculated_object_quantities(
|
||||
calculator=calculator, qto_name="Qto_WallBaseQuantities", obj=obj
|
||||
)
|
||||
return quantities
|
||||
|
||||
def test_meters_project_unit(self):
|
||||
self.setup_file()
|
||||
@@ -166,6 +186,35 @@ class TestGetCalculatedObjectQuantities(test.bim.bootstrap.NewFile):
|
||||
assert quantities["NetVolume"] == 282.517
|
||||
|
||||
|
||||
class TestAddObjectBaseQto(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
project = ifcopenshell.api.run("root.create_entity", ifc, ifc_class="IfcProject", name="My Project")
|
||||
context = ifcopenshell.api.run("context.add_context", ifc, context_type="Model")
|
||||
bpy.ops.mesh.primitive_cube_add(location=(0.0, 0.0, 0.0), size=2)
|
||||
obj = bpy.context.active_object
|
||||
element = blenderbim.core.root.assign_class(
|
||||
tool.Ifc,
|
||||
tool.Collector,
|
||||
tool.Root,
|
||||
obj=obj,
|
||||
ifc_class="IfcWall",
|
||||
predefined_type="ELEMENTEDWALL",
|
||||
context=context,
|
||||
)
|
||||
assert subject.add_object_base_qto(obj).Name == "Qto_WallBaseQuantities"
|
||||
|
||||
|
||||
class TestAddProductBaseQto(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
wall = ifc.createIfcWall()
|
||||
base_qto = subject.add_product_base_qto(wall)
|
||||
assert base_qto.Name == "Qto_WallBaseQuantities"
|
||||
|
||||
|
||||
class TestGetBaseQto(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
@@ -187,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):
|
||||
|
||||
@@ -76,7 +76,7 @@ class SI2ProjectUnitConverter:
|
||||
"IfcLengthMeasure": "METRE",
|
||||
"IfcMassMeasure": "GRAM",
|
||||
"IfcTimeMeasure": "SECOND",
|
||||
"IfcVolumeMeasure": "CUBIC_METRE",
|
||||
"IfcVolumeMeasure": "CUBIE_METRE",
|
||||
}
|
||||
|
||||
def convert(self, value, measure):
|
||||
|
||||
@@ -335,8 +335,8 @@ multiple times.
|
||||
results = tree.select_ray(origin, direction, length=5.)
|
||||
|
||||
for result in results:
|
||||
print(ifc_file.by_id(result.instance.id())) # The element the ray intersects with
|
||||
print(list(result.position)) # The XYZ intersection point
|
||||
print(result.distance) # The distance between the ray origin and the intersection
|
||||
print(list(result.normal)) # The normal of the face being intersected
|
||||
print(result.dot_product) # The dot product of the face being intersected with the ray
|
||||
print(ifc_file.by_id(r.instance.id())) # The element the ray intersects with
|
||||
print(list(r.position)) # The XYZ intersection point
|
||||
print(r.distance) # The distance between the ray origin and the intersection
|
||||
print(list(r.normal)) # The normal of the face being intersected
|
||||
print(r.dot_product) # The dot product of the face being intersected with the ray
|
||||
|
||||
@@ -102,7 +102,6 @@ elements in your filter group based on their criteria.
|
||||
"Material", "Filter", "``material{{=}}{{value}}``", "``material=Foo`` specifies the criteria that elements must have a IfcMaterial assigned directly or indirectly (such as within a layer set). That IfcMaterial must have either a ``Name`` or ``Category`` attribute with a value of ``Foo``."
|
||||
"Classification", "Filter", "``classification{{=}}{{value}}``", "``classification=Foo`` specifies the criteria that elements must have an IfcClassificationReference with an ``Identification`` attribute with a value of ``Foo``."
|
||||
"Location", "Filter", "``location{{=}}{{value}}``", "``location=Foo`` specifies the criteria that elements must be contained directly or indirectly in a spatial element with a ``Name`` attribute with a value of ``Foo``."
|
||||
"Parent", "Filter", "``parent{{=}}{{value}}``", "``parent=Foo`` specifies the criteria that elements must be a direct or indirect child in the spatial hierarchy to an element with a ``Name`` attribute with a value of ``Foo``."
|
||||
"Query", "Filter", "``query:{{keys}}{{=}}{{value}}``", "``query:types.count=0`` specifies the criteria that elements must have zero type occurrences. The query keys corresponds to the syntax used in the `Getting element values`_ section"
|
||||
|
||||
When you specify a filter with a ``{{=}}`` check, you can choose from one of
|
||||
@@ -184,7 +183,6 @@ Valid keys are:
|
||||
"``storey``", "Gets the first IfcBuildingStorey spatial element that an element is contained in."
|
||||
"``building``", "Gets the first IfcBuilding spatial element that an element is contained in."
|
||||
"``site``", "Gets the first IccSite spatial element that an element is contained in."
|
||||
"``parent``", "Gets the parent element in the spatial hierarchy."
|
||||
"``material`` or ``mat``", "Gets the assigned material, which may be a material set."
|
||||
"``item`` or ``i``", "If the previous key returns a material set, gets the relevant material set items"
|
||||
"``materials`` or ``mats``", "Gets a list of IfcMaterials assigned directly or indirectly (such as via a material set) to the element"
|
||||
|
||||
@@ -28,8 +28,3 @@ from .assign_object import assign_object
|
||||
from .unassign_object import unassign_object
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_object",
|
||||
"unassign_object",
|
||||
]
|
||||
|
||||
@@ -27,7 +27,3 @@ from .. import wrap_usecases
|
||||
from .edit_attributes import edit_attributes
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"edit_attributes",
|
||||
]
|
||||
|
||||
@@ -30,10 +30,3 @@ from .edit_attributes import edit_attributes
|
||||
from .remove_boundary import remove_boundary
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_connection_geometry",
|
||||
"copy_boundary",
|
||||
"edit_attributes",
|
||||
"remove_boundary",
|
||||
]
|
||||
|
||||
@@ -36,12 +36,3 @@ from .remove_classification import remove_classification
|
||||
from .remove_reference import remove_reference
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_classification",
|
||||
"add_reference",
|
||||
"edit_classification",
|
||||
"edit_reference",
|
||||
"remove_classification",
|
||||
"remove_reference",
|
||||
]
|
||||
|
||||
@@ -34,15 +34,3 @@ from .remove_metric import remove_metric
|
||||
from .unassign_constraint import unassign_constraint
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_metric",
|
||||
"add_metric_reference",
|
||||
"add_objective",
|
||||
"assign_constraint",
|
||||
"edit_metric",
|
||||
"edit_objective",
|
||||
"remove_constraint",
|
||||
"remove_metric",
|
||||
"unassign_constraint",
|
||||
]
|
||||
|
||||
@@ -31,9 +31,3 @@ from .edit_context import edit_context
|
||||
from .remove_context import remove_context
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_context",
|
||||
"edit_context",
|
||||
"remove_context",
|
||||
]
|
||||
|
||||
@@ -27,8 +27,3 @@ from .assign_control import assign_control
|
||||
from .unassign_control import unassign_control
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_control",
|
||||
"unassign_control",
|
||||
]
|
||||
|
||||
@@ -46,25 +46,3 @@ from .remove_cost_value import remove_cost_value
|
||||
from .unassign_cost_item_quantity import unassign_cost_item_quantity
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_cost_item",
|
||||
"add_cost_item_quantity",
|
||||
"add_cost_schedule",
|
||||
"add_cost_value",
|
||||
"assign_cost_item_quantity",
|
||||
"assign_cost_value",
|
||||
"calculate_cost_item_resource_value",
|
||||
"copy_cost_item",
|
||||
"copy_cost_item_values",
|
||||
"edit_cost_item",
|
||||
"edit_cost_item_quantity",
|
||||
"edit_cost_schedule",
|
||||
"edit_cost_value",
|
||||
"edit_cost_value_formula",
|
||||
"remove_cost_item",
|
||||
"remove_cost_item_quantity",
|
||||
"remove_cost_schedule",
|
||||
"remove_cost_value",
|
||||
"unassign_cost_item_quantity",
|
||||
]
|
||||
|
||||
@@ -35,14 +35,3 @@ from .remove_reference import remove_reference
|
||||
from .unassign_document import unassign_document
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_information",
|
||||
"add_reference",
|
||||
"assign_document",
|
||||
"edit_information",
|
||||
"edit_reference",
|
||||
"remove_information",
|
||||
"remove_reference",
|
||||
"unassign_document",
|
||||
]
|
||||
|
||||
@@ -28,9 +28,3 @@ from .edit_text_literal import edit_text_literal
|
||||
from .unassign_product import unassign_product
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_product",
|
||||
"edit_text_literal",
|
||||
"unassign_product",
|
||||
]
|
||||
|
||||
@@ -61,28 +61,3 @@ from .remove_representation import remove_representation
|
||||
from .unassign_representation import unassign_representation
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_axis_representation",
|
||||
"add_boolean",
|
||||
"add_door_representation",
|
||||
"add_footprint_representation",
|
||||
"add_mesh_representation",
|
||||
"add_profile_representation",
|
||||
"add_railing_representation",
|
||||
"add_representation",
|
||||
"add_slab_representation",
|
||||
"add_wall_representation",
|
||||
"add_window_representation",
|
||||
"assign_representation",
|
||||
"connect_element",
|
||||
"connect_path",
|
||||
"create_2pt_wall",
|
||||
"disconnect_element",
|
||||
"disconnect_path",
|
||||
"edit_object_placement",
|
||||
"map_representation",
|
||||
"remove_boolean",
|
||||
"remove_representation",
|
||||
"unassign_representation",
|
||||
]
|
||||
|
||||
@@ -29,9 +29,3 @@ from .edit_georeferencing import edit_georeferencing
|
||||
from .remove_georeferencing import remove_georeferencing
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_georeferencing",
|
||||
"edit_georeferencing",
|
||||
"remove_georeferencing",
|
||||
]
|
||||
|
||||
@@ -30,9 +30,3 @@ from .create_grid_axis import create_grid_axis
|
||||
from .remove_grid_axis import remove_grid_axis
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"create_axis_curve",
|
||||
"create_grid_axis",
|
||||
"remove_grid_axis",
|
||||
]
|
||||
|
||||
@@ -32,12 +32,3 @@ from .unassign_group import unassign_group
|
||||
from .update_group_products import update_group_products
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_group",
|
||||
"assign_group",
|
||||
"edit_group",
|
||||
"remove_group",
|
||||
"unassign_group",
|
||||
"update_group_products",
|
||||
]
|
||||
|
||||
@@ -33,11 +33,3 @@ from .remove_layer import remove_layer
|
||||
from .unassign_layer import unassign_layer
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_layer",
|
||||
"assign_layer",
|
||||
"edit_layer",
|
||||
"remove_layer",
|
||||
"unassign_layer",
|
||||
]
|
||||
|
||||
@@ -34,14 +34,3 @@ from .remove_reference import remove_reference
|
||||
from .unassign_reference import unassign_reference
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_library",
|
||||
"add_reference",
|
||||
"assign_reference",
|
||||
"edit_library",
|
||||
"edit_reference",
|
||||
"remove_library",
|
||||
"remove_reference",
|
||||
"unassign_reference",
|
||||
]
|
||||
|
||||
@@ -58,30 +58,3 @@ from .reorder_set_item import reorder_set_item
|
||||
from .unassign_material import unassign_material
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_constituent",
|
||||
"add_layer",
|
||||
"add_list_item",
|
||||
"add_material",
|
||||
"add_material_set",
|
||||
"add_profile",
|
||||
"assign_material",
|
||||
"assign_profile",
|
||||
"copy_material",
|
||||
"edit_assigned_material",
|
||||
"edit_constituent",
|
||||
"edit_layer",
|
||||
"edit_layer_usage",
|
||||
"edit_material",
|
||||
"edit_profile",
|
||||
"edit_profile_usage",
|
||||
"remove_constituent",
|
||||
"remove_layer",
|
||||
"remove_list_item",
|
||||
"remove_material",
|
||||
"remove_material_set",
|
||||
"remove_profile",
|
||||
"reorder_set_item",
|
||||
"unassign_material",
|
||||
]
|
||||
|
||||
@@ -34,10 +34,3 @@ from .reorder_nesting import reorder_nesting
|
||||
from .unassign_object import unassign_object
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_object",
|
||||
"change_nest",
|
||||
"reorder_nesting",
|
||||
"unassign_object",
|
||||
]
|
||||
|
||||
@@ -50,29 +50,3 @@ from .unassign_actor import unassign_actor
|
||||
from .update_owner_history import update_owner_history
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_actor",
|
||||
"add_address",
|
||||
"add_application",
|
||||
"add_organisation",
|
||||
"add_person",
|
||||
"add_person_and_organisation",
|
||||
"add_role",
|
||||
"assign_actor",
|
||||
"create_owner_history",
|
||||
"edit_actor",
|
||||
"edit_address",
|
||||
"edit_organisation",
|
||||
"edit_person",
|
||||
"edit_role",
|
||||
"remove_actor",
|
||||
"remove_address",
|
||||
"remove_application",
|
||||
"remove_organisation",
|
||||
"remove_person",
|
||||
"remove_person_and_organisation",
|
||||
"remove_role",
|
||||
"unassign_actor",
|
||||
"update_owner_history",
|
||||
]
|
||||
|
||||
@@ -30,11 +30,3 @@ from .edit_profile import edit_profile
|
||||
from .remove_profile import remove_profile
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_arbitrary_profile",
|
||||
"add_arbitrary_profile_with_voids",
|
||||
"add_parameterized_profile",
|
||||
"edit_profile",
|
||||
"remove_profile",
|
||||
]
|
||||
|
||||
@@ -33,10 +33,3 @@ from .create_file import create_file
|
||||
from .unassign_declaration import unassign_declaration
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"append_asset",
|
||||
"assign_declaration",
|
||||
"create_file",
|
||||
"unassign_declaration",
|
||||
]
|
||||
|
||||
@@ -31,11 +31,3 @@ from .edit_qto import edit_qto
|
||||
from .remove_pset import remove_pset
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_pset",
|
||||
"add_qto",
|
||||
"edit_pset",
|
||||
"edit_qto",
|
||||
"remove_pset",
|
||||
]
|
||||
|
||||
@@ -33,12 +33,3 @@ from .remove_prop_template import remove_prop_template
|
||||
from .remove_pset_template import remove_pset_template
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_prop_template",
|
||||
"add_pset_template",
|
||||
"edit_prop_template",
|
||||
"edit_pset_template",
|
||||
"remove_prop_template",
|
||||
"remove_pset_template",
|
||||
]
|
||||
|
||||
@@ -38,18 +38,3 @@ from .remove_resource_quantity import remove_resource_quantity
|
||||
from .unassign_resource import unassign_resource
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_resource",
|
||||
"add_resource_quantity",
|
||||
"add_resource_time",
|
||||
"assign_resource",
|
||||
"calculate_resource_usage",
|
||||
"calculate_resource_work",
|
||||
"edit_resource",
|
||||
"edit_resource_quantity",
|
||||
"edit_resource_time",
|
||||
"remove_resource",
|
||||
"remove_resource_quantity",
|
||||
"unassign_resource",
|
||||
]
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.resource
|
||||
|
||||
|
||||
def add_resource_quantity(
|
||||
@@ -67,14 +66,6 @@ def add_resource_quantity(
|
||||
"""
|
||||
settings = {"resource": resource, "ifc_class": ifc_class}
|
||||
|
||||
resource_type = resource.is_a()
|
||||
supported_quantities = ifcopenshell.util.resource.RESOURCES_TO_QUANTITIES[resource_type]
|
||||
if ifc_class not in supported_quantities:
|
||||
raise ValueError(
|
||||
f"Resource type '{resource_type}' does not support quantity type '{ifc_class}'. "
|
||||
f"Supported quantities: {','.join(supported_quantities)}"
|
||||
)
|
||||
|
||||
quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
|
||||
# 3 IfcPhysicalSimpleQuantity Value
|
||||
if settings["ifc_class"] == "IfcQuantityCount":
|
||||
|
||||
@@ -74,9 +74,8 @@ def remove_resource(file: ifcopenshell.file, resource: ifcopenshell.entity_insta
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
# Usage was added in IFC4.
|
||||
if usage := getattr(settings["resource"], "Usage", None):
|
||||
file.remove(usage)
|
||||
if settings["resource"].Usage:
|
||||
file.remove(settings["resource"].Usage)
|
||||
if settings["resource"].BaseQuantity:
|
||||
ifcopenshell.api.run(
|
||||
"resource.remove_resource_quantity",
|
||||
|
||||
@@ -33,10 +33,3 @@ from .reassign_class import reassign_class
|
||||
from .remove_product import remove_product
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"copy_class",
|
||||
"create_entity",
|
||||
"reassign_class",
|
||||
"remove_product",
|
||||
]
|
||||
|
||||
@@ -67,44 +67,3 @@ from .unassign_recurrence_pattern import unassign_recurrence_pattern
|
||||
from .unassign_sequence import unassign_sequence
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_task",
|
||||
"add_task_time",
|
||||
"add_time_period",
|
||||
"add_work_calendar",
|
||||
"add_work_plan",
|
||||
"add_work_schedule",
|
||||
"add_work_time",
|
||||
"assign_lag_time",
|
||||
"assign_process",
|
||||
"assign_product",
|
||||
"assign_recurrence_pattern",
|
||||
"assign_sequence",
|
||||
"assign_workplan",
|
||||
"calculate_task_duration",
|
||||
"cascade_schedule",
|
||||
"create_baseline",
|
||||
"duplicate_task",
|
||||
"edit_lag_time",
|
||||
"edit_recurrence_pattern",
|
||||
"edit_sequence",
|
||||
"edit_task",
|
||||
"edit_task_time",
|
||||
"edit_work_calendar",
|
||||
"edit_work_plan",
|
||||
"edit_work_schedule",
|
||||
"edit_work_time",
|
||||
"recalculate_schedule",
|
||||
"remove_task",
|
||||
"remove_time_period",
|
||||
"remove_work_calendar",
|
||||
"remove_work_plan",
|
||||
"remove_work_schedule",
|
||||
"remove_work_time",
|
||||
"unassign_lag_time",
|
||||
"unassign_process",
|
||||
"unassign_product",
|
||||
"unassign_recurrence_pattern",
|
||||
"unassign_sequence",
|
||||
]
|
||||
|
||||
@@ -29,10 +29,3 @@ from .reference_structure import reference_structure
|
||||
from .unassign_container import unassign_container
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_container",
|
||||
"dereference_structure",
|
||||
"reference_structure",
|
||||
"unassign_container",
|
||||
]
|
||||
|
||||
@@ -46,27 +46,3 @@ from .remove_structural_load_group import remove_structural_load_group
|
||||
from .unassign_structural_analysis_model import unassign_structural_analysis_model
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_structural_activity",
|
||||
"add_structural_analysis_model",
|
||||
"add_structural_boundary_condition",
|
||||
"add_structural_load",
|
||||
"add_structural_load_case",
|
||||
"add_structural_load_group",
|
||||
"add_structural_member_connection",
|
||||
"assign_structural_analysis_model",
|
||||
"edit_structural_analysis_model",
|
||||
"edit_structural_boundary_condition",
|
||||
"edit_structural_connection_cs",
|
||||
"edit_structural_item_axis",
|
||||
"edit_structural_load",
|
||||
"edit_structural_load_case",
|
||||
"remove_structural_analysis_model",
|
||||
"remove_structural_boundary_condition",
|
||||
"remove_structural_connection_condition",
|
||||
"remove_structural_load",
|
||||
"remove_structural_load_case",
|
||||
"remove_structural_load_group",
|
||||
"unassign_structural_analysis_model",
|
||||
]
|
||||
|
||||
@@ -38,18 +38,3 @@ from .unassign_material_style import unassign_material_style
|
||||
from .unassign_representation_styles import unassign_representation_styles
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_style",
|
||||
"add_surface_style",
|
||||
"add_surface_textures",
|
||||
"assign_material_style",
|
||||
"assign_representation_styles",
|
||||
"edit_presentation_style",
|
||||
"edit_surface_style",
|
||||
"remove_style",
|
||||
"remove_styled_representation",
|
||||
"remove_surface_style",
|
||||
"unassign_material_style",
|
||||
"unassign_representation_styles",
|
||||
]
|
||||
|
||||
@@ -39,18 +39,3 @@ from .unassign_port import unassign_port
|
||||
from .unassign_system import unassign_system
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_port",
|
||||
"add_system",
|
||||
"assign_flow_control",
|
||||
"assign_port",
|
||||
"assign_system",
|
||||
"connect_port",
|
||||
"disconnect_port",
|
||||
"edit_system",
|
||||
"remove_system",
|
||||
"unassign_flow_control",
|
||||
"unassign_port",
|
||||
"unassign_system",
|
||||
]
|
||||
|
||||
@@ -30,9 +30,3 @@ from .map_type_representations import map_type_representations
|
||||
from .unassign_type import unassign_type
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"assign_type",
|
||||
"map_type_representations",
|
||||
"unassign_type",
|
||||
]
|
||||
|
||||
@@ -36,16 +36,3 @@ from .remove_unit import remove_unit
|
||||
from .unassign_unit import unassign_unit
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_context_dependent_unit",
|
||||
"add_conversion_based_unit",
|
||||
"add_monetary_unit",
|
||||
"add_si_unit",
|
||||
"assign_unit",
|
||||
"edit_derived_unit",
|
||||
"edit_monetary_unit",
|
||||
"edit_named_unit",
|
||||
"remove_unit",
|
||||
"unassign_unit",
|
||||
]
|
||||
|
||||
@@ -31,10 +31,3 @@ from .remove_filling import remove_filling
|
||||
from .remove_opening import remove_opening
|
||||
|
||||
wrap_usecases(__path__, __name__)
|
||||
|
||||
__all__ = [
|
||||
"add_filling",
|
||||
"add_opening",
|
||||
"remove_filling",
|
||||
"remove_opening",
|
||||
]
|
||||
|
||||
@@ -332,7 +332,7 @@ class entity_instance:
|
||||
self.wrapped_data.setArgumentAsNull(idx)
|
||||
except RuntimeError as e:
|
||||
if e.args == ("Attribute not set",):
|
||||
raise TypeError(
|
||||
raise ValueError(
|
||||
"attribute '%s' is not optional for entity instance of type '%s'"
|
||||
% (self.wrapped_data.get_argument_name(idx), self.wrapped_data.is_a(True))
|
||||
)
|
||||
|
||||
@@ -648,8 +648,6 @@ def get_styles(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entit
|
||||
return styles
|
||||
|
||||
|
||||
# TODO: ifc_file argument is unnecessary for some methods now
|
||||
# since we have entity_instance.file, so we can deprecate it.
|
||||
def get_elements_by_material(
|
||||
ifc_file: ifcopenshell.file, material: ifcopenshell.entity_instance
|
||||
) -> list[ifcopenshell.entity_instance]:
|
||||
@@ -1043,9 +1041,7 @@ def get_parent(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.enti
|
||||
- Voiding: the opening voids another physical element, such as a hole in a wall
|
||||
|
||||
:param element: Any physical or spatial element in the tree
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: Its parent. This must exist for any valid file, or None if we've reached the IfcProject.
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1069,9 +1065,7 @@ def get_filled_void(element: ifcopenshell.entity_instance) -> Union[ifcopenshell
|
||||
Examples include windows and doors which fill a opening inside a wall.
|
||||
|
||||
:param element: The building element, typically a window or door
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: The IfcOpeningElement that it is filling
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1090,9 +1084,7 @@ def get_voided_element(element: ifcopenshell.entity_instance) -> Union[ifcopensh
|
||||
For all valid models, this should never return None.
|
||||
|
||||
:param element: The IfcOpeningElement
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: The building element, such as a wall or slab
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1110,9 +1102,7 @@ def get_aggregate(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.e
|
||||
Retrieves the aggregate parent of an element.
|
||||
|
||||
:param element: The IFC element
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: The aggregate of the element
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1126,14 +1116,14 @@ def get_aggregate(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.e
|
||||
return decomposes[0].RelatingObject
|
||||
|
||||
|
||||
def get_nest(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance, None]:
|
||||
def get_nest(element: ifcopenshell.entity_instance) -> Union[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Retrieves the nest parent of an element.
|
||||
|
||||
:param element: The IFC element
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: The nested whole of the element
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -1169,28 +1159,6 @@ def get_parts(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity
|
||||
if (is_decomposed_by := getattr(element, "IsDecomposedBy", None)) is not None and is_decomposed_by:
|
||||
if is_decomposed_by[0].is_a("IfcRelAggregates"):
|
||||
return is_decomposed_by[0].RelatedObjects
|
||||
return []
|
||||
|
||||
|
||||
def get_contained(element: ifcopenshell.entity_instance) -> list[ifcopenshell.entity_instance]:
|
||||
"""
|
||||
Retrieves the contained elements of spatial element.
|
||||
|
||||
:param element: The IFC element
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: The parts of the element
|
||||
:rtype: list[ifcopenshell.entity_instance]
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
element = file.by_type("IfcBuildingStorey")[0]
|
||||
elements = ifcopenshell.util.element.get_contained(element)
|
||||
"""
|
||||
if (rel := getattr(element, "ContainsElements", None)) is not None and rel:
|
||||
return rel[0].RelatedElements
|
||||
return []
|
||||
|
||||
|
||||
def get_components(element: ifcopenshell.entity_instance, include_ports=False) -> list[ifcopenshell.entity_instance]:
|
||||
@@ -1220,7 +1188,6 @@ def get_components(element: ifcopenshell.entity_instance, include_ports=False) -
|
||||
elif (is_decomposed_by := getattr(element, "IsDecomposedBy", None)) is not None and is_decomposed_by:
|
||||
if is_decomposed_by[0].is_a("IfcRelNests"):
|
||||
return is_decomposed_by[0].RelatedObjects
|
||||
return []
|
||||
|
||||
|
||||
ReferenceData = namedtuple("ReferenceData", "inverse_attribute, rel_class, relating_element_attribute")
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.placement
|
||||
from typing import Optional, Union, TypedDict
|
||||
@@ -99,12 +98,12 @@ def resolve_representation(representation: ifcopenshell.entity_instance) -> ifco
|
||||
|
||||
|
||||
class ResolvedItemDict(TypedDict):
|
||||
matrix: npt.NDArray[np.float64]
|
||||
matrix: np.array
|
||||
item: ifcopenshell.entity_instance
|
||||
|
||||
|
||||
def resolve_items(
|
||||
representation: ifcopenshell.entity_instance, matrix: Optional[npt.NDArray[np.float64]] = None
|
||||
representation: ifcopenshell.entity_instance, matrix: Optional[np.array] = None
|
||||
) -> list[ResolvedItemDict]:
|
||||
if matrix is None:
|
||||
matrix = np.eye(4)
|
||||
|
||||
@@ -23,20 +23,6 @@ from typing import Union, Any
|
||||
|
||||
|
||||
PRODUCTIVITY_PSET_DATA = Union[dict[str, Any], None]
|
||||
# https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/lexical/IfcConstructionResource.htm#Table-7.3.3.7.1.3.H
|
||||
RESOURCES_TO_QUANTITIES: dict[str, tuple[str, ...]] = {
|
||||
"IfcCrewResource": ("IfcQuantityTime",),
|
||||
"IfcLaborResource": ("IfcQuantityTime",),
|
||||
"IfcSubContractResource": ("IfcQuantityTime",),
|
||||
"IfcConstructionEquipmentResource": ("IfcQuantityTime",),
|
||||
"IfcConstructionMaterialResource": (
|
||||
"IfcQuantityVolume",
|
||||
"IfcQuantityArea",
|
||||
"IfcQuantityLength",
|
||||
"IfcQuantityWeight",
|
||||
),
|
||||
"IfcConstructionProductResource": ("IfcQuantityCount",),
|
||||
}
|
||||
|
||||
|
||||
def get_productivity(resource: ifcopenshell.entity_instance, should_inherit: bool = True) -> PRODUCTIVITY_PSET_DATA:
|
||||
|
||||
@@ -687,45 +687,21 @@ class FacetTransformer(lark.Transformer):
|
||||
def parent(self, args):
|
||||
comparison, value = args
|
||||
|
||||
parents = set()
|
||||
for rel in self.file.by_type("IfcRelAggregates"):
|
||||
parent = rel.RelatingObject
|
||||
if parent and self.compare(parent.Name, comparison, value):
|
||||
parents.add(parent)
|
||||
def filter_function(element):
|
||||
parents = []
|
||||
result = False
|
||||
if parent := ifcopenshell.util.element.get_parent(element):
|
||||
parents.append(parent)
|
||||
while parents:
|
||||
parent = parents.pop()
|
||||
if self.compare(parent.Name, comparison, value):
|
||||
result = True
|
||||
break
|
||||
if grandparent := ifcopenshell.util.element.get_parent(parent):
|
||||
parents.append(grandparent)
|
||||
return result if comparison == "=" else not result
|
||||
|
||||
for rel in self.file.by_type("IfcRelContainedInSpatialStructure"):
|
||||
parent = rel.RelatingStructure
|
||||
if parent and self.compare(parent.Name, comparison, value):
|
||||
parents.add(parent)
|
||||
|
||||
for rel in self.file.by_type("IfcRelNests"):
|
||||
parent = rel.RelatingObject
|
||||
if parent and self.compare(parent.Name, comparison, value):
|
||||
parents.add(parent)
|
||||
|
||||
for rel in self.file.by_type("IfcRelVoidsElement"):
|
||||
parent = rel.RelatingBuildingElement
|
||||
if parent and self.compare(parent.Name, comparison, value):
|
||||
parents.add(parent)
|
||||
|
||||
for rel in self.file.by_type("IfcRelVoidsElement"):
|
||||
parent = rel.RelatingBuildingElement
|
||||
if parent and self.compare(parent.Name, comparison, value):
|
||||
parents.add(parent)
|
||||
|
||||
for rel in self.file.by_type("IfcRelFillsElement"):
|
||||
parent = rel.RelatingOpeningElement
|
||||
if parent and self.compare(parent.Name, comparison, value):
|
||||
parents.add(parent)
|
||||
|
||||
children = set()
|
||||
for parent in parents:
|
||||
children |= set(ifcopenshell.util.element.get_decomposition(parent))
|
||||
|
||||
if comparison == "=":
|
||||
self.elements = self.elements & children
|
||||
else:
|
||||
self.elements -= children
|
||||
self.elements = set(filter(filter_function, self.elements))
|
||||
|
||||
def query(self, args):
|
||||
keys, comparison, value = args
|
||||
|
||||
@@ -16,45 +16,27 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import test.bootstrap
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.resource
|
||||
import ifcopenshell.util.resource
|
||||
|
||||
|
||||
class TestAddResourceQuantity(test.bootstrap.IFC4):
|
||||
def test_run(self):
|
||||
schema = ifcopenshell.schema_by_name(self.file.schema)
|
||||
quantity_types = [t.name() for t in schema.declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()]
|
||||
resource_types = [t.name() for t in schema.declaration_by_name("IfcConstructionResource").subtypes()]
|
||||
|
||||
self.file.create_entity("IfcProject") # add_resource
|
||||
resource = ifcopenshell.api.run("resource.add_resource", self.file, ifc_class="IfcCrewResource")
|
||||
|
||||
for resource_type in resource_types:
|
||||
resource = ifcopenshell.api.resource.add_resource(self.file, ifc_class=resource_type)
|
||||
available_quantities = ifcopenshell.util.resource.RESOURCES_TO_QUANTITIES[resource_type]
|
||||
|
||||
for quantity_type in quantity_types:
|
||||
if quantity_type not in available_quantities:
|
||||
with pytest.raises(ValueError):
|
||||
quantity = ifcopenshell.api.resource.add_resource_quantity(
|
||||
self.file, resource=resource, ifc_class=quantity_type
|
||||
)
|
||||
continue
|
||||
else:
|
||||
quantity = ifcopenshell.api.resource.add_resource_quantity(
|
||||
self.file, resource=resource, ifc_class=quantity_type
|
||||
)
|
||||
|
||||
assert quantity.is_a(quantity_type)
|
||||
assert quantity.Name == "Unnamed"
|
||||
assert quantity[3] == 0.0
|
||||
# previous quantity is reassigned and removed
|
||||
assert resource.BaseQuantity == quantity
|
||||
assert len(self.file.by_type("IfcPhysicalSimpleQuantity")) == 1
|
||||
|
||||
ifcopenshell.api.resource.remove_resource(self.file, resource)
|
||||
for quantity_type in quantity_types:
|
||||
quantity = ifcopenshell.api.run(
|
||||
"resource.add_resource_quantity", self.file, resource=resource, ifc_class=quantity_type
|
||||
)
|
||||
assert quantity.is_a(quantity_type)
|
||||
assert quantity.Name == "Unnamed"
|
||||
assert quantity[3] == 0.0
|
||||
# previous quantity is reassigned and removed
|
||||
assert resource.BaseQuantity == quantity
|
||||
assert len(self.file.by_type("IfcPhysicalSimpleQuantity")) == 1
|
||||
|
||||
|
||||
class TestAddResourceQuantityIFC2X3(test.bootstrap.IFC2X3, TestAddResourceQuantity):
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2024 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell 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.
|
||||
#
|
||||
# IfcOpenShell 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 IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# remove_resource tests is partially covered by test_add_resource_quantity.
|
||||
@@ -28,7 +28,7 @@ from typing import Union
|
||||
|
||||
def deprecation_check(test):
|
||||
def new_test(self):
|
||||
assert datetime.now().date() < datetime(2024, 8, 1).date(), "API arguments are completely deprecated"
|
||||
assert datetime.now().date() < datetime(2024, 6, 1).date(), "API arguments are completely deprecated"
|
||||
test(self)
|
||||
|
||||
return new_test
|
||||
|
||||
@@ -16,22 +16,13 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import test.bootstrap
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.unit as subject
|
||||
from math import pi
|
||||
|
||||
|
||||
class TestConvert(test.bootstrap.IFC4):
|
||||
def test_run(self):
|
||||
assert subject.convert(1, None, "METRE", None, "METRE") == 1
|
||||
assert subject.convert(1, None, "METRE", "MILLI", "METRE") == 1000
|
||||
assert subject.convert(1000, "MILLI", "METRE", None, "METRE") == 1
|
||||
assert subject.convert(1, None, "SQUARE_METRE", None, "SQUARE_METRE") == 1
|
||||
assert subject.convert(1, None, "SQUARE_METRE", "MILLI", "SQUARE_METRE") == 1000000
|
||||
assert subject.convert(1, None, "CUBIC_METRE", "MILLI", "CUBIC_METRE") == 1000000000
|
||||
|
||||
|
||||
class TestCalculateUnitScale(test.bootstrap.IFC4):
|
||||
def test_prefix_and_conversion_based_units_are_considered(self):
|
||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcProject")
|
||||
|
||||
Reference in New Issue
Block a user