mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-13 02:47:48 +00:00
typing
This commit is contained in:
@@ -380,7 +380,7 @@ class BIM_PT_object_qtos(Panel):
|
||||
filter_keyword=context.scene.GlobalPsetProperties.qto_filter,
|
||||
)
|
||||
layout = self.layout
|
||||
qtoprops = context.scene.BIMQtoProperties
|
||||
qtoprops = tool.Qto.get_qto_props()
|
||||
row = layout.row(align=True)
|
||||
row.prop(qtoprops, "qto_rule", text="")
|
||||
# A bit confusing as we typically use this icon for is_null.
|
||||
|
||||
@@ -76,6 +76,7 @@ def get_length(o: bpy.types.Object, vg_index: Optional[int] = None) -> float:
|
||||
return max(y, z)
|
||||
|
||||
length = 0
|
||||
assert isinstance(o.data, bpy.types.Mesh)
|
||||
edges = [
|
||||
e
|
||||
for e in o.data.edges
|
||||
@@ -269,6 +270,7 @@ def get_net_perimeter(o: bpy.types.Object) -> float:
|
||||
|
||||
def get_gross_perimeter(o: bpy.types.Object) -> float:
|
||||
element = tool.Ifc.get_entity(o)
|
||||
assert element
|
||||
mesh = get_gross_element_mesh(element)
|
||||
gross_obj = bpy.data.objects.new("GrossObj", mesh)
|
||||
gross_perimeter = get_net_perimeter(gross_obj)
|
||||
@@ -420,6 +422,7 @@ def get_gross_footprint_area(o: bpy.types.Object) -> float:
|
||||
return get_net_footprint_area(o)
|
||||
|
||||
element = tool.Ifc.get_entity(o)
|
||||
assert element
|
||||
mesh = get_gross_element_mesh(element)
|
||||
gross_obj = bpy.data.objects.new("GrossObj", mesh)
|
||||
gross_footprint_area = get_net_footprint_area(gross_obj)
|
||||
@@ -474,6 +477,7 @@ def get_gross_surface_area(o: bpy.types.Object, vg_index: Optional[int] = None)
|
||||
return get_net_surface_area(o)
|
||||
|
||||
element = tool.Ifc.get_entity(o)
|
||||
assert element
|
||||
mesh = get_gross_element_mesh(element)
|
||||
area = get_mesh_area(mesh)
|
||||
bpy.data.meshes.remove(mesh)
|
||||
@@ -506,6 +510,7 @@ def is_polygon_in_vg(polygon: bpy.types.MeshPolygon, vertices_in_vg: list[bpy.ty
|
||||
|
||||
|
||||
def get_net_volume(o: bpy.types.Object) -> float:
|
||||
assert isinstance(o.data, bpy.types.Mesh)
|
||||
o_mesh = bmesh.new()
|
||||
o_mesh.from_mesh(o.data)
|
||||
volume = o_mesh.calc_volume()
|
||||
@@ -518,6 +523,7 @@ def get_gross_volume(o: bpy.types.Object) -> float:
|
||||
return get_net_volume(o)
|
||||
|
||||
element = tool.Ifc.get_entity(o)
|
||||
assert element
|
||||
mesh = get_gross_element_mesh(element)
|
||||
bm = get_bmesh_from_mesh(mesh)
|
||||
|
||||
@@ -562,6 +568,7 @@ def get_net_weight(obj: bpy.types.Object) -> Union[float, None]:
|
||||
|
||||
def get_obj_mass_density(obj: bpy.types.Object) -> Union[float, None]:
|
||||
entity = tool.Ifc.get_entity(obj)
|
||||
assert entity
|
||||
material = ifcopenshell.util.element.get_material(entity)
|
||||
if material is None:
|
||||
return
|
||||
@@ -764,6 +771,7 @@ def get_outer_surface_area(obj: bpy.types.Object) -> float:
|
||||
|
||||
def get_end_area(obj: bpy.types.Object) -> float:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
assert element
|
||||
gross_mesh = get_gross_element_mesh(element)
|
||||
gross_obj = bpy.data.objects.new("MyObject", gross_mesh)
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ def calculate_mesh_quantity(
|
||||
result = 0
|
||||
edit_mode = context.active_object.mode == "EDIT"
|
||||
for obj in objs:
|
||||
assert isinstance(obj.data, bpy.types.Mesh)
|
||||
if edit_mode:
|
||||
bm = bmesh.from_edit_mesh(obj.data)
|
||||
result += operation(bm)
|
||||
|
||||
@@ -49,7 +49,7 @@ class CalculateEdgeLengths(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
result = helper.calculate_edges_lengths([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
tool.Qto.set_qto_result(result)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ class CalculateFaceAreas(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
result = helper.calculate_faces_areas([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
tool.Qto.set_qto_result(result)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class CalculateObjectVolumes(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
result = helper.calculate_volumes([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
tool.Qto.set_qto_result(result)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ class CalculateFormworkArea(bpy.types.Operator):
|
||||
|
||||
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))
|
||||
tool.Qto.set_qto_result(result)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ class CalculateSideFormworkArea(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
result = helper.calculate_side_formwork_area([o for o in context.selected_objects if o.type == "MESH"], context)
|
||||
context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
tool.Qto.set_qto_result(result)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ class CalculateSingleQuantity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
import ifc5d.qto
|
||||
|
||||
props = context.scene.BIMQtoProperties
|
||||
props = tool.Qto.get_qto_props()
|
||||
elements = set()
|
||||
for obj in tool.Blender.get_selected_objects(include_active=False):
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -163,7 +163,7 @@ class PerformQuantityTakeOff(bpy.types.Operator, tool.Ifc.Operator):
|
||||
def _execute(self, context):
|
||||
import ifc5d.qto
|
||||
|
||||
props = context.scene.BIMQtoProperties
|
||||
props = tool.Qto.get_qto_props()
|
||||
|
||||
elements: set[ifcopenshell.entity_instance]
|
||||
if context.selected_objects:
|
||||
|
||||
@@ -31,12 +31,16 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
|
||||
def get_qto_rule(self, context):
|
||||
CALCULATOR_FUNCTION_ENUM_ITEMS: list[Union[tuple[str, str, str], None]] = []
|
||||
|
||||
|
||||
def get_qto_rule(self: "BIMQtoProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
ifc_file = tool.Ifc.get()
|
||||
is_ifc4x3 = ifc_file.schema == "IFC4X3"
|
||||
results = []
|
||||
results: list[tuple[str, str, str]] = []
|
||||
for rule_id, rule in ifc5d.qto.rules.items():
|
||||
if rule_id.startswith("IFC4X3") != is_ifc4x3:
|
||||
continue
|
||||
@@ -44,14 +48,16 @@ def get_qto_rule(self, context):
|
||||
return results
|
||||
|
||||
|
||||
def get_calculator(self, context):
|
||||
results = []
|
||||
def get_calculator(self: "BIMQtoProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
results: list[tuple[str, str, str]] = []
|
||||
for name, calculator in ifc5d.qto.calculators.items():
|
||||
results.append((name, name, calculator.__doc__))
|
||||
results.append((name, name, calculator.__doc__ or ""))
|
||||
return results
|
||||
|
||||
|
||||
def get_calculator_function(self, context):
|
||||
def get_calculator_function(
|
||||
self: "BIMQtoProperties", context: bpy.types.Context
|
||||
) -> list[Union[tuple[str, str, str], None]]:
|
||||
global CALCULATOR_FUNCTION_ENUM_ITEMS
|
||||
calculator = ifc5d.qto.calculators[self.calculator]
|
||||
CALCULATOR_FUNCTION_ENUM_ITEMS = []
|
||||
@@ -60,6 +66,8 @@ def get_calculator_function(self, context):
|
||||
measure = function.measure.split("Measure")[0][3:]
|
||||
if previous_measure is not None and measure != previous_measure:
|
||||
CALCULATOR_FUNCTION_ENUM_ITEMS.append(None)
|
||||
description = function.description
|
||||
description += f"\n\nInternal function id: '{function_id}'."
|
||||
CALCULATOR_FUNCTION_ENUM_ITEMS.append((function_id, f"{measure}: {function.name}", function.description))
|
||||
previous_measure = measure
|
||||
return CALCULATOR_FUNCTION_ENUM_ITEMS
|
||||
@@ -80,3 +88,12 @@ class BIMQtoProperties(PropertyGroup):
|
||||
),
|
||||
default=False,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
qto_rule: str
|
||||
calculator: str
|
||||
calculator_function: str
|
||||
qto_result: str
|
||||
qto_name: str
|
||||
prop_name: str
|
||||
fallback: bool
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.qto.data import QtoData
|
||||
|
||||
|
||||
@@ -32,7 +33,7 @@ class BIM_PT_qto(bpy.types.Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
props = tool.Qto.get_qto_props()
|
||||
|
||||
row = layout.row()
|
||||
if context.selected_objects:
|
||||
@@ -57,7 +58,7 @@ class BIM_PT_qto_manual(bpy.types.Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
props = tool.Qto.get_qto_props()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "calculator")
|
||||
@@ -83,7 +84,7 @@ class BIM_PT_qto_simple(bpy.types.Panel):
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
props = context.scene.BIMQtoProperties
|
||||
props = tool.Qto.get_qto_props()
|
||||
|
||||
row = layout.row()
|
||||
row.prop(props, "qto_result", text="Results")
|
||||
|
||||
@@ -59,7 +59,7 @@ class LoadGroupDecorationData:
|
||||
return ret
|
||||
|
||||
m = models[0]
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
if props.activity_type == "Action":
|
||||
groups = m.LoadedBy or []
|
||||
for g in groups:
|
||||
@@ -128,7 +128,8 @@ class ConnectedStructuralMembersData:
|
||||
if not element:
|
||||
return []
|
||||
results = []
|
||||
props = bpy.context.active_object.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
for rel in element.ConnectsStructuralMembers or []:
|
||||
condition = rel.AppliedCondition
|
||||
if condition:
|
||||
@@ -257,7 +258,7 @@ class StructuralLoadCasesData:
|
||||
|
||||
@classmethod
|
||||
def applicable_structural_loads(cls):
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
results = []
|
||||
for load in tool.Ifc.get().by_type("IfcStructuralLoad"):
|
||||
if not load.Name or not load.is_a(props.applicable_structural_load_types):
|
||||
|
||||
@@ -24,6 +24,7 @@ from mathutils import Vector
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.attribute
|
||||
import ifcopenshell.util.placement
|
||||
import ifcopenshell.util.unit as ifcunit
|
||||
import bonsai.tool as tool
|
||||
from bonsai.bim.module.structural.shader import DecorationShader
|
||||
@@ -304,7 +305,7 @@ class ShaderInfo:
|
||||
populate_members_dict("surface_members", element, activity, factor)
|
||||
recursive_subgroups(subgorups, rec_limit - 1, activity_type, factor=factor)
|
||||
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
group_definition_id = int(props.load_group_to_show)
|
||||
file = tool.Ifc.get()
|
||||
groups = [file.by_id(group_definition_id)]
|
||||
@@ -327,7 +328,7 @@ class ShaderInfo:
|
||||
maximum = max([abs(float(i)) for i in values])
|
||||
if maximum == 0:
|
||||
continue
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
reference_frame = props.reference_frame
|
||||
orientation = np.eye(3)
|
||||
if reference_frame == "LOCAL_COORDS":
|
||||
@@ -435,7 +436,7 @@ class ShaderInfo:
|
||||
) -> np.ndarray:
|
||||
"provides the transformation matrix to convert between reference frames"
|
||||
global_or_local = activity.GlobalOrLocal
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
reference_frame = props.reference_frame
|
||||
transform_matrix = np.eye(3)
|
||||
if reference_frame == "LOCAL_COORDS" and global_or_local != reference_frame:
|
||||
@@ -473,7 +474,7 @@ class ShaderInfo:
|
||||
"mz": (np.array((1, 0, 0)), np.array((0, 1, 0))),
|
||||
}
|
||||
keys = ["fx", "fy", "fz", "mx", "my", "mz"]
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
reference_frame = props.reference_frame
|
||||
if reference_frame == "LOCAL_COORDS":
|
||||
for key in keys:
|
||||
@@ -591,7 +592,7 @@ class ShaderInfo:
|
||||
z_axis = x_axis.cross(y_axis).normalized()
|
||||
rotation = self.get_curve_member_rotation(member)
|
||||
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
reference_frame = props.reference_frame
|
||||
is_local = reference_frame == "LOCAL_COORDS"
|
||||
x_match = abs(Vector((1, 0, 0)).dot(x_axis)) > 0.99
|
||||
|
||||
@@ -20,6 +20,7 @@ import bpy
|
||||
import json
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.api.group
|
||||
import ifcopenshell.api.structural
|
||||
import ifcopenshell.util.attribute
|
||||
import bonsai.bim.helper
|
||||
@@ -77,7 +78,7 @@ class AddStructuralMemberConnection(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
oprops = tool.Blender.get_object_bim_props(obj)
|
||||
props = obj.BIMStructuralProperties
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
file = tool.Ifc.get()
|
||||
related_structural_connection = file.by_id(oprops.ifc_definition_id)
|
||||
relating_structural_member = tool.Ifc.get_entity(props.relating_structural_member)
|
||||
@@ -101,7 +102,8 @@ class EnableEditingStructuralConnectionCondition(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.active_connects_structural_member = self.connects_structural_member
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -113,7 +115,8 @@ class DisableEditingStructuralConnectionCondition(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.active_connects_structural_member = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -166,7 +169,8 @@ class EnableEditingStructuralBoundaryCondition(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.boundary_condition_attributes.clear()
|
||||
|
||||
condition = tool.Ifc.get().by_id(self.boundary_condition)
|
||||
@@ -206,7 +210,8 @@ class EditStructuralBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
file = tool.Ifc.get()
|
||||
connection = file.by_id(self.connection)
|
||||
@@ -236,7 +241,10 @@ class DisableEditingStructuralBoundaryCondition(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.active_object.BIMStructuralProperties.active_boundary_condition = 0
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.active_boundary_condition = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -353,7 +361,7 @@ class EnableEditingStructuralItemAxis(bpy.types.Operator):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
oprops = tool.Blender.get_object_bim_props(obj)
|
||||
props = obj.BIMStructuralProperties
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
self.file = tool.Ifc.get()
|
||||
item = self.file.by_id(oprops.ifc_definition_id)
|
||||
@@ -392,7 +400,8 @@ class DisableEditingStructuralItemAxis(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.is_editing_axis = False
|
||||
if props.axis_empty:
|
||||
bpy.data.objects.remove(props.axis_empty)
|
||||
@@ -407,7 +416,7 @@ class EditStructuralItemAxis(bpy.types.Operator, tool.Ifc.Operator):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
oprops = tool.Blender.get_object_bim_props(obj)
|
||||
props = obj.BIMStructuralProperties
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
relative_matrix = props.axis_empty.matrix_world @ obj.matrix_world.inverted()
|
||||
z_axis = relative_matrix.col[2][0:3]
|
||||
self.file = tool.Ifc.get()
|
||||
@@ -429,7 +438,7 @@ class EnableEditingStructuralConnectionCS(bpy.types.Operator):
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
props = obj.BIMStructuralProperties
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
self.file = tool.Ifc.get()
|
||||
item = tool.Ifc.get_entity(obj)
|
||||
@@ -481,7 +490,8 @@ class DisableEditingStructuralConnectionCS(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
obj = context.active_object
|
||||
props = obj.BIMStructuralProperties
|
||||
assert obj
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
props.is_editing_connection_cs = False
|
||||
if props.ccs_empty:
|
||||
bpy.data.objects.remove(props.ccs_empty)
|
||||
@@ -498,7 +508,7 @@ class EditStructuralConnectionCS(bpy.types.Operator, tool.Ifc.Operator):
|
||||
assert obj
|
||||
item = tool.Ifc.get_entity(obj)
|
||||
assert item
|
||||
props = obj.BIMStructuralProperties
|
||||
props = tool.Structural.get_object_structural_props(obj)
|
||||
relative_matrix = props.ccs_empty.matrix_world @ obj.matrix_world.inverted()
|
||||
x_axis = relative_matrix.col[0][0:3]
|
||||
z_axis = relative_matrix.col[2][0:3]
|
||||
@@ -567,13 +577,13 @@ class EditStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
attributes = bonsai.bim.helper.export_attributes(props.load_case_attributes)
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"structural.edit_structural_load_case",
|
||||
ifcopenshell.api.structural.edit_structural_load_case(
|
||||
self.file,
|
||||
**{"load_case": self.file.by_id(props.active_load_case_id), "attributes": attributes},
|
||||
load_case=self.file.by_id(props.active_load_case_id),
|
||||
attributes=attributes,
|
||||
)
|
||||
bpy.ops.bim.disable_editing_structural_load_case()
|
||||
return {"FINISHED"}
|
||||
@@ -587,9 +597,7 @@ class RemoveStructuralLoadCase(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"structural.remove_structural_load_case", self.file, load_case=self.file.by_id(self.load_case)
|
||||
)
|
||||
ifcopenshell.api.structural.remove_structural_load_case(self.file, load_case=self.file.by_id(self.load_case))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -600,7 +608,7 @@ class EnableEditingStructuralLoadCase(bpy.types.Operator):
|
||||
load_case: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
self.props.active_load_case_id = self.load_case
|
||||
self.props.load_case_editing_type = "ATTRIBUTES"
|
||||
self.props.load_case_attributes.clear()
|
||||
@@ -620,7 +628,8 @@ class DisableEditingStructuralLoadCase(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMStructuralProperties.active_load_case_id = 0
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.active_load_case_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -631,9 +640,9 @@ class EnableEditingStructuralLoadCaseGroups(bpy.types.Operator):
|
||||
load_case: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props.active_load_case_id = self.load_case
|
||||
self.props.load_case_editing_type = "GROUPS"
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.active_load_case_id = self.load_case
|
||||
props.load_case_editing_type = "GROUPS"
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -645,10 +654,8 @@ class AddStructuralLoadGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
load_group = ifcopenshell.api.run("structural.add_structural_load_group", self.file)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group", self.file, products=[load_group], group=self.file.by_id(self.load_case)
|
||||
)
|
||||
load_group = ifcopenshell.api.structural.add_structural_load_group(self.file)
|
||||
ifcopenshell.api.group.assign_group(self.file, products=[load_group], group=self.file.by_id(self.load_case))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -660,9 +667,7 @@ class RemoveStructuralLoadGroup(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
def _execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"structural.remove_structural_load_group", self.file, load_group=self.file.by_id(self.load_group)
|
||||
)
|
||||
ifcopenshell.api.structural.remove_structural_load_group(self.file, load_group=self.file.by_id(self.load_group))
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -674,15 +679,15 @@ class EnableEditingStructuralLoadGroupActivities(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
self.props.active_load_group_id = self.load_group
|
||||
self.props.load_group_editing_type = "ACTIVITY"
|
||||
self.load_structural_activities()
|
||||
return {"FINISHED"}
|
||||
|
||||
def load_structural_activities(self):
|
||||
def load_structural_activities(self) -> None:
|
||||
self.props.load_group_activities.clear()
|
||||
for rel in tool.Ifc.get().by_id(self.load_group).IsGroupedBy:
|
||||
for rel in self.file.by_id(self.load_group).IsGroupedBy:
|
||||
for activity in rel.RelatedObjects:
|
||||
new = self.props.load_group_activities.add()
|
||||
new.ifc_definition_id = activity.id()
|
||||
@@ -697,7 +702,7 @@ class AddStructuralActivity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
load_group: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
self.file = tool.Ifc.get()
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
@@ -726,16 +731,13 @@ class AddStructuralActivity(bpy.types.Operator, tool.Ifc.Operator):
|
||||
|
||||
ifc_class = applicable_activity_class[element.is_a()]
|
||||
|
||||
activity = ifcopenshell.api.run(
|
||||
"structural.add_structural_activity",
|
||||
activity = ifcopenshell.api.structural.add_structural_activity(
|
||||
self.file,
|
||||
ifc_class=ifc_class,
|
||||
applied_load=self.file.by_id(int(self.props.applicable_structural_loads)),
|
||||
structural_member=element,
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group", self.file, products=[activity], group=self.file.by_id(self.load_group)
|
||||
)
|
||||
ifcopenshell.api.group.assign_group(self.file, products=[activity], group=self.file.by_id(self.load_group))
|
||||
bpy.ops.bim.enable_editing_structural_load_group_activities(load_group=self.load_group)
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -747,7 +749,7 @@ class LoadStructuralLoads(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.structural_loads.clear()
|
||||
loads = tool.Ifc.get().by_type("IfcStructuralLoad")
|
||||
if props.filtered_structural_loads:
|
||||
@@ -780,7 +782,8 @@ class DisableStructuralLoadEditingUI(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMStructuralProperties.is_editing_loads = False
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.is_editing_loads = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -806,7 +809,7 @@ class EnableEditingStructuralLoad(bpy.types.Operator):
|
||||
structural_load: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.structural_load_attributes.clear()
|
||||
bonsai.bim.helper.import_attributes2(
|
||||
tool.Ifc.get().by_id(self.structural_load), props.structural_load_attributes
|
||||
@@ -821,7 +824,8 @@ class DisableEditingStructuralLoad(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMStructuralProperties.active_structural_load_id = 0
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.active_structural_load_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -832,12 +836,10 @@ class RemoveStructuralLoad(bpy.types.Operator, tool.Ifc.Operator):
|
||||
structural_load: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"structural.remove_structural_load",
|
||||
ifcopenshell.api.structural.remove_structural_load(
|
||||
self.file,
|
||||
**{"structural_load": self.file.by_id(self.structural_load)},
|
||||
structural_load=self.file.by_id(self.structural_load),
|
||||
)
|
||||
bpy.ops.bim.load_structural_loads()
|
||||
return {"FINISHED"}
|
||||
@@ -849,16 +851,13 @@ class EditStructuralLoad(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
attributes = bonsai.bim.helper.export_attributes(props.structural_load_attributes)
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"structural.edit_structural_load",
|
||||
ifcopenshell.api.structural.edit_structural_load(
|
||||
self.file,
|
||||
**{
|
||||
"structural_load": self.file.by_id(props.active_structural_load_id),
|
||||
"attributes": attributes,
|
||||
},
|
||||
structural_load=self.file.by_id(props.active_structural_load_id),
|
||||
attributes=attributes,
|
||||
)
|
||||
bpy.ops.bim.load_structural_loads()
|
||||
return {"FINISHED"}
|
||||
@@ -870,7 +869,7 @@ class ToggleFilterStructuralLoads(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.filtered_structural_loads = not props.filtered_structural_loads
|
||||
bpy.ops.bim.load_structural_loads()
|
||||
return {"FINISHED"}
|
||||
@@ -883,7 +882,7 @@ class LoadBoundaryConditions(bpy.types.Operator):
|
||||
|
||||
def execute(self, context):
|
||||
self.file = tool.Ifc.get()
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.boundary_conditions.clear()
|
||||
conditions = tool.Ifc.get().by_type("IfcBoundaryCondition")
|
||||
if props.filtered_boundary_conditions:
|
||||
@@ -916,7 +915,7 @@ class ToggleFilterBoundaryConditions(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.filtered_boundary_conditions = not props.filtered_boundary_conditions
|
||||
bpy.ops.bim.load_boundary_conditions()
|
||||
return {"FINISHED"}
|
||||
@@ -928,7 +927,8 @@ class DisableBoundaryConditionEditingUI(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMStructuralProperties.is_editing_boundary_conditions = False
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.is_editing_boundary_conditions = False
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -939,8 +939,7 @@ class AddBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
ifc_class: bpy.props.StringProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
result = ifcopenshell.api.run(
|
||||
"structural.add_structural_boundary_condition",
|
||||
result = ifcopenshell.api.structural.add_structural_boundary_condition(
|
||||
tool.Ifc.get(),
|
||||
name="New Load",
|
||||
ifc_class=self.ifc_class,
|
||||
@@ -957,7 +956,7 @@ class EnableEditingBoundaryCondition(bpy.types.Operator):
|
||||
boundary_condition: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.boundary_condition_attributes.clear()
|
||||
|
||||
boundary_condition = tool.Ifc.get().by_id(self.boundary_condition)
|
||||
@@ -994,7 +993,8 @@ class DisableEditingBoundaryCondition(bpy.types.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.BIMStructuralProperties.active_boundary_condition_id = 0
|
||||
props = tool.Structural.get_structural_props()
|
||||
props.active_boundary_condition_id = 0
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
@@ -1005,12 +1005,10 @@ class RemoveBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
boundary_condition: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
self.file = tool.Ifc.get()
|
||||
ifcopenshell.api.run(
|
||||
"structural.remove_structural_boundary_condition",
|
||||
ifcopenshell.api.structural.remove_structural_boundary_condition(
|
||||
self.file,
|
||||
**{"boundary_condition": self.file.by_id(self.boundary_condition)},
|
||||
boundary_condition=self.file.by_id(self.boundary_condition),
|
||||
)
|
||||
bpy.ops.bim.load_boundary_conditions()
|
||||
return {"FINISHED"}
|
||||
@@ -1022,7 +1020,7 @@ class EditBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def _execute(self, context):
|
||||
props = context.scene.BIMStructuralProperties
|
||||
props = tool.Structural.get_structural_props()
|
||||
self.file = tool.Ifc.get()
|
||||
# attributes = bonsai.bim.helper.export_attributes(props.boundary_condition_attributes)
|
||||
attributes = {}
|
||||
@@ -1035,10 +1033,10 @@ class EditBoundaryCondition(bpy.types.Operator, tool.Ifc.Operator):
|
||||
attributes[attribute.name] = {"value": attribute.bool_value, "type": attribute.enum_value}
|
||||
else:
|
||||
attributes[attribute.name] = {"value": attribute.float_value, "type": attribute.enum_value}
|
||||
ifcopenshell.api.run(
|
||||
"structural.edit_structural_boundary_condition",
|
||||
ifcopenshell.api.structural.edit_structural_boundary_condition(
|
||||
self.file,
|
||||
**{"condition": self.file.by_id(props.active_boundary_condition_id), "attributes": attributes},
|
||||
condition=self.file.by_id(props.active_boundary_condition_id),
|
||||
attributes=attributes,
|
||||
)
|
||||
bpy.ops.bim.load_boundary_conditions()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -37,52 +37,62 @@ from bpy.props import (
|
||||
FloatVectorProperty,
|
||||
CollectionProperty,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
|
||||
def get_load_groups_to_show(self, context):
|
||||
def get_load_groups_to_show(self: "BIMStructuralProperties", context: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||
if not LoadGroupDecorationData.is_loaded:
|
||||
LoadGroupDecorationData.load()
|
||||
return LoadGroupDecorationData.data["load_groups_to_show"]
|
||||
|
||||
|
||||
def update_activity_type(self, context):
|
||||
def update_activity_type(self: "BIMStructuralProperties", context: bpy.types.Context) -> None:
|
||||
LoadGroupDecorationData.is_loaded = False
|
||||
|
||||
|
||||
def get_applicable_structural_load_types(self, context):
|
||||
def get_applicable_structural_load_types(
|
||||
self: "BIMStructuralProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not StructuralLoadCasesData.is_loaded:
|
||||
StructuralLoadCasesData.load()
|
||||
return StructuralLoadCasesData.data["applicable_structural_load_types"]
|
||||
|
||||
|
||||
def updateApplicableStructuralLoadTypes(self, context):
|
||||
def updateApplicableStructuralLoadTypes(self: "BIMStructuralProperties", context: bpy.types.Context) -> None:
|
||||
StructuralLoadCasesData.data["applicable_structural_load_types"] = (
|
||||
StructuralLoadCasesData.applicable_structural_load_types()
|
||||
)
|
||||
|
||||
|
||||
def get_applicable_structural_loads(self, context):
|
||||
def get_applicable_structural_loads(
|
||||
self: "BIMStructuralProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not StructuralLoadCasesData.is_loaded:
|
||||
StructuralLoadCasesData.load()
|
||||
return StructuralLoadCasesData.data["applicable_structural_loads"]
|
||||
|
||||
|
||||
def get_structural_load_types(self, context):
|
||||
def get_structural_load_types(
|
||||
self: "BIMStructuralProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not StructuralLoadsData.is_loaded:
|
||||
StructuralLoadsData.load()
|
||||
return StructuralLoadsData.data["structural_load_types"]
|
||||
|
||||
|
||||
def get_boundary_condition_types(self, context):
|
||||
def get_boundary_condition_types(
|
||||
self: "BIMStructuralProperties", context: bpy.types.Context
|
||||
) -> list[tuple[str, str, str]]:
|
||||
if not BoundaryConditionsData.is_loaded:
|
||||
BoundaryConditionsData.load()
|
||||
return BoundaryConditionsData.data["boundary_condition_types"]
|
||||
|
||||
|
||||
def updateAxisAngle(self, context):
|
||||
def updateAxisAngle(self: "BIMObjectStructuralProperties", context: bpy.types.Context) -> None:
|
||||
if not self.axis_empty:
|
||||
return
|
||||
obj = context.active_object
|
||||
assert obj and isinstance(obj.data, bpy.types.Mesh)
|
||||
empty = self.axis_empty
|
||||
x_axis = obj.data.vertices[1].co - obj.data.vertices[0].co
|
||||
empty.location = obj.data.vertices[0].co
|
||||
@@ -92,10 +102,11 @@ def updateAxisAngle(self, context):
|
||||
empty.rotation_euler[0] = radians(self.axis_angle)
|
||||
|
||||
|
||||
def updateConnectionCS(self, context):
|
||||
def updateConnectionCS(self: "BIMObjectStructuralProperties", context: bpy.types.Context) -> None:
|
||||
if not self.ccs_empty:
|
||||
return
|
||||
obj = context.active_object
|
||||
assert obj and isinstance(obj.data, bpy.types.Mesh)
|
||||
empty = self.ccs_empty
|
||||
empty.location = obj.data.vertices[0].co
|
||||
empty.rotation_mode = "XYZ"
|
||||
@@ -108,24 +119,39 @@ class StructuralAnalysisModel(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ifc_definition_id: int
|
||||
|
||||
|
||||
class StructuralActivity(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
applied_load_class: StringProperty(name="Applied Load Class")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
applied_load_class: str
|
||||
ifc_definition_id: int
|
||||
|
||||
|
||||
class StructuralLoad(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
number_of_inverse_references: IntProperty(name="Number of Inverse References")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ifc_definition_id: int
|
||||
number_of_inverse_references: int
|
||||
|
||||
|
||||
class BoundaryCondition(PropertyGroup):
|
||||
name: StringProperty(name="Name")
|
||||
ifc_definition_id: IntProperty(name="IFC Definition ID")
|
||||
number_of_inverse_references: IntProperty(name="Number of Inverse References")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
ifc_definition_id: int
|
||||
number_of_inverse_references: int
|
||||
|
||||
|
||||
class BIMStructuralProperties(PropertyGroup):
|
||||
structural_analysis_model_attributes: CollectionProperty(
|
||||
@@ -187,6 +213,45 @@ class BIMStructuralProperties(PropertyGroup):
|
||||
)
|
||||
load_group_to_show: EnumProperty(items=get_load_groups_to_show, name="Load Groups")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
structural_analysis_model_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
is_editing: bool
|
||||
structural_analysis_models: bpy.types.bpy_prop_collection_idprop[StructuralAnalysisModel]
|
||||
active_structural_analysis_model_index: int
|
||||
active_structural_analysis_model_id: int
|
||||
load_case_editing_type: str
|
||||
load_case_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
active_load_case_id: int
|
||||
load_group_editing_type: str
|
||||
active_load_group_id: int
|
||||
applicable_structural_load_types: str
|
||||
applicable_structural_loads: str
|
||||
load_group_activities: bpy.types.bpy_prop_collection_idprop[StructuralActivity]
|
||||
active_load_group_activity_index: int
|
||||
|
||||
structural_loads: bpy.types.bpy_prop_collection_idprop[StructuralLoad]
|
||||
active_structural_load_index: int
|
||||
active_structural_load_id: int
|
||||
is_editing_loads: bool
|
||||
structural_load_types: str
|
||||
structural_load_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
filtered_structural_loads: bool
|
||||
|
||||
boundary_conditions: bpy.types.bpy_prop_collection_idprop[BoundaryCondition]
|
||||
active_boundary_condition_index: int
|
||||
active_boundary_condition_id: int
|
||||
is_editing_boundary_conditions: bool
|
||||
boundary_condition_types: str
|
||||
boundary_condition_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
filtered_boundary_conditions: bool
|
||||
|
||||
show_loads: bool
|
||||
update_load_repr: bool
|
||||
enable_repr_auto_update: bool
|
||||
reference_frame: str
|
||||
activity_type: str
|
||||
load_group_to_show: str
|
||||
|
||||
|
||||
class BIMObjectStructuralProperties(PropertyGroup):
|
||||
boundary_condition_attributes: CollectionProperty(name="Boundary Condition Attributes", type=Attribute)
|
||||
@@ -203,3 +268,19 @@ class BIMObjectStructuralProperties(PropertyGroup):
|
||||
ccs_y_angle: FloatProperty(name="Connection CS Y Angle", update=updateConnectionCS)
|
||||
ccs_z_angle: FloatProperty(name="Connection CS Z Angle", update=updateConnectionCS)
|
||||
ccs_empty: PointerProperty(name="CCS Empty", type=bpy.types.Object)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
boundary_condition_attributes: bpy.types.bpy_prop_collection_idprop[Attribute]
|
||||
active_boundary_condition: int
|
||||
active_connects_structural_member: int
|
||||
relating_structural_member: Union[bpy.types.Object, None]
|
||||
is_editing_axis: bool
|
||||
axis_angle: float
|
||||
axis_empty: Union[bpy.types.Object, None]
|
||||
|
||||
# relating_structural_activity: Union[bpy.types.Object, None]
|
||||
is_editing_connection_cs: bool
|
||||
ccs_x_angle: float
|
||||
ccs_y_angle: float
|
||||
ccs_z_angle: float
|
||||
ccs_empty: Union[bpy.types.Object, None]
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.helper
|
||||
@@ -31,9 +32,25 @@ from bonsai.bim.module.structural.data import (
|
||||
StructuralConnectionData,
|
||||
BoundaryConditionsData,
|
||||
)
|
||||
from typing import TYPE_CHECKING, Any, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.structural.prop import (
|
||||
BIMStructuralProperties,
|
||||
BIMObjectStructuralProperties,
|
||||
BoundaryCondition,
|
||||
StructuralLoad,
|
||||
StructuralActivity,
|
||||
StructuralAnalysisModel,
|
||||
)
|
||||
|
||||
|
||||
def draw_boundary_condition_ui(layout, boundary_condition, connection_id, props):
|
||||
def draw_boundary_condition_ui(
|
||||
layout: bpy.types.UILayout,
|
||||
boundary_condition: dict[str, Any],
|
||||
connection_id: int,
|
||||
props: BIMObjectStructuralProperties,
|
||||
) -> None:
|
||||
row = layout.row(align=True)
|
||||
if not boundary_condition:
|
||||
row.label(text="No Boundary Condition Found", icon="CON_TRACKTO")
|
||||
@@ -59,11 +76,13 @@ def draw_boundary_condition_ui(layout, boundary_condition, connection_id, props)
|
||||
draw_boundary_condition_read_only_ui(layout, boundary_condition)
|
||||
|
||||
|
||||
def draw_boundary_condition_editable_ui(layout: bpy.types.UILayout, props: bpy.types.PropertyGroup) -> None:
|
||||
def draw_boundary_condition_editable_ui(
|
||||
layout: bpy.types.UILayout, props: Union[BIMStructuralProperties, BIMObjectStructuralProperties]
|
||||
) -> None:
|
||||
draw_attributes(props.boundary_condition_attributes, layout)
|
||||
|
||||
|
||||
def draw_boundary_condition_read_only_ui(layout, boundary_condition):
|
||||
def draw_boundary_condition_read_only_ui(layout: bpy.types.UILayout, boundary_condition: dict[str, Any]) -> None:
|
||||
for attribute in boundary_condition["attributes"]:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=attribute["name"])
|
||||
@@ -100,11 +119,15 @@ class BIM_PT_structural_boundary_conditions(Panel):
|
||||
if not StructuralBoundaryConditionsData.is_loaded:
|
||||
StructuralBoundaryConditionsData.load()
|
||||
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
self.props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
draw_boundary_condition_ui(
|
||||
self.layout,
|
||||
StructuralBoundaryConditionsData.data["boundary_condition"],
|
||||
StructuralBoundaryConditionsData.data["connection_id"],
|
||||
context.active_object.BIMStructuralProperties,
|
||||
self.props,
|
||||
)
|
||||
|
||||
|
||||
@@ -135,7 +158,9 @@ class BIM_PT_connected_structural_members(Panel):
|
||||
if not ConnectedStructuralMembersData.is_loaded:
|
||||
ConnectedStructuralMembersData.load()
|
||||
|
||||
self.props = context.active_object.BIMStructuralProperties
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
self.props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.prop(self.props, "relating_structural_member", text="", icon="CON_TRACKTO")
|
||||
@@ -188,7 +213,9 @@ class BIM_PT_structural_member(Panel):
|
||||
if not StructuralMemberData.is_loaded:
|
||||
StructuralMemberData.load()
|
||||
|
||||
self.props = context.active_object.BIMStructuralProperties
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
self.props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
if StructuralMemberData.data["active_object_class"] == "IfcStructuralCurveMember":
|
||||
if self.props.is_editing_axis:
|
||||
@@ -231,7 +258,9 @@ class BIM_PT_structural_connection(Panel):
|
||||
if not StructuralConnectionData.is_loaded:
|
||||
StructuralConnectionData.load()
|
||||
|
||||
self.props = context.active_object.BIMStructuralProperties
|
||||
obj = context.active_object
|
||||
assert obj
|
||||
self.props = tool.Structural.get_object_structural_props(obj)
|
||||
|
||||
if StructuralConnectionData.data["active_object_class"] == "IfcStructuralCurveConnection":
|
||||
if self.props.is_editing_axis:
|
||||
@@ -281,7 +310,7 @@ class BIM_PT_structural_analysis_models(Panel):
|
||||
if not StructuralAnalysisModelsData.is_loaded:
|
||||
StructuralAnalysisModelsData.load()
|
||||
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(
|
||||
@@ -309,7 +338,16 @@ class BIM_PT_structural_analysis_models(Panel):
|
||||
|
||||
|
||||
class BIM_UL_structural_analysis_models(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self,
|
||||
context: bpy.types.Context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMStructuralProperties,
|
||||
item: StructuralAnalysisModel,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
@@ -324,10 +362,10 @@ class BIM_UL_structural_analysis_models(UIList):
|
||||
op = row.operator("bim.assign_structural_analysis_model", text="", icon="KEYFRAME", emboss=False)
|
||||
op.structural_analysis_model = item.ifc_definition_id
|
||||
|
||||
if context.scene.BIMStructuralProperties.active_structural_analysis_model_id == item.ifc_definition_id:
|
||||
if data.active_structural_analysis_model_id == item.ifc_definition_id:
|
||||
row.operator("bim.edit_structural_analysis_model", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_structural_analysis_model", text="", icon="CANCEL")
|
||||
elif context.scene.BIMStructuralProperties.active_structural_analysis_model_id:
|
||||
elif data.active_structural_analysis_model_id:
|
||||
op = row.operator("bim.remove_structural_analysis_model", text="", icon="X")
|
||||
op.structural_analysis_model = item.ifc_definition_id
|
||||
else:
|
||||
@@ -354,7 +392,7 @@ class BIM_PT_structural_load_cases(Panel):
|
||||
if not StructuralLoadCasesData.is_loaded:
|
||||
StructuralLoadCasesData.load()
|
||||
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
|
||||
row = self.layout.row()
|
||||
row.operator("bim.add_structural_load_case", icon="ADD")
|
||||
@@ -425,7 +463,16 @@ class BIM_PT_structural_load_cases(Panel):
|
||||
|
||||
|
||||
class BIM_UL_structural_activities(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMStructuralProperties,
|
||||
item: StructuralActivity,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=item.name)
|
||||
@@ -446,8 +493,7 @@ class BIM_PT_show_structural_activities(Panel):
|
||||
return file and hasattr(file, "schema") and file.schema != "IFC2X3"
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.operator(
|
||||
@@ -480,7 +526,7 @@ class BIM_PT_structural_loads(Panel):
|
||||
if not StructuralLoadsData.is_loaded:
|
||||
StructuralLoadsData.load()
|
||||
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(text=f"{StructuralLoadsData.data['total_loads']} Structural Loads Found", icon="ANIM_DATA")
|
||||
@@ -513,16 +559,25 @@ class BIM_PT_structural_loads(Panel):
|
||||
|
||||
|
||||
class BIM_UL_structural_loads(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMStructuralProperties,
|
||||
item: StructuralLoad,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{item.name} ({item.number_of_inverse_references})")
|
||||
row.label(text=StructuralLoadsData.data["load_classes"][item.ifc_definition_id])
|
||||
|
||||
if context.scene.BIMStructuralProperties.active_structural_load_id == item.ifc_definition_id:
|
||||
if data.active_structural_load_id == item.ifc_definition_id:
|
||||
row.operator("bim.edit_structural_load", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_structural_load", text="", icon="CANCEL")
|
||||
elif context.scene.BIMStructuralProperties.active_structural_load_id:
|
||||
elif data.active_structural_load_id:
|
||||
op = row.operator("bim.remove_structural_load", text="", icon="X")
|
||||
op.structural_load = item.ifc_definition_id
|
||||
else:
|
||||
@@ -549,7 +604,7 @@ class BIM_PT_boundary_conditions(Panel):
|
||||
if not BoundaryConditionsData.is_loaded:
|
||||
BoundaryConditionsData.load()
|
||||
|
||||
self.props = context.scene.BIMStructuralProperties
|
||||
self.props = tool.Structural.get_structural_props()
|
||||
|
||||
row = self.layout.row(align=True)
|
||||
row.label(
|
||||
@@ -587,16 +642,25 @@ class BIM_PT_boundary_conditions(Panel):
|
||||
|
||||
|
||||
class BIM_UL_boundary_conditions(UIList):
|
||||
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
|
||||
def draw_item(
|
||||
self,
|
||||
context,
|
||||
layout: bpy.types.UILayout,
|
||||
data: BIMStructuralProperties,
|
||||
item: BoundaryCondition,
|
||||
icon,
|
||||
active_data,
|
||||
active_propname,
|
||||
):
|
||||
if item:
|
||||
row = layout.row(align=True)
|
||||
row.label(text=f"{item.name} ({item.number_of_inverse_references})")
|
||||
row.label(text=BoundaryConditionsData.data["condition_classes"][item.ifc_definition_id])
|
||||
|
||||
if context.scene.BIMStructuralProperties.active_boundary_condition_id == item.ifc_definition_id:
|
||||
if data.active_boundary_condition_id == item.ifc_definition_id:
|
||||
row.operator("bim.edit_boundary_condition", text="", icon="CHECKMARK")
|
||||
row.operator("bim.disable_editing_boundary_condition", text="", icon="CANCEL")
|
||||
elif context.scene.BIMStructuralProperties.active_boundary_condition_id:
|
||||
elif data.active_boundary_condition_id:
|
||||
op = row.operator("bim.remove_boundary_condition", text="", icon="X")
|
||||
op.boundary_condition = item.ifc_definition_id
|
||||
else:
|
||||
|
||||
@@ -54,7 +54,7 @@ class StructuralToolUI:
|
||||
@classmethod
|
||||
def draw(cls, context, layout):
|
||||
cls.layout = layout
|
||||
# cls.props = context.scene.BIMStructuralProperties
|
||||
cls.props = tool.Structural.get_structural_props()
|
||||
|
||||
row = cls.layout.row(align=True)
|
||||
if not tool.Ifc.get():
|
||||
@@ -99,12 +99,12 @@ class Hotkey(bpy.types.Operator, tool.Ifc.Operator):
|
||||
return operator.description or ""
|
||||
|
||||
def _execute(self, context):
|
||||
# self.props = context.scene.BIMStructuralProperties
|
||||
# self.props = tool.Structural.get_structural_props()
|
||||
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.BIMStructuralProperties
|
||||
# self.props = tool.Structural.get_structural_props()
|
||||
return self.execute(context)
|
||||
|
||||
def draw(self, context):
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import bonsai.core.tool
|
||||
import bonsai.bim.schema
|
||||
@@ -24,12 +25,19 @@ import ifcopenshell
|
||||
import ifcopenshell.util.unit
|
||||
import ifcopenshell.util.element
|
||||
from mathutils import Vector
|
||||
from typing import Optional, Union, Literal
|
||||
from typing import Optional, Union, Literal, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.qto.prop import BIMQtoProperties
|
||||
|
||||
QuantityTypes = Literal["Q_LENGTH", "Q_AREA", "Q_VOLUME"]
|
||||
|
||||
|
||||
class Qto(bonsai.core.tool.Qto):
|
||||
@classmethod
|
||||
def get_qto_props(cls) -> BIMQtoProperties:
|
||||
return bpy.context.scene.BIMQtoProperties
|
||||
|
||||
@classmethod
|
||||
def get_radius_of_selected_vertices(cls, obj: bpy.types.Object) -> float:
|
||||
selected_verts = [v.co for v in obj.data.vertices if v.select]
|
||||
@@ -41,7 +49,8 @@ class Qto(bonsai.core.tool.Qto):
|
||||
|
||||
@classmethod
|
||||
def set_qto_result(cls, result: float) -> None:
|
||||
bpy.context.scene.BIMQtoProperties.qto_result = str(round(result, 3))
|
||||
props = cls.get_qto_props()
|
||||
props.qto_result = str(round(result, 3))
|
||||
|
||||
@classmethod
|
||||
def get_rounded_value(cls, new_quantity: float) -> float:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import annotations
|
||||
import bpy
|
||||
import ifcopenshell
|
||||
import ifcopenshell.util.representation
|
||||
@@ -23,30 +24,46 @@ import json
|
||||
import bonsai.bim.helper
|
||||
import bonsai.core.tool
|
||||
import bonsai.tool as tool
|
||||
from typing import Union, Any
|
||||
from typing import Union, Any, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from bonsai.bim.module.structural.prop import BIMStructuralProperties, BIMObjectStructuralProperties
|
||||
|
||||
|
||||
class Structural(bonsai.core.tool.Structural):
|
||||
@classmethod
|
||||
def get_structural_props(cls) -> BIMStructuralProperties:
|
||||
return bpy.context.scene.BIMStructuralProperties
|
||||
|
||||
@classmethod
|
||||
def get_object_structural_props(cls, obj: bpy.types.Object) -> BIMObjectStructuralProperties:
|
||||
return obj.BIMStructuralProperties
|
||||
|
||||
@classmethod
|
||||
def disable_editing_structural_analysis_model(cls) -> None:
|
||||
bpy.context.scene.BIMStructuralProperties.active_structural_analysis_model_id = 0
|
||||
props = cls.get_structural_props()
|
||||
props.active_structural_analysis_model_id = 0
|
||||
|
||||
@classmethod
|
||||
def disable_structural_analysis_model_editing_ui(cls) -> None:
|
||||
bpy.context.scene.BIMStructuralProperties.is_editing = False
|
||||
props = cls.get_structural_props()
|
||||
props.is_editing = False
|
||||
|
||||
@classmethod
|
||||
def enable_editing_structural_analysis_model(cls, model: Union[int, None]) -> None:
|
||||
if model:
|
||||
bpy.context.scene.BIMStructuralProperties.active_structural_analysis_model_id = model
|
||||
props = cls.get_structural_props()
|
||||
props.active_structural_analysis_model_id = model
|
||||
|
||||
@classmethod
|
||||
def enable_structural_analysis_model_editing_ui(cls) -> None:
|
||||
bpy.context.scene.BIMStructuralProperties.is_editing = True
|
||||
props = cls.get_structural_props()
|
||||
props.is_editing = True
|
||||
|
||||
@classmethod
|
||||
def enabled_structural_analysis_model_editing_ui(cls) -> bool:
|
||||
return bpy.context.scene.BIMStructuralProperties.is_editing
|
||||
props = cls.get_structural_props()
|
||||
return props.is_editing
|
||||
|
||||
@classmethod
|
||||
def ensure_representation_contexts(cls) -> None:
|
||||
@@ -71,7 +88,7 @@ class Structural(bonsai.core.tool.Structural):
|
||||
|
||||
@classmethod
|
||||
def get_active_structural_analysis_model(cls) -> ifcopenshell.entity_instance:
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = cls.get_structural_props()
|
||||
model = tool.Ifc.get().by_id(props.active_structural_analysis_model_id)
|
||||
return model
|
||||
|
||||
@@ -120,13 +137,13 @@ class Structural(bonsai.core.tool.Structural):
|
||||
|
||||
@classmethod
|
||||
def get_structural_analysis_model_attributes(cls) -> dict[str, Any]:
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = cls.get_structural_props()
|
||||
attributes = bonsai.bim.helper.export_attributes(props.structural_analysis_model_attributes)
|
||||
return attributes
|
||||
|
||||
@classmethod
|
||||
def load_structural_analysis_model_attributes(cls, data: dict[str, Any]) -> None:
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = cls.get_structural_props()
|
||||
props.structural_analysis_model_attributes.clear()
|
||||
schema = tool.Ifc.schema()
|
||||
for attribute in schema.declaration_by_name("IfcStructuralAnalysisModel").all_attributes():
|
||||
@@ -149,7 +166,7 @@ class Structural(bonsai.core.tool.Structural):
|
||||
@classmethod
|
||||
def load_structural_analysis_models(cls) -> None:
|
||||
models = tool.Structural.get_ifc_structural_analysis_models()
|
||||
props = bpy.context.scene.BIMStructuralProperties
|
||||
props = cls.get_structural_props()
|
||||
props.structural_analysis_models.clear()
|
||||
for ifc_definition_id, model in models.items():
|
||||
new = props.structural_analysis_models.add()
|
||||
|
||||
@@ -42,7 +42,8 @@ class TestGetRadiusOfSelectedVertices(test.bim.bootstrap.NewFile):
|
||||
class TestSetQtoResult(test.bim.bootstrap.NewFile):
|
||||
def test_run(self):
|
||||
subject.set_qto_result(123.4567)
|
||||
assert bpy.context.scene.BIMQtoProperties.qto_result == "123.457"
|
||||
props = tool.Qto.get_qto_props()
|
||||
assert props.qto_result == "123.457"
|
||||
|
||||
|
||||
class TestGetRoundedValue(test.bim.bootstrap.NewFile):
|
||||
|
||||
+34
-20
@@ -30,11 +30,16 @@ import ifcopenshell.util.shape
|
||||
import ifcopenshell.util.representation
|
||||
import ifcopenshell.util.type
|
||||
import multiprocessing
|
||||
from collections import namedtuple, defaultdict
|
||||
from typing import Any, Literal, get_args, Union, Iterable
|
||||
from collections import defaultdict
|
||||
from typing import Any, Literal, get_args, Union, Iterable, NamedTuple
|
||||
|
||||
|
||||
class Function(NamedTuple):
|
||||
measure: str
|
||||
name: str
|
||||
description: str
|
||||
|
||||
|
||||
Function = namedtuple("Function", ["measure", "name", "description"])
|
||||
RULE_SET = Literal[
|
||||
"IFC4QtoBaseQuantities",
|
||||
"IFC4QtoBaseQuantitiesBlender",
|
||||
@@ -161,7 +166,23 @@ class IteratorForTypes:
|
||||
return True
|
||||
|
||||
|
||||
class IfcOpenShell:
|
||||
class QtoCalculator:
|
||||
"""Abstract class for Qto calculators."""
|
||||
|
||||
functions: dict[str, Function]
|
||||
|
||||
@classmethod
|
||||
def calculate(
|
||||
cls,
|
||||
ifc_file: ifcopenshell.file,
|
||||
elements: set[ifcopenshell.entity_instance],
|
||||
qtos: dict[str, dict[str, Union[str, None]]],
|
||||
results: ResultsDict,
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class IfcOpenShell(QtoCalculator):
|
||||
"""Calculates Model body context geometry using the default IfcOpenShell
|
||||
iterator on triangulation elements."""
|
||||
|
||||
@@ -221,13 +242,7 @@ class IfcOpenShell:
|
||||
functions[f"net_{k}"] = Function(v.measure, f"Net {v.name}", v.description)
|
||||
|
||||
@classmethod
|
||||
def calculate(
|
||||
cls,
|
||||
ifc_file: ifcopenshell.file,
|
||||
elements: set[ifcopenshell.entity_instance],
|
||||
qtos: dict[str, dict[str, Union[str, None]]],
|
||||
results: ResultsDict,
|
||||
) -> None:
|
||||
def calculate(cls, ifc_file, elements, qtos, results):
|
||||
formula_functions: dict[str, types.FunctionType] = {}
|
||||
|
||||
cls.gross_settings = ifcopenshell.geom.settings()
|
||||
@@ -327,9 +342,10 @@ class IfcOpenShell:
|
||||
return max([x, y, z])
|
||||
|
||||
|
||||
class Blender:
|
||||
class Blender(QtoCalculator):
|
||||
"""Calculates geometry based on currently loaded Blender objects."""
|
||||
|
||||
# Implementations are located in bonsai.bim.module.qto.calculator.
|
||||
functions = {
|
||||
# IfcLengthMeasure
|
||||
"get_covering_width": Function("IfcLengthMeasure", "Covering Width", ""),
|
||||
@@ -372,13 +388,8 @@ class Blender:
|
||||
"get_net_weight": Function("IfcMassMeasure", "Net Weight", ""),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def calculate(
|
||||
ifc_file: ifcopenshell.file,
|
||||
elements: set[ifcopenshell.entity_instance],
|
||||
qtos: dict[str, dict[str, Union[str, None]]],
|
||||
results: ResultsDict,
|
||||
) -> None:
|
||||
@classmethod
|
||||
def calculate(cls, ifc_file, elements, qtos, results):
|
||||
import bonsai.tool as tool
|
||||
import bonsai.bim.module.qto.calculator as calculator
|
||||
|
||||
@@ -406,4 +417,7 @@ class Blender:
|
||||
results[element] = element_results
|
||||
|
||||
|
||||
calculators = {"Blender": Blender, "IfcOpenShell": IfcOpenShell}
|
||||
calculators: dict[str, type[QtoCalculator]] = {
|
||||
"Blender": Blender,
|
||||
"IfcOpenShell": IfcOpenShell,
|
||||
}
|
||||
|
||||
@@ -64,13 +64,10 @@ def assign_object(
|
||||
|
||||
:param products: The list of parts of the aggregate, typically of IfcElement or
|
||||
IfcSpatialStructureElement subclass
|
||||
:type product: list[ifcopenshell.entity_instance]
|
||||
:param relating_object: The whole of the aggregate, typically an
|
||||
IfcElement or IfcSpatialStructureElement subclass
|
||||
:type relating_object: ifcopenshell.entity_instance
|
||||
:return: The IfcRelAggregate relationship instance
|
||||
or `None` if `products` was empty list.
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -86,16 +83,10 @@ def assign_object(
|
||||
# The site has a building
|
||||
ifcopenshell.api.aggregate.assign_object(model, products=[subelement], relating_object=element)
|
||||
"""
|
||||
settings = {
|
||||
"products": products,
|
||||
"relating_object": relating_object,
|
||||
}
|
||||
|
||||
if not settings["products"]:
|
||||
if not products:
|
||||
return
|
||||
|
||||
products = set(settings["products"])
|
||||
relating_object = settings["relating_object"]
|
||||
products_set = set(products)
|
||||
is_decomposed_by = next((i for i in relating_object.IsDecomposedBy if i.is_a("IfcRelAggregates")), None)
|
||||
|
||||
previous_aggregates_rels: set[ifcopenshell.entity_instance] = set()
|
||||
@@ -103,7 +94,7 @@ def assign_object(
|
||||
products_with_aggregates: list[ifcopenshell.entity_instance] = []
|
||||
|
||||
# check if there is anything to change
|
||||
for product in products:
|
||||
for product in products_set:
|
||||
product_rel = next(iter(product.Decomposes), None)
|
||||
|
||||
if product_rel is None:
|
||||
@@ -129,7 +120,7 @@ def assign_object(
|
||||
|
||||
# unassign elements from previous aggregates
|
||||
for decomposes in previous_aggregates_rels:
|
||||
related_objects = set(decomposes.RelatedObjects) - products
|
||||
related_objects = set(decomposes.RelatedObjects) - products_set
|
||||
if related_objects:
|
||||
decomposes.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": decomposes})
|
||||
@@ -141,7 +132,7 @@ def assign_object(
|
||||
|
||||
# assign elements to a new aggregate
|
||||
if is_decomposed_by:
|
||||
is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products)
|
||||
is_decomposed_by.RelatedObjects = list(set(is_decomposed_by.RelatedObjects) | products_set)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": is_decomposed_by})
|
||||
else:
|
||||
is_decomposed_by = file.create_entity(
|
||||
@@ -149,7 +140,7 @@ def assign_object(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": list(products),
|
||||
"RelatedObjects": list(products_set),
|
||||
"RelatingObject": relating_object,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -23,9 +23,7 @@ def copy_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instanc
|
||||
"""Copies a space boundary
|
||||
|
||||
:param boundary: The IfcRelSpaceBoundary you want to copy.
|
||||
:type boundary: ifcopenshell.entity_instance
|
||||
:return: Duplicate of the IfcRelSpaceBoundary
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -36,9 +34,7 @@ def copy_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_instanc
|
||||
# And now we have two
|
||||
boundary_copy = ifcopenshell.api.boundary.copy_boundary(model, boundary=boundary)
|
||||
"""
|
||||
settings = {"boundary": boundary}
|
||||
|
||||
result = ifcopenshell.util.element.copy(file, settings["boundary"])
|
||||
result = ifcopenshell.util.element.copy(file, boundary)
|
||||
if result.ConnectionGeometry:
|
||||
result.ConnectionGeometry = ifcopenshell.util.element.copy_deep(file, result.ConnectionGeometry)
|
||||
return result
|
||||
|
||||
@@ -27,9 +27,7 @@ def remove_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_insta
|
||||
boundary and its connection geometry is removed.
|
||||
|
||||
:param boundary: The IfcRelSpaceBoundary you want to remove.
|
||||
:type boundary: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -40,13 +38,11 @@ def remove_boundary(file: ifcopenshell.file, boundary: ifcopenshell.entity_insta
|
||||
# Let's remove it!
|
||||
ifcopenshell.api.boundary.remove_boundary(model, boundary=boundary)
|
||||
"""
|
||||
settings = {"boundary": boundary}
|
||||
|
||||
geometry = settings["boundary"].ConnectionGeometry
|
||||
geometry = boundary.ConnectionGeometry
|
||||
if geometry:
|
||||
settings["boundary"].ConnectionGeometry = None
|
||||
boundary.ConnectionGeometry = None
|
||||
ifcopenshell.util.element.remove_deep2(file, geometry)
|
||||
history = settings["boundary"].OwnerHistory
|
||||
file.remove(settings["boundary"])
|
||||
history = boundary.OwnerHistory
|
||||
file.remove(boundary)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -28,9 +28,7 @@ def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance)
|
||||
to meet the objective of the constraint.
|
||||
|
||||
:param objective: The IfcObjective that this metric is a benchmark of.
|
||||
:type objective: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcMetric entity
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -40,10 +38,6 @@ def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance)
|
||||
metric = ifcopenshell.api.constraint.add_metric(model,
|
||||
objective=objective)
|
||||
"""
|
||||
settings = {
|
||||
"objective": objective,
|
||||
}
|
||||
|
||||
metric = file.create_entity(
|
||||
"IfcMetric",
|
||||
**{
|
||||
@@ -52,8 +46,9 @@ def add_metric(file: ifcopenshell.file, objective: ifcopenshell.entity_instance)
|
||||
"Benchmark": "EQUALTO",
|
||||
},
|
||||
)
|
||||
if settings["objective"]:
|
||||
benchmark_values = list(settings["objective"].BenchmarkValues or [])
|
||||
if objective:
|
||||
benchmark_values: list[ifcopenshell.entity_instance]
|
||||
benchmark_values = list(objective.BenchmarkValues or [])
|
||||
benchmark_values.append(metric)
|
||||
settings["objective"].BenchmarkValues = benchmark_values
|
||||
objective.BenchmarkValues = benchmark_values
|
||||
return metric
|
||||
|
||||
@@ -29,7 +29,6 @@ def add_objective(file: ifcopenshell.file) -> ifcopenshell.entity_instance:
|
||||
quantities. See ifcopenshell.api.constraint.add_metric for more information.
|
||||
|
||||
:return: The newly created IfcObjective entity
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -42,8 +41,6 @@ def add_objective(file: ifcopenshell.file) -> ifcopenshell.entity_instance:
|
||||
# Note: the objective right now is purely qualitative and for
|
||||
# information purposes. You may wish to add quantiative metrics.
|
||||
"""
|
||||
settings = {}
|
||||
|
||||
return file.create_entity(
|
||||
"IfcObjective", **{"Name": "Unnamed", "ConstraintGrade": "NOTDEFINED", "ObjectiveQualifier": "NOTDEFINED"}
|
||||
)
|
||||
|
||||
@@ -41,9 +41,7 @@ def remove_constraint(file: ifcopenshell.file, constraint: ifcopenshell.entity_i
|
||||
ifcopenshell.api.constraint.remove_constraint(model,
|
||||
constraint=objective)
|
||||
"""
|
||||
settings = {"constraint": constraint}
|
||||
|
||||
file.remove(settings["constraint"])
|
||||
file.remove(constraint)
|
||||
for rel in file.by_type("IfcRelAssociatesConstraint"):
|
||||
if not rel.RelatingConstraint:
|
||||
history = rel.OwnerHistory
|
||||
|
||||
@@ -29,9 +29,7 @@ def remove_context(file: ifcopenshell.file, context: ifcopenshell.entity_instanc
|
||||
removed. If a context is removed, then any subcontexts are also removed.
|
||||
|
||||
:param context: The IfcGeometricRepresentationContext entity to remove
|
||||
:type context: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -46,22 +44,20 @@ def remove_context(file: ifcopenshell.file, context: ifcopenshell.entity_instanc
|
||||
# Let's just get rid of it completely
|
||||
ifcopenshell.api.context.remove_context(model, context=body)
|
||||
"""
|
||||
settings = {"context": context}
|
||||
|
||||
for subcontext in settings["context"].HasSubContexts:
|
||||
for subcontext in context.HasSubContexts:
|
||||
ifcopenshell.api.context.remove_context(file, context=subcontext)
|
||||
|
||||
if getattr(settings["context"], "ParentContext", None):
|
||||
new = settings["context"].ParentContext
|
||||
for inverse in file.get_inverse(settings["context"]):
|
||||
if getattr(context, "ParentContext", None):
|
||||
new = context.ParentContext
|
||||
for inverse in file.get_inverse(context):
|
||||
if inverse.is_a("IfcCoordinateOperation"):
|
||||
inverse.SourceCRS = inverse.TargetCRS
|
||||
ifcopenshell.util.element.remove_deep(file, inverse)
|
||||
else:
|
||||
ifcopenshell.util.element.replace_attribute(inverse, settings["context"], new)
|
||||
file.remove(settings["context"])
|
||||
ifcopenshell.util.element.replace_attribute(inverse, context, new)
|
||||
file.remove(context)
|
||||
else:
|
||||
representations_in_context = settings["context"].RepresentationsInContext
|
||||
file.remove(settings["context"])
|
||||
representations_in_context = context.RepresentationsInContext
|
||||
file.remove(context)
|
||||
for element in representations_in_context:
|
||||
ifcopenshell.api.geometry.remove_representation(file, representation=element)
|
||||
|
||||
@@ -42,12 +42,9 @@ def assign_control(
|
||||
|
||||
:param relating_control: The IfcControl entity that is creating the
|
||||
control or constraint
|
||||
:type relating_control: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcObjectDefinition that is being controlled
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcRelAssignsToControl. If relationship already
|
||||
existed before and wasn't changed then returns None.
|
||||
:rtype: ifcopenshell.entity_instance, None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -72,25 +69,20 @@ def assign_control(
|
||||
ifcopenshell.api.control.assign_control(model,
|
||||
relating_control=cost_item, related_object=wall)
|
||||
"""
|
||||
settings = {
|
||||
"relating_control": relating_control,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
if settings["related_object"].HasAssignments:
|
||||
for assignment in settings["related_object"].HasAssignments:
|
||||
if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == settings["relating_control"]:
|
||||
if related_object.HasAssignments:
|
||||
for assignment in related_object.HasAssignments:
|
||||
if assignment.is_a("IfcRelAssignsToControl") and assignment.RelatingControl == relating_control:
|
||||
return
|
||||
|
||||
controls = None
|
||||
if settings["relating_control"].Controls:
|
||||
controls = settings["relating_control"].Controls[0]
|
||||
if relating_control.Controls:
|
||||
controls = relating_control.Controls[0]
|
||||
|
||||
if controls:
|
||||
if settings["related_object"] in controls.RelatedObjects:
|
||||
if related_object in controls.RelatedObjects:
|
||||
return
|
||||
related_objects = set(controls.RelatedObjects)
|
||||
related_objects.add(settings["related_object"])
|
||||
related_objects.add(related_object)
|
||||
controls.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": controls})
|
||||
else:
|
||||
@@ -99,8 +91,8 @@ def assign_control(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [settings["related_object"]],
|
||||
"RelatingControl": settings["relating_control"],
|
||||
"RelatedObjects": [related_object],
|
||||
"RelatingControl": relating_control,
|
||||
},
|
||||
)
|
||||
return controls
|
||||
|
||||
@@ -31,12 +31,9 @@ def unassign_control(
|
||||
|
||||
:param relating_control: The IfcControl entity that is creating the
|
||||
control or constraint
|
||||
:type relating_control: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcObjectDefinition that is being controlled
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: If the control still is related to other objects, the
|
||||
IfcRelAssignsToControl is returned, otherwise None.
|
||||
:rtype: ifcopenshell.entity_instance, None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -54,14 +51,8 @@ def unassign_control(
|
||||
ifcopenshell.api.control.unassign_control(model,
|
||||
relating_control=cost_item, related_object=wall)
|
||||
"""
|
||||
|
||||
settings = {
|
||||
"relating_control": relating_control,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
for rel in settings["related_object"].HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != settings["relating_control"]:
|
||||
for rel in related_object.HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToControl") or rel.RelatingControl != relating_control:
|
||||
continue
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
@@ -70,7 +61,7 @@ def unassign_control(
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(settings["related_object"])
|
||||
related_objects.remove(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
return rel
|
||||
|
||||
@@ -54,12 +54,9 @@ def add_cost_item_quantity(
|
||||
using another API call.
|
||||
|
||||
:param cost_item: The IfcCostItem to add the quantity to
|
||||
:type cost_item: ifcopenshell.entity_instance
|
||||
:param ifc_class: The type of quantity to add
|
||||
:type ifc_class: str, optional
|
||||
:return: The newly created quantity entity, chosen from the ifc_class
|
||||
parameter
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -76,20 +73,18 @@ def add_cost_item_quantity(
|
||||
ifcopenshell.api.cost.add_cost_item_quantity(model,
|
||||
cost_item=item, ifc_class="IfcQuantityCount")
|
||||
"""
|
||||
settings = {"cost_item": cost_item, "ifc_class": ifc_class}
|
||||
|
||||
quantity = file.create_entity(settings["ifc_class"], Name="Unnamed")
|
||||
quantity = file.create_entity(ifc_class, Name="Unnamed")
|
||||
# 3 IfcPhysicalSimpleQuantity Value
|
||||
# This is a bold assumption
|
||||
# https://forums.buildingsmart.org/t/how-does-a-cost-item-know-that-it-is-counting-a-controlled-product/3564
|
||||
if settings["ifc_class"] == "IfcQuantityCount":
|
||||
if ifc_class == "IfcQuantityCount":
|
||||
count = 0
|
||||
for rel in settings["cost_item"].Controls:
|
||||
for rel in cost_item.Controls:
|
||||
count += len(rel.RelatedObjects)
|
||||
quantity[3] = count
|
||||
else:
|
||||
quantity[3] = 0.0
|
||||
quantities = list(settings["cost_item"].CostQuantities or [])
|
||||
quantities = list(cost_item.CostQuantities or [])
|
||||
quantities.append(quantity)
|
||||
settings["cost_item"].CostQuantities = quantities
|
||||
cost_item.CostQuantities = quantities
|
||||
return quantity
|
||||
|
||||
@@ -39,13 +39,10 @@ def add_cost_schedule(
|
||||
managing any cost items.
|
||||
|
||||
:param name: The name of the cost schedule.
|
||||
:type name: str, optional
|
||||
:param predefined_type: The predefined type of the cost schedule, chosen
|
||||
from a valid type in the IFC documentation for
|
||||
IfcCostScheduleTypeEnum
|
||||
:type predefined_type: str, optional
|
||||
:return: The newly created IfcCostSchedule entity
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -55,13 +52,11 @@ def add_cost_schedule(
|
||||
# Now that we have a cost schedule, we may add cost items to it
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
"""
|
||||
settings = {"name": name, "predefined_type": predefined_type}
|
||||
|
||||
cost_schedule = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class="IfcCostSchedule",
|
||||
predefined_type=settings["predefined_type"],
|
||||
name=settings["name"],
|
||||
predefined_type=predefined_type,
|
||||
name=name,
|
||||
)
|
||||
if file.schema == "IFC2X3":
|
||||
cost_schedule.UpdateDate = createIfcDateAndTime(file, datetime.now())
|
||||
|
||||
@@ -46,9 +46,7 @@ def add_cost_value(file: ifcopenshell.file, parent: ifcopenshell.entity_instance
|
||||
:param parent: A parent IfcCostItem, if specifying a price directly to a
|
||||
cost item, or a top-level price component. Alternatively, this can
|
||||
be set to a IfcCostValue, if specifying price subcomponents.
|
||||
:type parent: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcCostValue
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -91,19 +89,17 @@ def add_cost_value(file: ifcopenshell.file, parent: ifcopenshell.entity_instance
|
||||
ifcopenshell.api.cost.edit_cost_value(model,
|
||||
cost_value=subvalue2, attributes={"AppliedValue": 3.0})
|
||||
"""
|
||||
settings = {"parent": parent}
|
||||
|
||||
value = file.create_entity("IfcCostValue")
|
||||
if settings["parent"].is_a("IfcCostItem"):
|
||||
values = list(settings["parent"].CostValues or [])
|
||||
if parent.is_a("IfcCostItem"):
|
||||
values = list(parent.CostValues or [])
|
||||
values.append(value)
|
||||
settings["parent"].CostValues = values
|
||||
elif settings["parent"].is_a("IfcConstructionResource"):
|
||||
values = list(settings["parent"].BaseCosts or [])
|
||||
parent.CostValues = values
|
||||
elif parent.is_a("IfcConstructionResource"):
|
||||
values = list(parent.BaseCosts or [])
|
||||
values.append(value)
|
||||
settings["parent"].BaseCosts = values
|
||||
elif settings["parent"].is_a("IfcCostValue"):
|
||||
values = list(settings["parent"].Components or [])
|
||||
parent.BaseCosts = values
|
||||
elif parent.is_a("IfcCostValue"):
|
||||
values = list(parent.Components or [])
|
||||
values.append(value)
|
||||
settings["parent"].Components = values
|
||||
parent.Components = values
|
||||
return value
|
||||
|
||||
@@ -36,11 +36,8 @@ def assign_cost_value(
|
||||
rates as a "template" to quickly populate your rates from.
|
||||
|
||||
:param cost_item: The IfcCostItem that you want to copy the values to
|
||||
:type cost_item: ifcopenshell.entity_instance
|
||||
:param cost_rate: The IfcCostItem that you want to copy the values from
|
||||
:type cost_rate: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -61,16 +58,14 @@ def assign_cost_value(
|
||||
# Now the cost item has the same rate as the one from the schedule of rate's item
|
||||
ifcopenshell.api.cost.assign_cost_value(model, cost_item=item, cost_rate=rate)
|
||||
"""
|
||||
settings = {"cost_item": cost_item, "cost_rate": cost_rate}
|
||||
|
||||
if settings["cost_item"].CostValues:
|
||||
if cost_item.CostValues:
|
||||
[
|
||||
ifcopenshell.api.cost.remove_cost_value(
|
||||
file,
|
||||
parent=settings["cost_item"],
|
||||
parent=cost_item,
|
||||
cost_value=cost_value,
|
||||
)
|
||||
for cost_value in settings["cost_item"].CostValues
|
||||
for cost_value in cost_item.CostValues
|
||||
]
|
||||
# This is an assumption, and not part of the official IFC documentation
|
||||
settings["cost_item"].CostValues = settings["cost_rate"].CostValues
|
||||
cost_item.CostValues = cost_rate.CostValues
|
||||
|
||||
@@ -83,13 +83,11 @@ def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcop
|
||||
# (42 * 200) + 50000 = 58400 is our calculated cost
|
||||
ifcopenshell.api.cost.calculate_cost_item_resource_value(model, cost_item=item)
|
||||
"""
|
||||
settings = {"cost_item": cost_item}
|
||||
|
||||
for cost_value in settings["cost_item"].CostValues or []:
|
||||
ifcopenshell.api.cost.remove_cost_value(file, parent=settings["cost_item"], cost_value=cost_value)
|
||||
for cost_value in cost_item.CostValues or []:
|
||||
ifcopenshell.api.cost.remove_cost_value(file, parent=cost_item, cost_value=cost_value)
|
||||
|
||||
resources = []
|
||||
for rel in settings["cost_item"].Controls or []:
|
||||
for rel in cost_item.Controls or []:
|
||||
for related_object in rel.RelatedObjects:
|
||||
if related_object.is_a("IfcConstructionResource"):
|
||||
resources.append(related_object)
|
||||
@@ -112,6 +110,6 @@ def calculate_cost_item_resource_value(file: ifcopenshell.file, cost_item: ifcop
|
||||
if unit and "day" in unit:
|
||||
quantity = quantity / 8 # Assume 8 hour working day - TODO implement resource calendar
|
||||
formula = "{}*{}".format(cost, quantity)
|
||||
cost_value = ifcopenshell.api.cost.add_cost_value(file, parent=settings["cost_item"])
|
||||
cost_value = ifcopenshell.api.cost.add_cost_value(file, parent=cost_item)
|
||||
cost_value.Name = resource.Name
|
||||
ifcopenshell.api.cost.edit_cost_value_formula(file, cost_value=cost_value, formula=formula)
|
||||
|
||||
@@ -29,9 +29,7 @@ def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_ins
|
||||
retained.
|
||||
|
||||
:param cost_item: The IfcCostItem entity you want to remove
|
||||
:type cost_item: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -41,15 +39,13 @@ def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_ins
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
ifcopenshell.api.cost.remove_cost_item(model, cost_item=item)
|
||||
"""
|
||||
settings = {"cost_item": cost_item}
|
||||
|
||||
# TODO: do a deep purge
|
||||
for inverse in file.get_inverse(settings["cost_item"]):
|
||||
for inverse in file.get_inverse(cost_item):
|
||||
if inverse.is_a("IfcRelNests"):
|
||||
if inverse.RelatingObject == settings["cost_item"]:
|
||||
if inverse.RelatingObject == cost_item:
|
||||
for related_object in inverse.RelatedObjects:
|
||||
ifcopenshell.api.cost.remove_cost_item(file, cost_item=related_object)
|
||||
elif inverse.RelatedObjects == (settings["cost_item"],):
|
||||
elif inverse.RelatedObjects == (cost_item,):
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
@@ -59,7 +55,7 @@ def remove_cost_item(file: ifcopenshell.file, cost_item: ifcopenshell.entity_ins
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
history = settings["cost_item"].OwnerHistory
|
||||
file.remove(settings["cost_item"])
|
||||
history = cost_item.OwnerHistory
|
||||
file.remove(cost_item)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -40,17 +40,15 @@ def remove_cost_schedule(file: ifcopenshell.file, cost_schedule: ifcopenshell.en
|
||||
item = ifcopenshell.api.cost.add_cost_item(model, cost_schedule=schedule)
|
||||
ifcopenshell.api.cost.remove_cost_schedule(model, cost_schedule=schedule)
|
||||
"""
|
||||
settings = {"cost_schedule": cost_schedule}
|
||||
|
||||
# TODO: do a deep purge
|
||||
for inverse in file.get_inverse(settings["cost_schedule"]):
|
||||
for inverse in file.get_inverse(cost_schedule):
|
||||
if inverse.is_a("IfcRelAssignsToControl"):
|
||||
[
|
||||
ifcopenshell.api.cost.remove_cost_item(file, cost_item=related_object)
|
||||
for related_object in inverse.RelatedObjects
|
||||
if related_object.is_a("IfcCostItem")
|
||||
]
|
||||
history = settings["cost_schedule"].OwnerHistory
|
||||
file.remove(settings["cost_schedule"])
|
||||
history = cost_schedule.OwnerHistory
|
||||
file.remove(cost_schedule)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -38,9 +38,7 @@ def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_inst
|
||||
|
||||
:param information: The IfcDocumentInformation that the reference will
|
||||
be created for
|
||||
:type information: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcDocumentReference entity
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -63,13 +61,11 @@ def add_reference(file: ifcopenshell.file, information: ifcopenshell.entity_inst
|
||||
ifcopenshell.api.document.edit_reference(model,
|
||||
reference=reference2, attributes={"Identification": "2.1.15"})
|
||||
"""
|
||||
settings = {"information": information}
|
||||
|
||||
if file.schema == "IFC2X3":
|
||||
reference = file.create_entity("IfcDocumentReference", ItemReference="X")
|
||||
if settings["information"]:
|
||||
references = list(settings["information"].DocumentReferences or [])
|
||||
if information:
|
||||
references = list(information.DocumentReferences or [])
|
||||
references.append(reference)
|
||||
settings["information"].DocumentReferences = references
|
||||
information.DocumentReferences = references
|
||||
return reference
|
||||
return file.create_entity("IfcDocumentReference", ReferencedDocument=settings["information"], Identification="X")
|
||||
return file.create_entity("IfcDocumentReference", ReferencedDocument=information, Identification="X")
|
||||
|
||||
@@ -30,12 +30,9 @@ def unassign_document(
|
||||
|
||||
:param product: The list of objects that the document reference or information is
|
||||
related to.
|
||||
:type product: list[ifcopenshell.entity_instance]
|
||||
:param document: The IfcDocumentReference (typically) or in rare cases
|
||||
the IfcDocumentInformation that is associated with the product
|
||||
:type document: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -54,27 +51,21 @@ def unassign_document(
|
||||
# Now let's change our mind and remove the association
|
||||
ifcopenshell.api.document.unassign_document(model, products=[storey], document=reference)
|
||||
"""
|
||||
settings = {
|
||||
"products": products,
|
||||
"document": document,
|
||||
}
|
||||
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
# NOTE: reuses code from `library.un assign_reference`
|
||||
|
||||
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||
products = set(settings["products"])
|
||||
for product in products:
|
||||
products_set = set(products)
|
||||
for product in products_set:
|
||||
reference_rels.update(product.HasAssociations)
|
||||
|
||||
reference_rels = {
|
||||
rel
|
||||
for rel in reference_rels
|
||||
if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == settings["document"]
|
||||
rel for rel in reference_rels if rel.is_a("IfcRelAssociatesDocument") and rel.RelatingDocument == document
|
||||
}
|
||||
|
||||
for rel in reference_rels:
|
||||
related_objects = set(rel.RelatedObjects) - products
|
||||
related_objects = set(rel.RelatedObjects) - products_set
|
||||
if related_objects:
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
|
||||
@@ -46,12 +46,9 @@ def assign_product(
|
||||
in 3D.
|
||||
|
||||
:param relating_product: The IfcProduct the object is related to
|
||||
:type relating_product: ifcopenshell.entity_instance
|
||||
:param related_object: The object (typically IfcAnnotation) that the
|
||||
product is related to
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: The created IfcRelAssignsToProduct relationship
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -62,42 +59,36 @@ def assign_product(
|
||||
ifcopenshell.api.drawing.assign_product(model,
|
||||
relating_product=furniture, related_object=annotation)
|
||||
"""
|
||||
settings = {
|
||||
"relating_product": relating_product,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
is_grid_axis = settings["relating_product"].is_a("IfcGridAxis")
|
||||
is_grid_axis = relating_product.is_a("IfcGridAxis")
|
||||
|
||||
if is_grid_axis:
|
||||
if settings["related_object"].HasAssignments:
|
||||
for rel in settings["related_object"].HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct") and rel.Name == settings["relating_product"].AxisTag:
|
||||
if related_object.HasAssignments:
|
||||
for rel in related_object.HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct") and rel.Name == relating_product.AxisTag:
|
||||
return
|
||||
elif settings["related_object"].HasAssignments:
|
||||
for rel in settings["related_object"].HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == settings["relating_product"]:
|
||||
elif related_object.HasAssignments:
|
||||
for rel in related_object.HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToProduct") and rel.RelatingProduct == relating_product:
|
||||
return
|
||||
|
||||
referenced_by = None
|
||||
|
||||
if is_grid_axis:
|
||||
axis = settings["relating_product"]
|
||||
axis = relating_product
|
||||
grid = None
|
||||
for attribute in ("PartOfW", "PartOfV", "PartOfU"):
|
||||
if getattr(axis, attribute, None):
|
||||
grid = getattr(axis, attribute)[0]
|
||||
settings["relating_product"] = grid
|
||||
for rel in grid.ReferencedBy:
|
||||
if rel.Name == axis.AxisTag:
|
||||
referenced_by = rel
|
||||
break
|
||||
elif settings["relating_product"].ReferencedBy:
|
||||
referenced_by = settings["relating_product"].ReferencedBy[0]
|
||||
elif relating_product.ReferencedBy:
|
||||
referenced_by = relating_product.ReferencedBy[0]
|
||||
|
||||
if referenced_by:
|
||||
related_objects = list(referenced_by.RelatedObjects)
|
||||
related_objects.append(settings["related_object"])
|
||||
related_objects.append(related_object)
|
||||
referenced_by.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": referenced_by})
|
||||
else:
|
||||
@@ -106,8 +97,8 @@ def assign_product(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [settings["related_object"]],
|
||||
"RelatingProduct": settings["relating_product"],
|
||||
"RelatedObjects": [related_object],
|
||||
"RelatingProduct": relating_product,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -34,12 +34,9 @@ def unassign_product(
|
||||
object later or leave the annotation as a "dumb" annotation.
|
||||
|
||||
:param relating_product: The IfcProduct the object is related to
|
||||
:type relating_product: ifcopenshell.entity_instance
|
||||
:param related_object: The object (typically IfcAnnotation) that the
|
||||
product is related to
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -54,13 +51,8 @@ def unassign_product(
|
||||
ifcopenshell.api.drawing.unassign_product(model,
|
||||
relating_product=furniture, related_object=annotation)
|
||||
"""
|
||||
settings = {
|
||||
"relating_product": relating_product,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
for rel in settings["related_object"].HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
|
||||
for rel in related_object.HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != relating_product:
|
||||
continue
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
@@ -69,6 +61,6 @@ def unassign_product(
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(settings["related_object"])
|
||||
related_objects.remove(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=rel)
|
||||
|
||||
@@ -33,11 +33,8 @@ def add_filling(
|
||||
filled.
|
||||
|
||||
:param opening: The IfcOpeningElement to fill with the element.
|
||||
:type opening: ifcopenshell.entity_instance
|
||||
:param element: The IfcElement to be inserted into the opening.
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: The new IfcRelFillsElement relationship
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -102,12 +99,10 @@ def add_filling(
|
||||
# The door will now fill the opening.
|
||||
ifcopenshell.api.feature.add_filling(model, opening=opening, element=door)
|
||||
"""
|
||||
settings = {"opening": opening, "element": element}
|
||||
|
||||
fills_voids = settings["element"].FillsVoids
|
||||
fills_voids = element.FillsVoids
|
||||
|
||||
if fills_voids:
|
||||
if fills_voids[0].RelatingOpeningElement == settings["opening"]:
|
||||
if fills_voids[0].RelatingOpeningElement == opening:
|
||||
return fills_voids[0]
|
||||
history = fills_voids[0].OwnerHistory
|
||||
file.remove(fills_voids[0])
|
||||
@@ -117,6 +112,6 @@ def add_filling(
|
||||
return file.create_entity(
|
||||
"IfcRelFillsElement",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
RelatingOpeningElement=settings["opening"],
|
||||
RelatedBuildingElement=settings["element"],
|
||||
RelatingOpeningElement=opening,
|
||||
RelatedBuildingElement=element,
|
||||
)
|
||||
|
||||
@@ -28,9 +28,7 @@ def remove_filling(file: ifcopenshell.file, element: ifcopenshell.entity_instanc
|
||||
fills the opening.
|
||||
|
||||
:param element: The element filling an opening.
|
||||
:type element: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -52,10 +50,8 @@ def remove_filling(file: ifcopenshell.file, element: ifcopenshell.entity_instanc
|
||||
# Not anymore!
|
||||
ifcopenshell.api.feature.remove_filling(model, element=door)
|
||||
"""
|
||||
settings = {"element": element}
|
||||
|
||||
for rel in file.by_type("IfcRelFillsElement"):
|
||||
if rel.RelatedBuildingElement == settings["element"]:
|
||||
if rel.RelatedBuildingElement == element:
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
|
||||
@@ -56,13 +56,10 @@ def add_axis_representation(
|
||||
:param context: The IfcGeometricRepresentationContext that the
|
||||
representation is part of. This must be either a
|
||||
Model/Axis/GRAPH_VIEW (3D) or Plan/Axis/GRAPH_VIEW (2D).
|
||||
:type context: ifcopenshell.entity_instance
|
||||
:param axis: The axis, as a list of two coordinates, the coordinates
|
||||
being either a list of 2 or 3 float coordinates depending on whether
|
||||
the axis is 2D or 3D.
|
||||
:type axis: list[list[float]]
|
||||
:return: The newly created IfcShapeRepresentation entity
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -29,20 +29,14 @@ def connect_element(
|
||||
related_element: ifcopenshell.entity_instance,
|
||||
description: Optional[str] = None,
|
||||
) -> ifcopenshell.entity_instance:
|
||||
settings = {
|
||||
"relating_element": relating_element,
|
||||
"related_element": related_element,
|
||||
"description": description,
|
||||
}
|
||||
|
||||
incompatible_connections = []
|
||||
|
||||
for rel in settings["relating_element"].ConnectedFrom:
|
||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == settings["related_element"]:
|
||||
for rel in relating_element.ConnectedFrom:
|
||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatingElement == related_element:
|
||||
incompatible_connections.append(rel)
|
||||
|
||||
for rel in settings["related_element"].ConnectedTo:
|
||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["relating_element"]:
|
||||
for rel in related_element.ConnectedTo:
|
||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == relating_element:
|
||||
incompatible_connections.append(rel)
|
||||
|
||||
if incompatible_connections:
|
||||
@@ -52,15 +46,15 @@ def connect_element(
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
for rel in settings["relating_element"].ConnectedTo:
|
||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == settings["related_element"]:
|
||||
rel.Description = settings["description"]
|
||||
for rel in relating_element.ConnectedTo:
|
||||
if rel.is_a() == "IfcRelConnectsElements" and rel.RelatedElement == related_element:
|
||||
rel.Description = description
|
||||
return rel
|
||||
|
||||
return file.createIfcRelConnectsElements(
|
||||
ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
|
||||
Description=settings["description"],
|
||||
RelatingElement=settings["relating_element"],
|
||||
RelatedElement=settings["related_element"],
|
||||
Description=description,
|
||||
RelatingElement=relating_element,
|
||||
RelatedElement=related_element,
|
||||
)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
from typing import Any
|
||||
|
||||
|
||||
def map_representation(
|
||||
@@ -25,15 +24,14 @@ def map_representation(
|
||||
) -> ifcopenshell.entity_instance:
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
usecase.settings = {"representation": representation}
|
||||
return usecase.execute()
|
||||
return usecase.execute(representation)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self) -> ifcopenshell.entity_instance:
|
||||
def execute(self, representation: ifcopenshell.entity_instance) -> ifcopenshell.entity_instance:
|
||||
self.representation = representation
|
||||
mapping_source = self.get_mapping_source()
|
||||
|
||||
zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
|
||||
@@ -46,15 +44,15 @@ class Usecase:
|
||||
return self.file.create_entity(
|
||||
"IfcShapeRepresentation",
|
||||
**{
|
||||
"ContextOfItems": self.settings["representation"].ContextOfItems,
|
||||
"RepresentationIdentifier": self.settings["representation"].RepresentationIdentifier,
|
||||
"ContextOfItems": representation.ContextOfItems,
|
||||
"RepresentationIdentifier": representation.RepresentationIdentifier,
|
||||
"RepresentationType": "MappedRepresentation",
|
||||
"Items": [mapped_item],
|
||||
}
|
||||
)
|
||||
|
||||
def get_mapping_source(self) -> ifcopenshell.entity_instance:
|
||||
for inverse in self.file.get_inverse(self.settings["representation"]):
|
||||
for inverse in self.file.get_inverse(self.representation):
|
||||
if inverse.is_a("IfcRepresentationMap"):
|
||||
return inverse
|
||||
zero = self.file.createIfcCartesianPoint((0.0, 0.0, 0.0))
|
||||
@@ -62,5 +60,5 @@ class Usecase:
|
||||
z_axis = self.file.createIfcDirection((0.0, 0.0, 1.0))
|
||||
mapping_origin = self.file.createIfcAxis2Placement3D(zero, z_axis, x_axis)
|
||||
return self.file.createIfcRepresentationMap(
|
||||
MappingOrigin=mapping_origin, MappedRepresentation=self.settings["representation"]
|
||||
MappingOrigin=mapping_origin, MappedRepresentation=self.representation
|
||||
)
|
||||
|
||||
@@ -35,12 +35,9 @@ def add_group(
|
||||
or structural load groups, which group together loads for structural
|
||||
analysis, or inventories, which are groups of assets.
|
||||
|
||||
:param Name: The name of the group. Defaults to "Unnamed"
|
||||
:type Name: str, optional
|
||||
:param name: The name of the group. Defaults to "Unnamed"
|
||||
:param description: The description of the purpose of the group.
|
||||
:type description: str, optional
|
||||
:return: The newly created IfcGroup
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -48,17 +45,11 @@ def add_group(
|
||||
|
||||
ifcopenshell.api.group.add_group(model, name="Unit 1A")
|
||||
"""
|
||||
settings = {
|
||||
"name": name or "Unnamed",
|
||||
"description": description,
|
||||
}
|
||||
|
||||
return file.create_entity(
|
||||
"IfcGroup",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"Name": settings["name"],
|
||||
"Description": settings["description"],
|
||||
}
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
|
||||
Name=name,
|
||||
Description=description,
|
||||
)
|
||||
|
||||
@@ -39,9 +39,7 @@ def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -
|
||||
group = ifcopenshell.api.group.add_group(model, name="Unit 1A")
|
||||
ifcopenshell.api.group.remove_group(model, group=group)
|
||||
"""
|
||||
settings = {"group": group}
|
||||
|
||||
for inverse_id in [i.id() for i in file.get_inverse(settings["group"])]:
|
||||
for inverse_id in [i.id() for i in file.get_inverse(group)]:
|
||||
try:
|
||||
inverse = file.by_id(inverse_id)
|
||||
except:
|
||||
@@ -49,11 +47,11 @@ def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
ifcopenshell.api.pset.remove_pset(
|
||||
file,
|
||||
product=settings["group"],
|
||||
product=group,
|
||||
pset=inverse.RelatingPropertyDefinition,
|
||||
)
|
||||
elif inverse.is_a("IfcRelAssignsToGroup"):
|
||||
if inverse.RelatingGroup == settings["group"]:
|
||||
if inverse.RelatingGroup == group:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
@@ -63,7 +61,7 @@ def remove_group(file: ifcopenshell.file, group: ifcopenshell.entity_instance) -
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
history = settings["group"].OwnerHistory
|
||||
file.remove(settings["group"])
|
||||
history = group.OwnerHistory
|
||||
file.remove(group)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -50,14 +50,10 @@ def add_reference(file: ifcopenshell.file, library: ifcopenshell.entity_instance
|
||||
ifcopenshell.api.library.edit_reference(model,
|
||||
reference=reference, attributes={"Identification": "http://example.org/digitaltwin#AHU01"})
|
||||
"""
|
||||
settings = {
|
||||
"library": library,
|
||||
}
|
||||
|
||||
if file.schema == "IFC2X3":
|
||||
reference = file.createIfcLibraryReference()
|
||||
references = list(settings["library"].LibraryReference or [])
|
||||
references = list(library.LibraryReference or [])
|
||||
references.append(reference)
|
||||
settings["library"].LibraryReference = references
|
||||
library.LibraryReference = references
|
||||
return reference
|
||||
return file.createIfcLibraryReference(ReferencedLibrary=settings["library"])
|
||||
return file.createIfcLibraryReference(ReferencedLibrary=library)
|
||||
|
||||
@@ -33,14 +33,11 @@ def assign_reference(
|
||||
detail about how references work.
|
||||
|
||||
:param products: The list of IfcProducts you want to associate with the reference
|
||||
:type products: list[ifcopenshell.entity_instance]
|
||||
:param reference: The IfcLibraryReference you want the product to be
|
||||
associated with.
|
||||
:type reference: ifcopenshell.entity_instance
|
||||
:return: The IfcRelAssociatesLibrary relationship entity
|
||||
or `None` if `products` was an empty list or all products were
|
||||
already assigned to the `reference`.
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -60,38 +57,33 @@ def assign_reference(
|
||||
# And now assign the IFC model's AHU with its Brickschema counterpart
|
||||
ifcopenshell.api.library.assign_reference(model, reference=reference, products=[ahu])
|
||||
"""
|
||||
settings = {
|
||||
"products": products,
|
||||
"reference": reference,
|
||||
}
|
||||
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
|
||||
referenced_elements = ifcopenshell.util.element.get_referenced_elements(settings["reference"])
|
||||
products: set[ifcopenshell.entity_instance] = set(settings["products"])
|
||||
products = products - referenced_elements
|
||||
referenced_elements = ifcopenshell.util.element.get_referenced_elements(reference)
|
||||
products_set: set[ifcopenshell.entity_instance] = set(products)
|
||||
products_set = products_set - referenced_elements
|
||||
|
||||
if not products:
|
||||
if not products_set:
|
||||
return
|
||||
|
||||
if file.schema == "IFC2X3":
|
||||
rel = next(
|
||||
(r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == settings["reference"]),
|
||||
(r for r in file.by_type("IfcRelAssociatesLibrary") if r.RelatingLibrary == reference),
|
||||
None,
|
||||
)
|
||||
else:
|
||||
rel = next(iter(settings["reference"].LibraryRefForObjects), None)
|
||||
rel = next(iter(reference.LibraryRefForObjects), None)
|
||||
|
||||
if not rel:
|
||||
return file.create_entity(
|
||||
"IfcRelAssociatesLibrary",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
OwnerHistory=ifcopenshell.api.owner.create_owner_history(file),
|
||||
RelatedObjects=list(products),
|
||||
RelatingLibrary=settings["reference"],
|
||||
RelatedObjects=list(products_set),
|
||||
RelatingLibrary=reference,
|
||||
)
|
||||
|
||||
related_objects = set(rel.RelatedObjects) | products
|
||||
related_objects = set(rel.RelatedObjects) | products_set
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=rel)
|
||||
return rel
|
||||
|
||||
@@ -31,11 +31,8 @@ def unassign_reference(
|
||||
If the product isn't assigned to the reference, nothing will happen.
|
||||
|
||||
:param reference: The IfcLibraryReference to unassign from
|
||||
:type reference: ifcopenshell.entity_instance
|
||||
:param products: A list of IfcProduct elements to unassign from the reference
|
||||
:type products: list[ifcopenshell.entity_instance]
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -58,24 +55,19 @@ def unassign_reference(
|
||||
# Let's change our mind and unassign it.
|
||||
ifcopenshell.api.library.unassign_reference(model, reference=reference, products=[ahu])
|
||||
"""
|
||||
|
||||
settings = {"reference": reference, "products": products}
|
||||
|
||||
# TODO: do we need to support non-ifcroot elements like we do in classification.add_reference?
|
||||
|
||||
reference_rels: set[ifcopenshell.entity_instance] = set()
|
||||
products = set(settings["products"])
|
||||
for product in products:
|
||||
products_set = set(products)
|
||||
for product in products_set:
|
||||
reference_rels.update(product.HasAssociations)
|
||||
|
||||
reference_rels = {
|
||||
rel
|
||||
for rel in reference_rels
|
||||
if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == settings["reference"]
|
||||
rel for rel in reference_rels if rel.is_a("IfcRelAssociatesLibrary") and rel.RelatingLibrary == reference
|
||||
}
|
||||
|
||||
for rel in reference_rels:
|
||||
related_objects = set(rel.RelatedObjects) - products
|
||||
related_objects = set(rel.RelatedObjects) - products_set
|
||||
if related_objects:
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
|
||||
@@ -40,11 +40,8 @@ def add_list_item(
|
||||
|
||||
:param material_list: The IfcMaterialList the material should be added
|
||||
to.
|
||||
:type material_list: ifcopenshell.entity_instance
|
||||
:param material: The IfcMaterial to add to the list
|
||||
:type material: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -80,8 +77,6 @@ def add_list_item(
|
||||
# aluminium and glass.
|
||||
ifcopenshell.api.material.assign_material(model, products=[window_type], material=material_set)
|
||||
"""
|
||||
settings = {"material_list": material_list, "material": material}
|
||||
|
||||
materials = list(settings["material_list"].Materials or [])
|
||||
materials.append(settings["material"])
|
||||
settings["material_list"].Materials = materials
|
||||
materials = list(material_list.Materials or [])
|
||||
materials.append(material)
|
||||
material_list.Materials = materials
|
||||
|
||||
@@ -58,13 +58,9 @@ def add_material(
|
||||
|
||||
:param name: The name of the material, typically tagged in a finishes
|
||||
drawing or schedule.
|
||||
:type name: str, optional
|
||||
:param category: The category of the material.
|
||||
:type category: str, optional
|
||||
:param description: A description of the material.
|
||||
:type description: str, optional
|
||||
:return: The newly created IfcMaterial
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -81,11 +77,9 @@ def add_material(
|
||||
# "Style" has been specified.
|
||||
ifcopenshell.api.material.assign_material(model, products=[concrete_bench], material=concrete)
|
||||
"""
|
||||
settings = {"name": name or "Unnamed", "category": category, "description": description}
|
||||
|
||||
material = file.create_entity("IfcMaterial", **{"Name": settings["name"] or "Unnamed"})
|
||||
if settings["category"]:
|
||||
material.Category = settings["category"]
|
||||
if settings["description"]:
|
||||
material.Description = settings["description"]
|
||||
material = file.create_entity("IfcMaterial", **{"Name": name or "Unnamed"})
|
||||
if category:
|
||||
material.Category = category
|
||||
if description:
|
||||
material.Description = description
|
||||
return material
|
||||
|
||||
@@ -68,14 +68,11 @@ def add_material_set(
|
||||
|
||||
:param name: The name of the material set, which may be purely
|
||||
descriptive or annotated in drawings. Defaults to "Unnamed".
|
||||
:type name: str, optional
|
||||
:param set_type: What type of set you want to create, chosen from
|
||||
IfcMaterialLayerSet, IfcMaterialProfileSet,
|
||||
IfcMaterialConstituentSet, or IfcMaterialList. Defaults to
|
||||
IfcMaterialConstituentSet.
|
||||
:type set_type: str, optional
|
||||
:return: The newly created material set element
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -113,10 +110,8 @@ def add_material_set(
|
||||
# Great! Let's assign our material set to our wall type.
|
||||
ifcopenshell.api.material.assign_material(model, products=[wall_type], material=material_set)
|
||||
"""
|
||||
settings = {"name": name or "Unnamed", "set_type": set_type}
|
||||
|
||||
if settings["set_type"] == "IfcMaterialLayerSet":
|
||||
return file.create_entity("IfcMaterialLayerSet", LayerSetName=settings["name"] or "Unnamed")
|
||||
elif settings["set_type"] == "IfcMaterialList":
|
||||
if set_type == "IfcMaterialLayerSet":
|
||||
return file.create_entity("IfcMaterialLayerSet", LayerSetName=name or "Unnamed")
|
||||
elif set_type == "IfcMaterialList":
|
||||
return file.create_entity("IfcMaterialList")
|
||||
return file.create_entity(settings["set_type"], Name=settings["name"] or "Unnamed")
|
||||
return file.create_entity(set_type, Name=name or "Unnamed")
|
||||
|
||||
@@ -29,9 +29,7 @@ def remove_material(file: ifcopenshell.file, material: ifcopenshell.entity_insta
|
||||
take care of this situation themselves.
|
||||
|
||||
:param material: The IfcMaterial entity you want to remove
|
||||
:type material: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -43,10 +41,8 @@ def remove_material(file: ifcopenshell.file, material: ifcopenshell.entity_insta
|
||||
# ... and remove it
|
||||
ifcopenshell.api.material.remove_material(model, material=aluminium)
|
||||
"""
|
||||
settings = {"material": material}
|
||||
|
||||
inverse_elements = file.get_inverse(settings["material"])
|
||||
file.remove(settings["material"])
|
||||
inverse_elements = file.get_inverse(material)
|
||||
file.remove(material)
|
||||
# TODO: Right now, we we choose only to delete set items (e.g. a layer) but not the material set
|
||||
# This can lead to invalid material sets, but we assume the user will deal with it
|
||||
for inverse in inverse_elements:
|
||||
|
||||
@@ -29,9 +29,7 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
|
||||
|
||||
:param material: The IfcMaterialLayerSet, IfcMaterialConstituentSet,
|
||||
IfcMaterialProfileSet entity you want to remove.
|
||||
:type material: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -55,20 +53,18 @@ def remove_material_set(file: ifcopenshell.file, material: ifcopenshell.entity_i
|
||||
ifcopenshell.api.material.remove_material_set(model, material=material_set)
|
||||
"""
|
||||
|
||||
settings = {"material": material}
|
||||
|
||||
inverse_elements = file.get_inverse(settings["material"])
|
||||
if settings["material"].is_a("IfcMaterialLayerSet"):
|
||||
set_items = settings["material"].MaterialLayers or []
|
||||
elif settings["material"].is_a("IfcMaterialProfileSet"):
|
||||
set_items = settings["material"].MaterialProfiles or []
|
||||
elif settings["material"].is_a("IfcMaterialConstituentSet"):
|
||||
set_items = settings["material"].MaterialConstituents or []
|
||||
elif settings["material"].is_a("IfcMaterialList"):
|
||||
inverse_elements = file.get_inverse(material)
|
||||
if material.is_a("IfcMaterialLayerSet"):
|
||||
set_items = material.MaterialLayers or []
|
||||
elif material.is_a("IfcMaterialProfileSet"):
|
||||
set_items = material.MaterialProfiles or []
|
||||
elif material.is_a("IfcMaterialConstituentSet"):
|
||||
set_items = material.MaterialConstituents or []
|
||||
elif material.is_a("IfcMaterialList"):
|
||||
set_items = []
|
||||
for set_item in set_items:
|
||||
file.remove(set_item)
|
||||
file.remove(settings["material"])
|
||||
file.remove(material)
|
||||
for inverse in inverse_elements:
|
||||
if inverse.is_a("IfcRelAssociatesMaterial"):
|
||||
history = inverse.OwnerHistory
|
||||
|
||||
@@ -49,11 +49,8 @@ def add_actor(
|
||||
IfcPerson if it is a sole individual, or an IfcPersonAndOrganization
|
||||
if a specific person is liable within an organisation and must be
|
||||
legally nominated.
|
||||
:type actor: ifcopenshell.entity_instance
|
||||
:param ifc_class: Either "IfcActor" or "IfcOccupant".
|
||||
:type ifc_class: str, optional
|
||||
:return: The newly created IfcActor or IfcOccupant
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -67,8 +64,7 @@ def add_actor(
|
||||
# Assign that organisation to a newly created actor
|
||||
actor = ifcopenshell.api.owner.add_actor(model, actor=organisation)
|
||||
"""
|
||||
settings = {"actor": actor, "ifc_class": ifc_class or "IfcActor"}
|
||||
|
||||
actor = ifcopenshell.api.root.create_entity(file, ifc_class=settings["ifc_class"])
|
||||
actor.TheActor = settings["actor"]
|
||||
return actor
|
||||
ifc_class = ifc_class or "IfcActor"
|
||||
actor_ = ifcopenshell.api.root.create_entity(file, ifc_class=ifc_class)
|
||||
actor_.TheActor = actor
|
||||
return actor_
|
||||
|
||||
@@ -39,12 +39,9 @@ def add_address(
|
||||
|
||||
:param assigned_object: The IfcOrganization or IfcPerson the contact
|
||||
address belongs to.
|
||||
:type assigned_object: ifcopenshell.entity_instance
|
||||
:param ifc_class: Either IfcPostalAddress or IfcTelecomAddress. Defaults
|
||||
to IfcPostalAddress.
|
||||
:type ifc_class: str, optional
|
||||
:return: The new IfcPostalAddress or IfcTelecomAddress
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -67,10 +64,8 @@ def add_address(
|
||||
"ElectronicMailAddresses": ["bobthebuilder@example.com"],
|
||||
"WWWHomePageURL": "https://thinkmoult.com"})
|
||||
"""
|
||||
settings = {"assigned_object": assigned_object, "ifc_class": ifc_class}
|
||||
|
||||
address = file.create_entity(settings["ifc_class"], "OFFICE")
|
||||
addresses = list(settings["assigned_object"].Addresses) if settings["assigned_object"].Addresses else []
|
||||
address = file.create_entity(ifc_class, "OFFICE")
|
||||
addresses = list(assigned_object.Addresses) if assigned_object.Addresses else []
|
||||
addresses.append(address)
|
||||
settings["assigned_object"].Addresses = addresses
|
||||
assigned_object.Addresses = addresses
|
||||
return address
|
||||
|
||||
@@ -31,11 +31,8 @@ def add_organisation(
|
||||
Sometimes used in drawing naming schemes. Otherise used as a
|
||||
canonicalised way of computers to identify the organisation. Like
|
||||
their stock name.
|
||||
:type identification: str, optional
|
||||
:param name: The legal name of the organisation
|
||||
:type name: str, optional
|
||||
:return: The newly created IfcOrganization
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -44,11 +41,9 @@ def add_organisation(
|
||||
organisation = ifcopenshell.api.owner.add_organisation(model,
|
||||
identification="AWB", name="Architects Without Ballpens")
|
||||
"""
|
||||
settings = {"identification": identification, "name": name}
|
||||
|
||||
data = {"Name": settings["name"]}
|
||||
data = {"Name": name}
|
||||
if file.schema == "IFC2X3":
|
||||
data["Id"] = settings["identification"]
|
||||
data["Id"] = identification
|
||||
else:
|
||||
data["Identification"] = settings["identification"]
|
||||
data["Identification"] = identification
|
||||
return file.create_entity("IfcOrganization", **data)
|
||||
|
||||
@@ -31,13 +31,9 @@ def add_person(
|
||||
|
||||
:param identification: The computer readable unique identification of
|
||||
the person. For example, their username in a CDE or alias.
|
||||
:type identification: str, optional
|
||||
:param family_name: The family name
|
||||
:type family_name: str, optional
|
||||
:param given_name: The given name
|
||||
:type given_name: str, optional
|
||||
:return: The newly created IfcPerson
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -46,15 +42,9 @@ def add_person(
|
||||
ifcopenshell.api.owner.add_person(model,
|
||||
identification="bobthebuilder", family_name="Thebuilder", given_name="Bob")
|
||||
"""
|
||||
settings = {
|
||||
"identification": identification,
|
||||
"family_name": family_name,
|
||||
"given_name": given_name,
|
||||
}
|
||||
|
||||
data = {"FamilyName": settings["family_name"], "GivenName": settings["given_name"]}
|
||||
data = {"FamilyName": family_name, "GivenName": given_name}
|
||||
if file.schema == "IFC2X3":
|
||||
data["Id"] = settings["identification"]
|
||||
data["Id"] = identification
|
||||
else:
|
||||
data["Identification"] = settings["identification"]
|
||||
data["Identification"] = identification
|
||||
return file.create_entity("IfcPerson", **data)
|
||||
|
||||
@@ -30,11 +30,8 @@ def add_person_and_organisation(
|
||||
|
||||
:param person: The IfcPerson being the representative of the
|
||||
organisation.
|
||||
:type person: ifcopenshell.entity_instance
|
||||
:param organisation: The IfcOrganization it
|
||||
:type organisation: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcPersonAndOrganization
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -48,6 +45,4 @@ def add_person_and_organisation(
|
||||
ifcopenshell.api.owner.add_person_and_organisation(model,
|
||||
person=person, organisation=organisation)
|
||||
"""
|
||||
settings = {"person": person, "organisation": organisation}
|
||||
|
||||
return file.createIfcPersonAndOrganization(settings["person"], settings["organisation"])
|
||||
return file.create_entity("IfcPersonAndOrganization", person, organisation)
|
||||
|
||||
@@ -43,11 +43,8 @@ def assign_actor(
|
||||
ifcopenshell.api.resource.assign_resource.
|
||||
|
||||
:param relating_actor: The IfcActor who is responsible for the object.
|
||||
:type relating_actor: ifcopenshell.entity_instance
|
||||
:param related_object: The object the actor is responsible for.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcRelAssignsToActor relationship.
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -74,24 +71,19 @@ def assign_actor(
|
||||
ifcopenshell.api.owner.assign_actor(model,
|
||||
relating_actor=manufacturer, related_object=pump_type)
|
||||
"""
|
||||
settings = {
|
||||
"relating_actor": relating_actor,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
if settings["related_object"].HasAssignments:
|
||||
for rel in settings["related_object"].HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == settings["relating_actor"]:
|
||||
if related_object.HasAssignments:
|
||||
for rel in related_object.HasAssignments:
|
||||
if rel.is_a("IfcRelAssignsToActor") and rel.RelatingActor == relating_actor:
|
||||
return rel
|
||||
|
||||
rel = None
|
||||
|
||||
if settings["relating_actor"].IsActingUpon:
|
||||
rel = settings["relating_actor"].IsActingUpon[0]
|
||||
if relating_actor.IsActingUpon:
|
||||
rel = relating_actor.IsActingUpon[0]
|
||||
|
||||
if rel:
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.append(settings["related_object"])
|
||||
related_objects.append(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
else:
|
||||
@@ -100,8 +92,8 @@ def assign_actor(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [settings["related_object"]],
|
||||
"RelatingActor": settings["relating_actor"],
|
||||
"RelatedObjects": [related_object],
|
||||
"RelatingActor": relating_actor,
|
||||
}
|
||||
)
|
||||
return rel
|
||||
|
||||
@@ -24,9 +24,7 @@ def remove_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance) -
|
||||
"""Removes an actor
|
||||
|
||||
:param actor: The IfcActor to remove.
|
||||
:type actor: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -44,9 +42,7 @@ def remove_actor(file: ifcopenshell.file, actor: ifcopenshell.entity_instance) -
|
||||
# Actually we need ballpens on this project
|
||||
ifcopenshell.api.owner.remove_actor(model, actor=actor)
|
||||
"""
|
||||
settings = {"actor": actor}
|
||||
|
||||
history = settings["actor"].OwnerHistory
|
||||
file.remove(settings["actor"])
|
||||
history = actor.OwnerHistory
|
||||
file.remove(actor)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -25,9 +25,7 @@ def remove_address(file: ifcopenshell.file, address: ifcopenshell.entity_instanc
|
||||
relationship removed.
|
||||
|
||||
:param address: The IfcAddress to remove.
|
||||
:type address: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -40,10 +38,8 @@ def remove_address(file: ifcopenshell.file, address: ifcopenshell.entity_instanc
|
||||
# Change our mind and delete it
|
||||
ifcopenshell.api.owner.remove_address(model, address=address)
|
||||
"""
|
||||
settings = {"address": address}
|
||||
|
||||
for inverse in file.get_inverse(settings["address"]):
|
||||
for inverse in file.get_inverse(address):
|
||||
if inverse.is_a() in ("IfcOrganization", "IfcPerson"):
|
||||
if inverse.Addresses == (settings["address"],):
|
||||
if inverse.Addresses == (address,):
|
||||
inverse.Addresses = None
|
||||
file.remove(settings["address"])
|
||||
file.remove(address)
|
||||
|
||||
@@ -25,9 +25,7 @@ def remove_application(file: ifcopenshell.file, application: ifcopenshell.entity
|
||||
Check whether or not the application is used anywhere prior to removal.
|
||||
|
||||
:param address: The IfcApplication to remove.
|
||||
:type address: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -36,6 +34,4 @@ def remove_application(file: ifcopenshell.file, application: ifcopenshell.entity
|
||||
application = ifcopenshell.api.owner.add_application(model)
|
||||
ifcopenshell.api.owner.remove_address(model, application=application)
|
||||
"""
|
||||
settings = {"application": application}
|
||||
|
||||
file.remove(settings["application"])
|
||||
file.remove(application)
|
||||
|
||||
@@ -28,9 +28,7 @@ def remove_person_and_organisation(
|
||||
the "person and organisation" group.
|
||||
|
||||
:param person_and_organisation: The IfcPersonAndOrganization to remove.
|
||||
:type person_and_organisation: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -46,17 +44,15 @@ def remove_person_and_organisation(
|
||||
|
||||
ifcopenshell.api.owner.remove_person_and_organisation(model, person_and_organisation=user)
|
||||
"""
|
||||
settings = {"person_and_organisation": person_and_organisation}
|
||||
|
||||
for inverse in file.get_inverse(settings["person_and_organisation"]):
|
||||
for inverse in file.get_inverse(person_and_organisation):
|
||||
if inverse.is_a("IfcDocumentInformation"):
|
||||
if inverse.Editors == (settings["person_and_organisation"],):
|
||||
if inverse.Editors == (person_and_organisation,):
|
||||
inverse.Editors = None
|
||||
elif inverse.is_a("IfcActor"):
|
||||
ifcopenshell.api.root.remove_product(file, product=inverse)
|
||||
elif inverse.is_a("IfcResourceLevelRelationship"):
|
||||
if inverse.RelatedResourceObjects == (settings["person_and_organisation"],):
|
||||
if inverse.RelatedResourceObjects == (person_and_organisation,):
|
||||
file.remove(inverse)
|
||||
elif inverse.is_a("IfcOwnerHistory"):
|
||||
file.remove(inverse)
|
||||
file.remove(settings["person_and_organisation"])
|
||||
file.remove(person_and_organisation)
|
||||
|
||||
@@ -25,9 +25,7 @@ def remove_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance) ->
|
||||
leave some of them without roles.
|
||||
|
||||
:param role: The IfcActorRole to remove.
|
||||
:type role: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -40,13 +38,11 @@ def remove_role(file: ifcopenshell.file, role: ifcopenshell.entity_instance) ->
|
||||
# After running this, the organisation will have no role again
|
||||
ifcopenshell.api.owner.remove_role(model, role=role)
|
||||
"""
|
||||
settings = {"role": role}
|
||||
|
||||
for inverse in file.get_inverse(settings["role"]):
|
||||
for inverse in file.get_inverse(role):
|
||||
if inverse.is_a() in ("IfcOrganization", "IfcPerson", "IfcPersonAndOrganization"):
|
||||
if inverse.Roles == (settings["role"],):
|
||||
if inverse.Roles == (role,):
|
||||
inverse.Roles = None
|
||||
elif inverse.is_a("IfcResourceLevelRelationship") and not inverse.is_a("IfcOrganizationRelationship"):
|
||||
if inverse.RelatedResourceObjects == (settings["organisation"],):
|
||||
if inverse.RelatedResourceObjects == (organisation,):
|
||||
file.remove(inverse)
|
||||
file.remove(settings["role"])
|
||||
file.remove(role)
|
||||
|
||||
@@ -29,11 +29,8 @@ def unassign_actor(
|
||||
This means that the actor is no longer responsible for the object.
|
||||
|
||||
:param relating_actor: The IfcActor who is responsible for the object.
|
||||
:type relating_actor: ifcopenshell.entity_instance
|
||||
:param related_object: The object the actor is responsible for.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -55,13 +52,8 @@ def unassign_actor(
|
||||
ifcopenshell.api.owner.unassign_actor(model,
|
||||
relating_actor=manufacturer, related_object=pump_type)
|
||||
"""
|
||||
settings = {
|
||||
"relating_actor": relating_actor,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
for rel in settings["related_object"].HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != settings["relating_actor"]:
|
||||
for rel in related_object.HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToActor") or rel.RelatingActor != relating_actor:
|
||||
continue
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
@@ -70,6 +62,6 @@ def unassign_actor(
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(settings["related_object"])
|
||||
related_objects.remove(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
|
||||
@@ -59,9 +59,6 @@ def update_owner_history(
|
||||
# API calls either.
|
||||
ifcopenshell.api.attribute.edit_attributes(model, product=space, attributes={"Name": "Lobby"})
|
||||
"""
|
||||
settings = {"element": element}
|
||||
|
||||
element = settings["element"]
|
||||
if not element.is_a("IfcRoot"):
|
||||
return
|
||||
user = ifcopenshell.api.owner.settings.get_user(file)
|
||||
|
||||
@@ -30,9 +30,7 @@ def add_parameterized_profile(file: ifcopenshell.file, ifc_class: str) -> ifcope
|
||||
|
||||
:param ifc_class: The subclass of IfcParameterizedProfileDef that you'd
|
||||
like to create.
|
||||
:type ifc_class: str
|
||||
:return: The newly created element depending on the specified ifc_class.
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -42,6 +40,4 @@ def add_parameterized_profile(file: ifcopenshell.file, ifc_class: str) -> ifcope
|
||||
ifc_class="IfcCircleProfileDef")
|
||||
circle.Radius = 1.
|
||||
"""
|
||||
settings = {"ifc_class": ifc_class}
|
||||
|
||||
return file.create_entity(settings["ifc_class"])
|
||||
return file.create_entity(ifc_class)
|
||||
|
||||
@@ -35,11 +35,10 @@ def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instanc
|
||||
circle = 1.
|
||||
ifcopenshell.api.profile.remove_profile(model, profile=circle)
|
||||
"""
|
||||
settings = {"profile": profile}
|
||||
is_ifc2x3 = file.schema == "IFC2X3"
|
||||
|
||||
subelements = set()
|
||||
for attribute in settings["profile"]:
|
||||
for attribute in profile:
|
||||
if isinstance(attribute, ifcopenshell.entity_instance):
|
||||
subelements.add(attribute)
|
||||
|
||||
@@ -56,6 +55,6 @@ def remove_profile(file: ifcopenshell.file, profile: ifcopenshell.entity_instanc
|
||||
for pset in profile_psets:
|
||||
ifcopenshell.api.pset.remove_pset(file, product=profile, pset=pset)
|
||||
|
||||
file.remove(settings["profile"])
|
||||
file.remove(profile)
|
||||
for subelement in subelements:
|
||||
ifcopenshell.util.element.remove_deep2(file, subelement)
|
||||
|
||||
@@ -50,9 +50,7 @@ def create_file(version: ifcopenshell.util.schema.IFC_SCHEMA = "IFC4") -> ifcope
|
||||
|
||||
# ... and off we go!
|
||||
"""
|
||||
settings = {"version": version}
|
||||
|
||||
file = ifcopenshell.file(schema=settings["version"])
|
||||
file = ifcopenshell.file(schema=version)
|
||||
file.wrapped_data.header.file_name.name = "/dev/null" # Hehehe
|
||||
file.wrapped_data.header.file_name.time_stamp = (
|
||||
datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc).astimezone().replace(microsecond=0).isoformat()
|
||||
|
||||
@@ -28,11 +28,8 @@ def remove_pset(
|
||||
All properties that are part of this property set are also removed.
|
||||
|
||||
:param product: The IfcObject to remove the property set from.
|
||||
:type product: ifcopenshell.entity_instance
|
||||
:param pset: The IfcPropertySet or IfcElementQuantity to remove.
|
||||
:type pset: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -45,27 +42,25 @@ def remove_pset(
|
||||
# Remove it!
|
||||
ifcopenshell.api.pset.remove_pset(model, product=wall_type, pset=pset)
|
||||
"""
|
||||
settings = {"product": product, "pset": pset}
|
||||
|
||||
to_purge = []
|
||||
should_remove_pset = True
|
||||
for inverse in file.get_inverse(settings["pset"]):
|
||||
for inverse in file.get_inverse(pset):
|
||||
if inverse.is_a("IfcRelDefinesByProperties"):
|
||||
if not inverse.RelatedObjects or len(inverse.RelatedObjects) == 1:
|
||||
to_purge.append(inverse)
|
||||
else:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(settings["product"])
|
||||
related_objects.remove(product)
|
||||
inverse.RelatedObjects = related_objects
|
||||
should_remove_pset = False
|
||||
if should_remove_pset:
|
||||
properties = [] # Predefined psets have no properties
|
||||
if settings["pset"].is_a("IfcPropertySet"):
|
||||
properties = settings["pset"].HasProperties or []
|
||||
elif settings["pset"].is_a("IfcQuantitySet"):
|
||||
properties = settings["pset"].Quantities or []
|
||||
elif settings["pset"].is_a() in ("IfcMaterialProperties", "IfcProfileProperties"):
|
||||
properties = settings["pset"].Properties or []
|
||||
if pset.is_a("IfcPropertySet"):
|
||||
properties = pset.HasProperties or []
|
||||
elif pset.is_a("IfcQuantitySet"):
|
||||
properties = pset.Quantities or []
|
||||
elif pset.is_a() in ("IfcMaterialProperties", "IfcProfileProperties"):
|
||||
properties = pset.Properties or []
|
||||
for prop in properties:
|
||||
if file.get_total_inverses(prop) != 1:
|
||||
continue
|
||||
@@ -75,8 +70,8 @@ def remove_pset(
|
||||
file.remove(enumeration)
|
||||
file.remove(prop)
|
||||
# IfcMaterialProperties and IfcProfileProperties don't have OwnerHistory
|
||||
history = getattr(settings["pset"], "OwnerHistory", None)
|
||||
file.remove(settings["pset"])
|
||||
history = getattr(pset, "OwnerHistory", None)
|
||||
file.remove(pset)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
for element in to_purge:
|
||||
|
||||
@@ -71,19 +71,15 @@ def add_pset_template(
|
||||
overridden by occurrences, and is applicable to everything.
|
||||
|
||||
:param name: The name of the property set
|
||||
:type name: str,optional
|
||||
:param template_type: Choose from one of PSET_TYPEDRIVENONLY,
|
||||
PSET_TYPEDRIVENOVERRIDE, PSET_OCCURRENCEDRIVEN,
|
||||
PSET_PERFORMANCEDRIVEN, QTO_TYPEDRIVENONLY, QTO_TYPEDRIVENOVERRIDE,
|
||||
QTO_OCCURRENCEDRIVEN, NOTDEFINED
|
||||
:type template_type: str,optional
|
||||
:param applicable_entity: The entity that this template is allowed to be
|
||||
applied to. For example, IfcWall means that the property set may be
|
||||
assigned to walls only. IfcTypeObject, the default, means that the
|
||||
property set may be assigned to any type.
|
||||
:type applicable_entity: str,optional
|
||||
:return: The newly created IfcPropertySetTemplate
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -99,12 +95,10 @@ def add_pset_template(
|
||||
name="HighVoltage", description="Whether there is a risk of high voltage.",
|
||||
primary_measure_type="IfcBoolean")
|
||||
"""
|
||||
settings = {"name": name, "template_type": template_type, "applicable_entity": applicable_entity}
|
||||
|
||||
return file.create_entity(
|
||||
"IfcPropertySetTemplate",
|
||||
GlobalId=ifcopenshell.guid.new(),
|
||||
Name=settings["name"],
|
||||
TemplateType=settings["template_type"],
|
||||
ApplicableEntity=settings["applicable_entity"],
|
||||
Name=name,
|
||||
TemplateType=template_type,
|
||||
ApplicableEntity=applicable_entity,
|
||||
)
|
||||
|
||||
@@ -27,9 +27,7 @@ def remove_prop_template(file: ifcopenshell.file, prop_template: ifcopenshell.en
|
||||
templates.
|
||||
|
||||
:param prop_template: The IfcSimplePropertyTemplate to remove.
|
||||
:type prop_template: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -44,13 +42,11 @@ def remove_prop_template(file: ifcopenshell.file, prop_template: ifcopenshell.en
|
||||
# Let's remove the second one.
|
||||
ifcopenshell.api.pset_template.remove_prop_template(model, prop_template=prop2)
|
||||
"""
|
||||
settings = {"prop_template": prop_template}
|
||||
|
||||
for inverse in file.get_inverse(settings["prop_template"]):
|
||||
for inverse in file.get_inverse(prop_template):
|
||||
if len(inverse.HasPropertyTemplates) == 1:
|
||||
inverse.HasPropertyTemplates = []
|
||||
else:
|
||||
has_property_templates = list(inverse.HasPropertyTemplates)
|
||||
has_property_templates.remove(settings["prop_template"])
|
||||
has_property_templates.remove(prop_template)
|
||||
inverse.HasPropertyTemplates = has_property_templates
|
||||
ifcopenshell.util.element.remove_deep(file, settings["prop_template"])
|
||||
ifcopenshell.util.element.remove_deep(file, prop_template)
|
||||
|
||||
@@ -26,9 +26,7 @@ def remove_pset_template(file: ifcopenshell.file, pset_template: ifcopenshell.en
|
||||
along with it.
|
||||
|
||||
:param pset_template: The IfcPropertySetTemplate to remove.
|
||||
:type pset_template: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -40,6 +38,4 @@ def remove_pset_template(file: ifcopenshell.file, pset_template: ifcopenshell.en
|
||||
# Let's remove the template.
|
||||
ifcopenshell.api.pset_template.remove_pset_template(model, pset_template=template)
|
||||
"""
|
||||
settings = {"pset_template": pset_template}
|
||||
|
||||
ifcopenshell.util.element.remove_deep(file, settings["pset_template"])
|
||||
ifcopenshell.util.element.remove_deep(file, pset_template)
|
||||
|
||||
@@ -51,20 +51,15 @@ def add_resource(
|
||||
|
||||
:param parent_resource: If this is a child resource (typically to a crew
|
||||
resource), then nominate the parent IfcConstructionResource here.
|
||||
:type parent_resource: ifcopenshell.entity_instance, optional
|
||||
:param ifc_class: The class of resource chosen from
|
||||
IfcConstructionEquipmentResource, IfcConstructionMaterialResource,
|
||||
IfcConstructionProductResource, IfcCrewResource, IfcLaborResource,
|
||||
or IfcSubContractResource.
|
||||
:type ifc_class: str,optional
|
||||
:param name: The name of the resource
|
||||
:type name: str,optional
|
||||
:param predefined_type: Consult the IFC documentation for the valid
|
||||
predefined types for each type of resource class.
|
||||
:type predefined_type: str,optional
|
||||
:return: The newly created resource depending on the nominated IFC
|
||||
class.
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -76,25 +71,17 @@ def add_resource(
|
||||
# Add some labour to our crew.
|
||||
ifcopenshell.api.resource.add_resource(model, parent_resource=crew, ifc_class="IfcLaborResource")
|
||||
"""
|
||||
settings = {
|
||||
"parent_resource": parent_resource,
|
||||
"ifc_class": ifc_class,
|
||||
"name": name,
|
||||
"predefined_type": predefined_type,
|
||||
}
|
||||
|
||||
resource = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class=settings["ifc_class"],
|
||||
predefined_type=settings["predefined_type"],
|
||||
name=settings["name"] or "Unnamed",
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
name=name or "Unnamed",
|
||||
)
|
||||
# TODO: this is an ambiguity by buildingSMART: Can we nest an IfcCrewResource under an IfcCrewResource ?
|
||||
# https://forums.buildingsmart.org/t/what-are-allowed-to-be-root-level-construction-resources/3550
|
||||
if settings["parent_resource"]:
|
||||
ifcopenshell.api.nest.assign_object(
|
||||
file, related_objects=[resource], relating_object=settings["parent_resource"]
|
||||
)
|
||||
if parent_resource:
|
||||
ifcopenshell.api.nest.assign_object(file, related_objects=[resource], relating_object=parent_resource)
|
||||
elif file.schema != "IFC2X3":
|
||||
context = file.by_type("IfcContext")[0]
|
||||
ifcopenshell.api.project.assign_declaration(file, definitions=[resource], relating_context=context)
|
||||
|
||||
@@ -42,12 +42,9 @@ def assign_resource(
|
||||
(e.g. if the resource is a labour resource).
|
||||
|
||||
:param relating_resource: The IfcResource to assign the object to.
|
||||
:type relating_resource: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcProduct or IfcActor to assign to the
|
||||
object.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcRelAssignsToResource
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -82,26 +79,18 @@ def assign_resource(
|
||||
# This means that UCO is now our crane operator.
|
||||
ifcopenshell.api.resource.assign_resource(model, relating_resource=crane, related_object=actor)
|
||||
"""
|
||||
settings = {
|
||||
"relating_resource": relating_resource,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
if settings["related_object"].HasAssignments:
|
||||
for assignment in settings["related_object"].HasAssignments:
|
||||
if (
|
||||
assignment.is_a("IfclRelAssignsToResource")
|
||||
and assignment.RelatingResource == settings["relating_resource"]
|
||||
):
|
||||
if related_object.HasAssignments:
|
||||
for assignment in related_object.HasAssignments:
|
||||
if assignment.is_a("IfclRelAssignsToResource") and assignment.RelatingResource == relating_resource:
|
||||
return assignment
|
||||
|
||||
resource_of = None
|
||||
if settings["relating_resource"].ResourceOf:
|
||||
resource_of = settings["relating_resource"].ResourceOf[0]
|
||||
if relating_resource.ResourceOf:
|
||||
resource_of = relating_resource.ResourceOf[0]
|
||||
|
||||
if resource_of:
|
||||
related_objects = list(resource_of.RelatedObjects)
|
||||
related_objects.append(settings["related_object"])
|
||||
related_objects.append(related_object)
|
||||
resource_of.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": resource_of})
|
||||
else:
|
||||
@@ -110,8 +99,8 @@ def assign_resource(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [settings["related_object"]],
|
||||
"RelatingResource": settings["relating_resource"],
|
||||
"RelatedObjects": [related_object],
|
||||
"RelatingResource": relating_resource,
|
||||
}
|
||||
)
|
||||
return resource_of
|
||||
|
||||
@@ -25,15 +25,18 @@ import ifcopenshell.util.resource
|
||||
|
||||
|
||||
def calculate_resource_usage(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> None:
|
||||
"""Calculates the number of resources required to perform scheduled work on a task."""
|
||||
settings = {"resource": resource}
|
||||
"""Calculates the number of resources required to perform scheduled work on a task.
|
||||
|
||||
if ifcopenshell.util.constraint.is_attribute_locked(settings["resource"], "Usage.ScheduleUsage"):
|
||||
:param resource: The IfcConstructionResource to calculate the usage for.
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if ifcopenshell.util.constraint.is_attribute_locked(resource, "Usage.ScheduleUsage"):
|
||||
return
|
||||
if not settings["resource"].Usage or not settings["resource"].Usage.ScheduleWork:
|
||||
if not resource.Usage or not resource.Usage.ScheduleWork:
|
||||
return
|
||||
|
||||
task = ifcopenshell.util.resource.get_task_assignments(settings["resource"])
|
||||
task = ifcopenshell.util.resource.get_task_assignments(resource)
|
||||
if not task or not task.TaskTime:
|
||||
return
|
||||
|
||||
@@ -46,7 +49,7 @@ def calculate_resource_usage(file: ifcopenshell.file, resource: ifcopenshell.ent
|
||||
seconds = task_duration.days * hours_per_day * 60 * 60
|
||||
seconds += task_duration.seconds
|
||||
|
||||
person_hours = ifcopenshell.util.date.ifc2datetime(settings["resource"].Usage.ScheduleWork)
|
||||
person_hours = ifcopenshell.util.date.ifc2datetime(resource.Usage.ScheduleWork)
|
||||
|
||||
required_resources = person_hours.total_seconds() / seconds
|
||||
settings["resource"].Usage.ScheduleUsage = float(required_resources)
|
||||
resource.Usage.ScheduleUsage = float(required_resources)
|
||||
|
||||
@@ -22,6 +22,9 @@ import ifcopenshell.util.element
|
||||
def remove_resource_quantity(file: ifcopenshell.file, resource: ifcopenshell.entity_instance) -> None:
|
||||
"""Removes the base quantity of a resource
|
||||
|
||||
:param resource: The IfcConstructionResource to remove the quantity from.
|
||||
:return: None
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: python
|
||||
@@ -41,9 +44,7 @@ def remove_resource_quantity(file: ifcopenshell.file, resource: ifcopenshell.ent
|
||||
# let's clean up our mess and remove the quantity.
|
||||
ifcopenshell.api.resource.remove_resource_quantity(model, resource=labour)
|
||||
"""
|
||||
settings = {"resource": resource}
|
||||
|
||||
old_quantity = settings["resource"].BaseQuantity
|
||||
settings["resource"].BaseQuantity = None
|
||||
old_quantity = resource.BaseQuantity
|
||||
resource.BaseQuantity = None
|
||||
if old_quantity:
|
||||
ifcopenshell.util.element.remove_deep(file, old_quantity)
|
||||
|
||||
@@ -29,12 +29,9 @@ def unassign_resource(
|
||||
"""Removes the relationship between a resource and object
|
||||
|
||||
:param relating_resource: The IfcResource to assign the object to.
|
||||
:type relating_resource: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcProduct or IfcActor to assign to the
|
||||
object.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -60,13 +57,8 @@ def unassign_resource(
|
||||
ifcopenshell.api.resource.unassign_resource(model,
|
||||
relating_resource=crane, related_object=product)
|
||||
"""
|
||||
settings = {
|
||||
"relating_resource": relating_resource,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
for rel in settings["related_object"].HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != settings["relating_resource"]:
|
||||
for rel in related_object.HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToResource") or rel.RelatingResource != relating_resource:
|
||||
continue
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
@@ -75,6 +67,6 @@ def unassign_resource(
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(settings["related_object"])
|
||||
related_objects.remove(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
|
||||
@@ -71,48 +71,44 @@ def create_entity(
|
||||
"""
|
||||
usecase = Usecase()
|
||||
usecase.file = file
|
||||
usecase.settings = {
|
||||
"ifc_class": ifc_class,
|
||||
"predefined_type": predefined_type,
|
||||
"name": name,
|
||||
}
|
||||
return usecase.execute()
|
||||
return usecase.execute(ifc_class, predefined_type, name)
|
||||
|
||||
|
||||
class Usecase:
|
||||
file: ifcopenshell.file
|
||||
settings: dict[str, Any]
|
||||
|
||||
def execute(self):
|
||||
def execute(
|
||||
self, ifc_class: str, predefined_type: Optional[str] = None, name: Optional[str] = None
|
||||
) -> ifcopenshell.entity_instance:
|
||||
element = self.file.create_entity(
|
||||
self.settings["ifc_class"],
|
||||
ifc_class,
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(self.file),
|
||||
}
|
||||
)
|
||||
element.Name = self.settings["name"] or None
|
||||
if self.settings["predefined_type"]:
|
||||
element.Name = name or None
|
||||
if predefined_type:
|
||||
if hasattr(element, "PredefinedType"):
|
||||
try:
|
||||
element.PredefinedType = self.settings["predefined_type"]
|
||||
element.PredefinedType = predefined_type
|
||||
except:
|
||||
element.PredefinedType = "USERDEFINED"
|
||||
if hasattr(element, "ObjectType"):
|
||||
element.ObjectType = self.settings["predefined_type"]
|
||||
element.ObjectType = predefined_type
|
||||
elif hasattr(element, "ElementType"):
|
||||
element.ElementType = self.settings["predefined_type"]
|
||||
element.ElementType = predefined_type
|
||||
elif hasattr(element, "ProcessType"):
|
||||
element.ProcessType = self.settings["predefined_type"]
|
||||
element.ProcessType = predefined_type
|
||||
elif hasattr(element, "ObjectType"):
|
||||
element.ObjectType = self.settings["predefined_type"]
|
||||
element.ObjectType = predefined_type
|
||||
if self.file.schema == "IFC2X3":
|
||||
self.handle_2x3_defaults(element)
|
||||
else:
|
||||
self.handle_4_defaults(element)
|
||||
return element
|
||||
|
||||
def handle_2x3_defaults(self, element):
|
||||
def handle_2x3_defaults(self, element: ifcopenshell.entity_instance) -> None:
|
||||
if element.is_a("IfcElementType"):
|
||||
if hasattr(element, "PredefinedType") and not element.PredefinedType:
|
||||
element.PredefinedType = "NOTDEFINED"
|
||||
@@ -129,7 +125,7 @@ class Usecase:
|
||||
element.ParameterTakesPrecedence = False
|
||||
element.Sizeable = False
|
||||
|
||||
def handle_4_defaults(self, element):
|
||||
def handle_4_defaults(self, element: ifcopenshell.entity_instance) -> None:
|
||||
if element.is_a("IfcElementType"):
|
||||
if hasattr(element, "PredefinedType") and not element.PredefinedType:
|
||||
element.PredefinedType = "NOTDEFINED"
|
||||
|
||||
@@ -69,23 +69,16 @@ def add_task(
|
||||
:param work_schedule: The work schedule to group the task in, if the
|
||||
task is to be a top-level or root task. This is mutually exclusive
|
||||
with the parent_task parameter.
|
||||
:type work_schedule: ifcopenshell.entity_instance, optional
|
||||
:param parent_task: The parent task, if the task is to be a subtask or
|
||||
child task. This is mutually exclusive with the work_schedule
|
||||
parameter.
|
||||
:type parent_task: ifcopenshell.entity_instance, optioanl
|
||||
:param name: The name of the task.
|
||||
:type name: str,optional
|
||||
:param description: The description of the task.
|
||||
:type description: str,optional
|
||||
:param identification: The identification code of the task.
|
||||
:type identification: str,optional
|
||||
:param predefined_type: The predefined type of the task. Common ones
|
||||
include CONSTRUCTION, DEMOLITION, or MAINTENANCE. Consultant the
|
||||
IFC documentation for IfcTaskTypeEnum for more information.
|
||||
:type predefined_type: str
|
||||
:return: The newly created IfcTask
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -139,39 +132,28 @@ def add_task(
|
||||
ifcopenshell.api.sequence.add_task(model, parent_task=cleaning, identification="3",
|
||||
description="Setup the water pressure by tapping to a water supply and connecting to a ...")
|
||||
"""
|
||||
settings = {
|
||||
"work_schedule": work_schedule,
|
||||
"parent_task": parent_task,
|
||||
"name": name,
|
||||
"description": description,
|
||||
"identification": identification,
|
||||
"predefined_type": predefined_type,
|
||||
}
|
||||
|
||||
task = ifcopenshell.api.root.create_entity(
|
||||
file, ifc_class="IfcTask", name=settings["name"], predefined_type=settings["predefined_type"]
|
||||
)
|
||||
if settings["description"]:
|
||||
task.Description = settings["description"]
|
||||
if settings["identification"]:
|
||||
task.Identification = settings["identification"]
|
||||
task = ifcopenshell.api.root.create_entity(file, ifc_class="IfcTask", name=name, predefined_type=predefined_type)
|
||||
if description:
|
||||
task.Description = description
|
||||
if identification:
|
||||
task.Identification = identification
|
||||
task.IsMilestone = False
|
||||
if settings["work_schedule"]:
|
||||
if work_schedule:
|
||||
file.create_entity(
|
||||
"IfcRelAssignsToControl",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [task],
|
||||
"RelatingControl": settings["work_schedule"],
|
||||
"RelatingControl": work_schedule,
|
||||
}
|
||||
)
|
||||
elif settings["parent_task"]:
|
||||
elif parent_task:
|
||||
rel = ifcopenshell.api.nest.assign_object(
|
||||
file,
|
||||
related_objects=[task],
|
||||
relating_object=settings["parent_task"],
|
||||
relating_object=parent_task,
|
||||
)
|
||||
if file.schema != "IFC2X3" and settings["parent_task"].Identification:
|
||||
task.Identification = settings["parent_task"].Identification + "." + str(len(rel.RelatedObjects))
|
||||
if file.schema != "IFC2X3" and parent_task.Identification:
|
||||
task.Identification = parent_task.Identification + "." + str(len(rel.RelatedObjects))
|
||||
return task
|
||||
|
||||
@@ -44,17 +44,13 @@ def add_time_period(
|
||||
|
||||
:param recurrence_pattern: The IfcRecurrencePattern to add the time
|
||||
period to. See ifcopenshell.api.sequence.assign_recurrence_pattern.
|
||||
:type recurrence_pattern: ifcopenshell.entity_instance
|
||||
:param start_time: The start time of the time period, in a format
|
||||
compatible with IfcTime, such as an ISO format time string or a
|
||||
datetime.time object.
|
||||
:type start_time: str,datetime.time
|
||||
:param end_time: The end time of the time period, in a format
|
||||
compatible with IfcTime, such as an ISO format time string or a
|
||||
datetime.time object.
|
||||
:type end_time: str,datetime.time
|
||||
:return: The newly created IfcTimePeriod
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -81,18 +77,12 @@ def add_time_period(
|
||||
ifcopenshell.api.sequence.add_time_period(model,
|
||||
recurrence_pattern=pattern, start_time="13:00", end_time="17:00")
|
||||
"""
|
||||
settings = {
|
||||
"recurrence_pattern": recurrence_pattern,
|
||||
"start_time": start_time,
|
||||
"end_time": end_time,
|
||||
}
|
||||
|
||||
time_period = file.create_entity("IfcTimePeriod")
|
||||
time_period.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcTime")
|
||||
time_period.EndTime = ifcopenshell.util.date.datetime2ifc(settings["end_time"], "IfcTime")
|
||||
time_periods = list(settings["recurrence_pattern"].TimePeriods or [])
|
||||
time_period.StartTime = ifcopenshell.util.date.datetime2ifc(start_time, "IfcTime")
|
||||
time_period.EndTime = ifcopenshell.util.date.datetime2ifc(end_time, "IfcTime")
|
||||
time_periods = list(recurrence_pattern.TimePeriods or [])
|
||||
time_periods.append(time_period)
|
||||
settings["recurrence_pattern"].TimePeriods = time_periods
|
||||
recurrence_pattern.TimePeriods = time_periods
|
||||
|
||||
ifcopenshell.util.sequence.is_working_day.cache_clear()
|
||||
ifcopenshell.util.sequence.is_calendar_applicable.cache_clear()
|
||||
|
||||
@@ -38,12 +38,10 @@ def add_work_calendar(
|
||||
|
||||
:param name: The name of the calendar. Typically something like
|
||||
"5 Day Working Week" or "24/7".
|
||||
:type name: str, optional
|
||||
:param predefined_type: The type of calendar, typically used to more
|
||||
specifically define shifts, such as FIRSTSHIFT, SECONDSHIFT, or
|
||||
THIRDSHIFT. Leave as NOTDEFINED for basic calendar usage.
|
||||
:return: The newly created IfcWorkCalendar
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -79,13 +77,11 @@ def add_work_calendar(
|
||||
# this calendar by default (though you can override them).
|
||||
ifcopenshell.api.control.assign_control(model, relating_control=calendar, related_object=task)
|
||||
"""
|
||||
settings = {"name": name, "predefined_type": predefined_type}
|
||||
|
||||
work_calendar = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class="IfcWorkCalendar",
|
||||
predefined_type=settings["predefined_type"],
|
||||
name=settings["name"],
|
||||
predefined_type=predefined_type,
|
||||
name=name,
|
||||
)
|
||||
context = file.by_type("IfcContext")[0]
|
||||
ifcopenshell.api.project.assign_declaration(
|
||||
|
||||
@@ -41,15 +41,11 @@ def add_work_plan(
|
||||
|
||||
:param name: The name of the work plan. Recommended to be "Maintenance"
|
||||
or "Construction" for the two main purposes.
|
||||
:type name: str, optional
|
||||
:param predefined_type: The type of work plan, used for baselining.
|
||||
Leave as "NOTDEFINED" if unsure.
|
||||
:type predefined_type: str
|
||||
:param start_time: The earliest start time when the schedules grouped
|
||||
within the work plan are relevant.
|
||||
:type start_time: str,datetime.time
|
||||
:return: The newly created IfcWorkPlan
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -62,23 +58,18 @@ def add_work_plan(
|
||||
schedule = ifcopenshell.api.sequence.add_work_schedule(model,
|
||||
name="Construction Schedule A", work_plan=work_plan)
|
||||
"""
|
||||
settings = {
|
||||
"name": name,
|
||||
"predefined_type": predefined_type,
|
||||
"start_time": start_time or datetime.now(),
|
||||
}
|
||||
|
||||
start_time = start_time or datetime.now()
|
||||
work_plan = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class="IfcWorkPlan",
|
||||
predefined_type=settings["predefined_type"],
|
||||
name=settings["name"],
|
||||
predefined_type=predefined_type,
|
||||
name=name,
|
||||
)
|
||||
work_plan.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
|
||||
user = ifcopenshell.api.owner.settings.get_user(file)
|
||||
if user:
|
||||
work_plan.Creators = [user.ThePerson]
|
||||
work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
|
||||
work_plan.StartTime = ifcopenshell.util.date.datetime2ifc(start_time, "IfcDateTime")
|
||||
|
||||
context = file.by_type("IfcContext")[0]
|
||||
ifcopenshell.api.project.assign_declaration(
|
||||
|
||||
@@ -77,19 +77,12 @@ def add_work_schedule(
|
||||
construction = ifcopenshell.api.sequence.add_task(model,
|
||||
work_schedule=schedule, name="Construction", identification="C")
|
||||
"""
|
||||
settings = {
|
||||
"name": name,
|
||||
"predefined_type": predefined_type,
|
||||
"object_type": object_type,
|
||||
"start_time": start_time or datetime.now(),
|
||||
"work_plan": work_plan,
|
||||
}
|
||||
|
||||
start_time = start_time or datetime.now()
|
||||
work_schedule = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class="IfcWorkSchedule",
|
||||
predefined_type=settings["predefined_type"],
|
||||
name=settings["name"],
|
||||
predefined_type=predefined_type,
|
||||
name=name,
|
||||
)
|
||||
if file.schema == "IFC2X3":
|
||||
work_schedule.CreationDate = createIfcDateAndTime(file, datetime.now())
|
||||
@@ -99,17 +92,17 @@ def add_work_schedule(
|
||||
if user:
|
||||
work_schedule.Creators = [user.ThePerson]
|
||||
if file.schema == "IFC2X3":
|
||||
work_schedule.StartTime = createIfcDateAndTime(file, settings["start_time"])
|
||||
work_schedule.StartTime = createIfcDateAndTime(file, start_time)
|
||||
else:
|
||||
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(settings["start_time"], "IfcDateTime")
|
||||
if settings["object_type"]:
|
||||
work_schedule.ObjectType = settings["object_type"]
|
||||
if settings["work_plan"]:
|
||||
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(start_time, "IfcDateTime")
|
||||
if object_type:
|
||||
work_schedule.ObjectType = object_type
|
||||
if work_plan:
|
||||
ifcopenshell.api.aggregate.assign_object(
|
||||
file,
|
||||
**{
|
||||
"products": [work_schedule],
|
||||
"relating_object": settings["work_plan"],
|
||||
"relating_object": work_plan,
|
||||
}
|
||||
)
|
||||
elif file.schema != "IFC2X3":
|
||||
|
||||
@@ -36,12 +36,9 @@ def add_work_time(
|
||||
|
||||
:param work_calendar: The IfcWorkCalendar to add the work or holiday
|
||||
time definition to.
|
||||
:type work_calendar: ifcopenshell.entity_instance
|
||||
:param time_type: Either WorkingTimes or ExceptionTimes, depending on
|
||||
what you want to define.
|
||||
:type time_type: str
|
||||
:return: The newly created IfcWorkTime
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -74,15 +71,13 @@ def add_work_time(
|
||||
ifcopenshell.api.sequence.edit_recurrence_pattern(model,
|
||||
recurrence_pattern=pattern, attributes={"DayComponent": [1], "MonthComponent": [1]})
|
||||
"""
|
||||
settings = {"work_calendar": work_calendar, "time_type": time_type}
|
||||
|
||||
work_time = file.create_entity("IfcWorkTime")
|
||||
if settings["time_type"] == "WorkingTimes":
|
||||
working_times = list(settings["work_calendar"].WorkingTimes or [])
|
||||
if time_type == "WorkingTimes":
|
||||
working_times = list(work_calendar.WorkingTimes or [])
|
||||
working_times.append(work_time)
|
||||
settings["work_calendar"].WorkingTimes = working_times
|
||||
elif settings["time_type"] == "ExceptionTimes":
|
||||
exception_times = list(settings["work_calendar"].ExceptionTimes or [])
|
||||
work_calendar.WorkingTimes = working_times
|
||||
elif time_type == "ExceptionTimes":
|
||||
exception_times = list(work_calendar.ExceptionTimes or [])
|
||||
exception_times.append(work_time)
|
||||
settings["work_calendar"].ExceptionTimes = exception_times
|
||||
work_calendar.ExceptionTimes = exception_times
|
||||
return work_time
|
||||
|
||||
@@ -65,12 +65,9 @@ def assign_process(
|
||||
|
||||
:param relating_process: The IfcProcess (typically IfcTask) that the
|
||||
input, control, or resource is related to.
|
||||
:type relating_process: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcProduct (for input), IfcCostItem (for
|
||||
control) or IfcConstructionResource (for resource).
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcRelAssignsToProcess relationship
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -91,23 +88,18 @@ def assign_process(
|
||||
# Let's demolish that wall!
|
||||
ifcopenshell.api.sequence.assign_process(model, relating_process=task, related_object=wall)
|
||||
"""
|
||||
settings = {
|
||||
"relating_process": relating_process,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
if settings["related_object"].HasAssignments:
|
||||
for assignment in settings["related_object"].HasAssignments:
|
||||
if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == settings["relating_process"]:
|
||||
if related_object.HasAssignments:
|
||||
for assignment in related_object.HasAssignments:
|
||||
if assignment.is_a("IfcRelAssignsToProcess") and assignment.RelatingProcess == relating_process:
|
||||
return
|
||||
|
||||
operates_on = None
|
||||
if settings["relating_process"].OperatesOn:
|
||||
operates_on = settings["relating_process"].OperatesOn[0]
|
||||
if relating_process.OperatesOn:
|
||||
operates_on = relating_process.OperatesOn[0]
|
||||
|
||||
if operates_on:
|
||||
related_objects = list(operates_on.RelatedObjects)
|
||||
related_objects.append(settings["related_object"])
|
||||
related_objects.append(related_object)
|
||||
operates_on.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": operates_on})
|
||||
else:
|
||||
@@ -116,8 +108,8 @@ def assign_process(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [settings["related_object"]],
|
||||
"RelatingProcess": settings["relating_process"],
|
||||
"RelatedObjects": [related_object],
|
||||
"RelatingProcess": relating_process,
|
||||
}
|
||||
)
|
||||
return operates_on
|
||||
|
||||
@@ -41,12 +41,9 @@ def assign_product(
|
||||
|
||||
:param relating_product: The IfcProduct that was constructed as a result
|
||||
of the task.
|
||||
:type relating_product: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcProcess (typically IfcTask) of the
|
||||
construction task.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: The newly created IfcRelAssignsToProduct relationship
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -67,23 +64,18 @@ def assign_product(
|
||||
# Let's construct that wall!
|
||||
ifcopenshell.api.sequence.assign_product(model, relating_product=wall, related_object=task)
|
||||
"""
|
||||
settings = {
|
||||
"relating_product": relating_product,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
if settings["related_object"].HasAssignments:
|
||||
for assignment in settings["related_object"].HasAssignments:
|
||||
if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == settings["relating_product"]:
|
||||
if related_object.HasAssignments:
|
||||
for assignment in related_object.HasAssignments:
|
||||
if assignment.is_a("IfcRelAssignsToProduct") and assignment.RelatingProduct == relating_product:
|
||||
return assignment
|
||||
|
||||
referenced_by = None
|
||||
if settings["relating_product"].ReferencedBy:
|
||||
referenced_by = settings["relating_product"].ReferencedBy[0]
|
||||
if relating_product.ReferencedBy:
|
||||
referenced_by = relating_product.ReferencedBy[0]
|
||||
|
||||
if referenced_by:
|
||||
related_objects = list(referenced_by.RelatedObjects)
|
||||
related_objects.append(settings["related_object"])
|
||||
related_objects.append(related_object)
|
||||
referenced_by.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": referenced_by})
|
||||
else:
|
||||
@@ -92,8 +84,8 @@ def assign_product(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedObjects": [settings["related_object"]],
|
||||
"RelatingProduct": settings["relating_product"],
|
||||
"RelatedObjects": [related_object],
|
||||
"RelatingProduct": relating_product,
|
||||
}
|
||||
)
|
||||
return referenced_by
|
||||
|
||||
@@ -19,11 +19,12 @@
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api.project
|
||||
import ifcopenshell.api.aggregate
|
||||
from typing import Union
|
||||
|
||||
|
||||
def assign_work_plan(
|
||||
file: ifcopenshell.file, work_schedule: ifcopenshell.entity_instance, work_plan: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
) -> Union[ifcopenshell.entity_instance, None]:
|
||||
"""Assigns a work schedule to a work plan
|
||||
|
||||
Typically, work schedules would be assigned to a work plan at creation.
|
||||
@@ -31,11 +32,8 @@ def assign_work_plan(
|
||||
|
||||
:param work_schedule: The IfcWorkSchedule that will be assigned to the
|
||||
work plan.
|
||||
:type work_schedule: ifcopenshell.entity_instance
|
||||
:param work_plan: The IfcWorkPlan for the schedule to be assigned to.
|
||||
:type work_plan: ifcopenshell.entity_instance
|
||||
:return: The IfcRelAggregates relationship
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -50,20 +48,16 @@ def assign_work_plan(
|
||||
# ... you can assign the work plan afterwards.
|
||||
ifcopenshell.api.sequence.assign_work_plan(work_schedule=schedule, work_plan=work_plan)
|
||||
"""
|
||||
settings = {"work_schedule": work_schedule, "work_plan": work_plan}
|
||||
|
||||
# TODO: this is an ambiguity by buildingSMART
|
||||
# See https://forums.buildingsmart.org/t/is-the-ifcworkschedule-project-declaration-mutually-exclusive-to-aggregation-within-a-relating-ifcworkplan/3510
|
||||
ifcopenshell.api.project.unassign_declaration(
|
||||
file,
|
||||
definitions=[settings["work_schedule"]],
|
||||
definitions=[work_schedule],
|
||||
relating_context=file.by_type("IfcContext")[0],
|
||||
)
|
||||
rel_aggregates = ifcopenshell.api.aggregate.assign_object(
|
||||
file,
|
||||
**{
|
||||
"products": [settings["work_schedule"]],
|
||||
"relating_object": settings["work_plan"],
|
||||
}
|
||||
products=[work_schedule],
|
||||
relating_object=work_plan,
|
||||
)
|
||||
return rel_aggregates
|
||||
|
||||
@@ -21,6 +21,7 @@ import ifcopenshell.guid
|
||||
import ifcopenshell.api.nest
|
||||
import ifcopenshell.api.owner
|
||||
import ifcopenshell.api.sequence
|
||||
import ifcopenshell.util.date
|
||||
import ifcopenshell.util.element
|
||||
import ifcopenshell.util.sequence
|
||||
from typing import Union, Any
|
||||
@@ -155,7 +156,9 @@ class Usecase:
|
||||
duration_type=inverse.TimeLag.DurationType,
|
||||
)
|
||||
|
||||
def create_object_reference(self, relating_object, related_object):
|
||||
def create_object_reference(
|
||||
self, relating_object: ifcopenshell.entity_instance, related_object: ifcopenshell.entity_instance
|
||||
) -> ifcopenshell.entity_instance:
|
||||
referenced_by = None
|
||||
if relating_object.Declares:
|
||||
referenced_by = relating_object.Declares[0]
|
||||
|
||||
@@ -31,9 +31,7 @@ def remove_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) ->
|
||||
sequences or controls are also removed.
|
||||
|
||||
:param task: The IfcTask to remove.
|
||||
:type task: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -56,76 +54,74 @@ def remove_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) ->
|
||||
# just fix it on site.
|
||||
ifcopenshell.api.sequence.remove_task(model, task=design)
|
||||
"""
|
||||
settings = {"task": task}
|
||||
|
||||
# TODO: do a deep purge
|
||||
ifcopenshell.api.project.unassign_declaration(
|
||||
file,
|
||||
definitions=[settings["task"]],
|
||||
definitions=[task],
|
||||
relating_context=file.by_type("IfcContext")[0],
|
||||
)
|
||||
if task_time := settings["task"].TaskTime:
|
||||
if task_time := task.TaskTime:
|
||||
if task_time.is_a("IfcTaskTimeRecurring"):
|
||||
ifcopenshell.api.sequence.unassign_recurrence_pattern(file, task_time.Recurrence)
|
||||
file.remove(task_time)
|
||||
|
||||
# Handle IfcRelNests.
|
||||
if rels := settings["task"].IsNestedBy:
|
||||
if rels := task.IsNestedBy:
|
||||
subtasks = rels[0].RelatedObjects
|
||||
# Use batching for optimization.
|
||||
ifcopenshell.api.nest.unassign_object(file, subtasks)
|
||||
for task_ in subtasks:
|
||||
ifcopenshell.api.sequence.remove_task(file, task=task_)
|
||||
if settings["task"].Nests:
|
||||
ifcopenshell.api.nest.unassign_object(file, [settings["task"]])
|
||||
if task.Nests:
|
||||
ifcopenshell.api.nest.unassign_object(file, [task])
|
||||
|
||||
for inverse in file.get_inverse(settings["task"]):
|
||||
for inverse in file.get_inverse(task):
|
||||
if inverse.is_a("IfcRelSequence"):
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
elif inverse.is_a("IfcRelAssignsToControl"):
|
||||
if inverse.RelatingControl == settings["task"] or len(inverse.RelatedObjects) == 1:
|
||||
if inverse.RelatingControl == task or len(inverse.RelatedObjects) == 1:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
else:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(settings["task"])
|
||||
related_objects.remove(task)
|
||||
inverse.RelatedObjects = related_objects
|
||||
elif inverse.is_a("IfcRelDefinesByProperties"):
|
||||
ifcopenshell.api.pset.remove_pset(
|
||||
file,
|
||||
product=settings["task"],
|
||||
product=task,
|
||||
pset=inverse.RelatingPropertyDefinition,
|
||||
)
|
||||
elif inverse.is_a("IfcRelAssignsToProcess"):
|
||||
if inverse.RelatingProcess == settings["task"] or len(inverse.RelatedObjects) == 1:
|
||||
if inverse.RelatingProcess == task or len(inverse.RelatedObjects) == 1:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
elif inverse.is_a("IfcRelAssignsToProduct"):
|
||||
if inverse.RelatingProduct == settings["task"] or len(inverse.RelatedObjects) == 1:
|
||||
if inverse.RelatingProduct == task or len(inverse.RelatedObjects) == 1:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
else:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(settings["task"])
|
||||
related_objects.remove(task)
|
||||
inverse.RelatedObjects = related_objects
|
||||
elif inverse.is_a("IfcRelAssignsToObject"):
|
||||
if inverse.RelatingObject == settings["task"] or len(inverse.RelatedObjects) == 1:
|
||||
if inverse.RelatingObject == task or len(inverse.RelatedObjects) == 1:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
else:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(settings["task"])
|
||||
related_objects.remove(task)
|
||||
inverse.RelatedObjects = related_objects
|
||||
elif inverse.is_a("IfcRelAssignsToProcess"):
|
||||
history = inverse.OwnerHistory
|
||||
@@ -133,7 +129,7 @@ def remove_task(file: ifcopenshell.file, task: ifcopenshell.entity_instance) ->
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
history = settings["task"].OwnerHistory
|
||||
file.remove(settings["task"])
|
||||
history = task.OwnerHistory
|
||||
file.remove(task)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -23,9 +23,7 @@ def remove_time_period(file: ifcopenshell.file, time_period: ifcopenshell.entity
|
||||
"""Removes a time period
|
||||
|
||||
:param time_period: The IfcTimePeriod to remove.
|
||||
:type time_period: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -55,6 +53,4 @@ def remove_time_period(file: ifcopenshell.file, time_period: ifcopenshell.entity
|
||||
# Let's take the afternoon off!
|
||||
ifcopenshell.api.sequence.remove_time_period(model, time_period=afternoon)
|
||||
"""
|
||||
settings = {"time_period": time_period}
|
||||
|
||||
file.remove(settings["time_period"])
|
||||
file.remove(time_period)
|
||||
|
||||
@@ -30,9 +30,7 @@ def remove_work_calendar(file: ifcopenshell.file, work_calendar: ifcopenshell.en
|
||||
calendar.
|
||||
|
||||
:param work_calendar: The IfcWorkCalendar to remove
|
||||
:type work_calendar: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -44,32 +42,30 @@ def remove_work_calendar(file: ifcopenshell.file, work_calendar: ifcopenshell.en
|
||||
# And remove it immediately
|
||||
ifcopenshell.api.sequence.remove_work_calendar(model, work_calendar=calendar)
|
||||
"""
|
||||
settings = {"work_calendar": work_calendar}
|
||||
|
||||
# TODO: do a deep purge
|
||||
ifcopenshell.api.project.unassign_declaration(
|
||||
file,
|
||||
definitions=[settings["work_calendar"]],
|
||||
definitions=[work_calendar],
|
||||
relating_context=file.by_type("IfcContext")[0],
|
||||
)
|
||||
if settings["work_calendar"].Controls:
|
||||
for rel in settings["work_calendar"].Controls:
|
||||
if work_calendar.Controls:
|
||||
for rel in work_calendar.Controls:
|
||||
for related_object in rel.RelatedObjects:
|
||||
ifcopenshell.api.control.unassign_control(
|
||||
file,
|
||||
relating_control=settings["work_calendar"],
|
||||
relating_control=work_calendar,
|
||||
related_object=related_object,
|
||||
)
|
||||
|
||||
# Currently in API work times are created already attached
|
||||
# to the work calendar, so they are never reused.
|
||||
for working_time in settings["work_calendar"].WorkingTimes or []:
|
||||
for working_time in work_calendar.WorkingTimes or []:
|
||||
ifcopenshell.api.sequence.remove_work_time(file, work_time=working_time)
|
||||
|
||||
for exception_time in settings["work_calendar"].ExceptionTimes or []:
|
||||
for exception_time in work_calendar.ExceptionTimes or []:
|
||||
ifcopenshell.api.sequence.remove_work_time(file, work_time=exception_time)
|
||||
|
||||
history = settings["work_calendar"].OwnerHistory
|
||||
file.remove(settings["work_calendar"])
|
||||
history = work_calendar.OwnerHistory
|
||||
file.remove(work_calendar)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -29,9 +29,7 @@ def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_ins
|
||||
removed.
|
||||
|
||||
:param work_plan: The IfcWorkPlan to remove.
|
||||
:type work_plan: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -43,11 +41,9 @@ def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_ins
|
||||
# And remove it immediately
|
||||
ifcopenshell.api.sequence.remove_work_plan(model, work_plan=work_plan)
|
||||
"""
|
||||
settings = {"work_plan": work_plan}
|
||||
|
||||
ifcopenshell.api.project.unassign_declaration(
|
||||
file,
|
||||
definitions=[settings["work_plan"]],
|
||||
definitions=[work_plan],
|
||||
relating_context=file.by_type("IfcContext")[0],
|
||||
)
|
||||
|
||||
@@ -55,7 +51,7 @@ def remove_work_plan(file: ifcopenshell.file, work_plan: ifcopenshell.entity_ins
|
||||
if related_objects:
|
||||
ifcopenshell.api.aggregate.unassign_object(file, related_objects)
|
||||
|
||||
history = settings["work_plan"].OwnerHistory
|
||||
file.remove(settings["work_plan"])
|
||||
history = work_plan.OwnerHistory
|
||||
file.remove(work_plan)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -47,32 +47,30 @@ def remove_work_schedule(file: ifcopenshell.file, work_schedule: ifcopenshell.en
|
||||
# And remove it immediately
|
||||
ifcopenshell.api.sequence.remove_work_schedule(model, work_schedule=schedule)
|
||||
"""
|
||||
settings = {"work_schedule": work_schedule}
|
||||
|
||||
# TODO: do a deep purge
|
||||
ifcopenshell.api.project.unassign_declaration(
|
||||
file, definitions=[settings["work_schedule"]], relating_context=file.by_type("IfcContext")[0]
|
||||
file, definitions=[work_schedule], relating_context=file.by_type("IfcContext")[0]
|
||||
)
|
||||
|
||||
if settings["work_schedule"].Declares:
|
||||
for rel in settings["work_schedule"].Declares:
|
||||
for work_schedule in rel.RelatedObjects:
|
||||
ifcopenshell.api.sequence.remove_work_schedule(file, work_schedule=work_schedule)
|
||||
if work_schedule.Declares:
|
||||
for rel in work_schedule.Declares:
|
||||
for work_schedule_ in rel.RelatedObjects:
|
||||
ifcopenshell.api.sequence.remove_work_schedule(file, work_schedule=work_schedule_)
|
||||
|
||||
# Unassign from work plans.
|
||||
if settings["work_schedule"].Decomposes:
|
||||
ifcopenshell.api.aggregate.unassign_object(file, [settings["work_schedule"]])
|
||||
if work_schedule.Decomposes:
|
||||
ifcopenshell.api.aggregate.unassign_object(file, [work_schedule])
|
||||
|
||||
for inverse in file.get_inverse(settings["work_schedule"]):
|
||||
for inverse in file.get_inverse(work_schedule):
|
||||
if inverse.is_a("IfcRelDefinesByObject"):
|
||||
if inverse.RelatingObject == settings["work_schedule"] or len(inverse.RelatedObjects) == 1:
|
||||
if inverse.RelatingObject == work_schedule or len(inverse.RelatedObjects) == 1:
|
||||
history = inverse.OwnerHistory
|
||||
file.remove(inverse)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
else:
|
||||
related_objects = list(inverse.RelatedObjects)
|
||||
related_objects.remove(settings["work_schedule"])
|
||||
related_objects.remove(work_schedule)
|
||||
inverse.RelatedObjects = related_objects
|
||||
elif inverse.is_a("IfcRelAssignsToControl"):
|
||||
[
|
||||
@@ -81,7 +79,7 @@ def remove_work_schedule(file: ifcopenshell.file, work_schedule: ifcopenshell.en
|
||||
if related_object.is_a("IfcTask")
|
||||
]
|
||||
|
||||
history = settings["work_schedule"].OwnerHistory
|
||||
file.remove(settings["work_schedule"])
|
||||
history = work_schedule.OwnerHistory
|
||||
file.remove(work_schedule)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
|
||||
@@ -31,11 +31,8 @@ def unassign_process(
|
||||
See ifcopenshell.api.sequence.assign_process for details.
|
||||
|
||||
:param relating_process: The IfcTask in the relationship.
|
||||
:type relating_process: ifcopenshell.entity_instance
|
||||
:param related_object: The related object.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -59,13 +56,8 @@ def unassign_process(
|
||||
# Change our mind.
|
||||
ifcopenshell.api.sequence.unassign_process(model, relating_process=task, related_object=wall)
|
||||
"""
|
||||
settings = {
|
||||
"relating_process": relating_process,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
for rel in settings["related_object"].HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != settings["relating_process"]:
|
||||
for rel in related_object.HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToProcess") or rel.RelatingProcess != relating_process:
|
||||
continue
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
@@ -74,7 +66,7 @@ def unassign_process(
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(settings["related_object"])
|
||||
related_objects.remove(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=rel)
|
||||
return rel
|
||||
|
||||
@@ -31,11 +31,8 @@ def unassign_product(
|
||||
See ifcopenshell.api.sequence.assign_product for details.
|
||||
|
||||
:param relating_product: The IfcProduct in the relationship.
|
||||
:type relating_product: ifcopenshell.entity_instance
|
||||
:param related_object: The IfcTask in the relationship.
|
||||
:type related_object: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -59,13 +56,8 @@ def unassign_product(
|
||||
# Change our mind.
|
||||
ifcopenshell.api.sequence.unassign_product(relating_product=wall, related_object=task)
|
||||
"""
|
||||
settings = {
|
||||
"relating_product": relating_product,
|
||||
"related_object": related_object,
|
||||
}
|
||||
|
||||
for rel in settings["related_object"].HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != settings["relating_product"]:
|
||||
for rel in related_object.HasAssignments or []:
|
||||
if not rel.is_a("IfcRelAssignsToProduct") or rel.RelatingProduct != relating_product:
|
||||
continue
|
||||
if len(rel.RelatedObjects) == 1:
|
||||
history = rel.OwnerHistory
|
||||
@@ -74,7 +66,7 @@ def unassign_product(
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
return
|
||||
related_objects = list(rel.RelatedObjects)
|
||||
related_objects.remove(settings["related_object"])
|
||||
related_objects.remove(related_object)
|
||||
rel.RelatedObjects = related_objects
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=rel)
|
||||
return rel
|
||||
|
||||
@@ -28,9 +28,7 @@ def unassign_recurrence_pattern(file: ifcopenshell.file, recurrence_pattern: ifc
|
||||
or replace IfcTaskTimeRecurring with IfcTaskTime).
|
||||
|
||||
:param recurrence_pattern: The IfcRecurrencePattern to remove.
|
||||
:type recurrence_pattern: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -50,8 +48,6 @@ def unassign_recurrence_pattern(file: ifcopenshell.file, recurrence_pattern: ifc
|
||||
# Change our mind, let's just maintain it whenever we feel like it.
|
||||
ifcopenshell.api.sequence.unassign_recurrence_pattern(recurrence_pattern=pattern)
|
||||
"""
|
||||
settings = {"recurrence_pattern": recurrence_pattern}
|
||||
|
||||
for time_period in settings["recurrence_pattern"].TimePeriods or []:
|
||||
for time_period in recurrence_pattern.TimePeriods or []:
|
||||
file.remove(time_period)
|
||||
file.remove(settings["recurrence_pattern"])
|
||||
file.remove(recurrence_pattern)
|
||||
|
||||
@@ -29,11 +29,8 @@ def unassign_sequence(
|
||||
"""Removes a sequence relationship between tasks
|
||||
|
||||
:param relating_process: The previous / predecessor task.
|
||||
:type relating_process: ifcopenshell.entity_instance
|
||||
:param related_process: The next / successor task.
|
||||
:type related_process: ifcopenshell.entity_instance
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -60,15 +57,10 @@ def unassign_sequence(
|
||||
ifcopenshell.api.sequence.unassign_sequence(model,
|
||||
relating_process=zone1, related_process=zone2)
|
||||
"""
|
||||
settings = {
|
||||
"relating_process": relating_process,
|
||||
"related_process": related_process,
|
||||
}
|
||||
|
||||
for rel in settings["related_process"].IsSuccessorFrom or []:
|
||||
if rel.RelatingProcess == settings["relating_process"]:
|
||||
for rel in related_process.IsSuccessorFrom or []:
|
||||
if rel.RelatingProcess == relating_process:
|
||||
history = rel.OwnerHistory
|
||||
file.remove(rel)
|
||||
if history:
|
||||
ifcopenshell.util.element.remove_deep2(file, history)
|
||||
ifcopenshell.api.sequence.cascade_schedule(file, task=settings["related_process"])
|
||||
ifcopenshell.api.sequence.cascade_schedule(file, task=related_process)
|
||||
|
||||
@@ -69,13 +69,11 @@ def assign_container(
|
||||
previous aggregation, containment, or nesting relationships it may have.
|
||||
|
||||
:param products: A list of physical IfcElements existing in the space.
|
||||
:type products: list[ifcopenshell.entity_instance]
|
||||
:param relating_structure: The IfcSpatialStructureElement element, such
|
||||
as IfcBuilding, IfcBuildingStorey, or IfcSpace that the element
|
||||
exists in.
|
||||
:return: The IfcRelContainedInSpatialStructure relationship instance
|
||||
or `None` if `products` was empty list.
|
||||
:rtype: Union[ifcopenshell.entity_instance, None]
|
||||
|
||||
Example:
|
||||
|
||||
@@ -103,16 +101,10 @@ def assign_container(
|
||||
ifcopenshell.api.spatial.assign_container(model, products=[wall], relating_structure=storey)
|
||||
ifcopenshell.api.spatial.assign_container(model, products=[furniture], relating_structure=space)
|
||||
"""
|
||||
settings = {
|
||||
"products": products,
|
||||
"relating_structure": relating_structure,
|
||||
}
|
||||
|
||||
if not settings["products"]:
|
||||
if not products:
|
||||
return
|
||||
|
||||
products = set(settings["products"])
|
||||
relating_structure = settings["relating_structure"]
|
||||
products_set = set(products)
|
||||
structure_rel = next(iter(relating_structure.ContainsElements), None)
|
||||
|
||||
previous_containers_rels: set[ifcopenshell.entity_instance] = set()
|
||||
@@ -120,7 +112,7 @@ def assign_container(
|
||||
products_with_containers: list[ifcopenshell.entity_instance] = []
|
||||
|
||||
# check if there is anything to change
|
||||
for product in products:
|
||||
for product in products_set:
|
||||
product_rel = next(iter(product.ContainedInStructure), None)
|
||||
|
||||
if product_rel is None:
|
||||
@@ -144,7 +136,7 @@ def assign_container(
|
||||
|
||||
# unassign elements from previous containers
|
||||
for rel in previous_containers_rels:
|
||||
related_elements = set(rel.RelatedElements) - products
|
||||
related_elements = set(rel.RelatedElements) - products_set
|
||||
if related_elements:
|
||||
rel.RelatedElements = list(related_elements)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": rel})
|
||||
@@ -156,7 +148,7 @@ def assign_container(
|
||||
|
||||
# assign elements to a new container
|
||||
if structure_rel:
|
||||
structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products)
|
||||
structure_rel.RelatedElements = list(set(structure_rel.RelatedElements) | products_set)
|
||||
ifcopenshell.api.owner.update_owner_history(file, **{"element": structure_rel})
|
||||
else:
|
||||
structure_rel = file.create_entity(
|
||||
@@ -164,8 +156,8 @@ def assign_container(
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.owner.create_owner_history(file),
|
||||
"RelatedElements": list(products),
|
||||
"RelatingStructure": settings["relating_structure"],
|
||||
"RelatedElements": list(products_set),
|
||||
"RelatingStructure": relating_structure,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -25,9 +25,7 @@ def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.enti
|
||||
"""Unassigns a container from products.
|
||||
|
||||
:param product: A list of IfcProducts to remove the containment from.
|
||||
:type product: list[ifcopenshell.entity_instance]
|
||||
:return: None
|
||||
:rtype: None
|
||||
|
||||
Example:
|
||||
|
||||
@@ -54,15 +52,11 @@ def unassign_container(file: ifcopenshell.file, products: list[ifcopenshell.enti
|
||||
# Not anymore!
|
||||
ifcopenshell.api.spatial.unassign_container(model, products=[wall])
|
||||
"""
|
||||
settings = {
|
||||
"products": products,
|
||||
}
|
||||
|
||||
products = set(settings["products"])
|
||||
rels = set(rel for product in products if (rel := next(iter(product.ContainedInStructure), None)))
|
||||
products_set = set(products)
|
||||
rels = set(rel for product in products_set if (rel := next(iter(product.ContainedInStructure), None)))
|
||||
|
||||
for rel in rels:
|
||||
related_elements = set(rel.RelatedElements) - products
|
||||
related_elements = set(rel.RelatedElements) - products_set
|
||||
if related_elements:
|
||||
rel.RelatedElements = list(related_elements)
|
||||
ifcopenshell.api.owner.update_owner_history(file, element=rel)
|
||||
|
||||
@@ -27,7 +27,7 @@ def add_structural_activity(
|
||||
ifc_class: str = "IfcStructuralPlanarAction",
|
||||
predefined_type: str = "CONST",
|
||||
global_or_local: Literal["GLOBAL_COORDS", "LOCAL_COORDS"] = "GLOBAL_COORDS",
|
||||
) -> None:
|
||||
) -> ifcopenshell.entity_instance:
|
||||
"""Adds a new structural activity
|
||||
|
||||
A structural activity is either a structural action or a reaction. It
|
||||
@@ -38,42 +38,29 @@ def add_structural_activity(
|
||||
a structural member.
|
||||
|
||||
:param ifc_class: Choose from any subtype of IfcStructuralActivity.
|
||||
:type ifc_class: str
|
||||
:param predefined_type: View the IFC documentation for what valid
|
||||
predefined types may be chosen.
|
||||
:type predefined_type: str
|
||||
:param global_or_local: The location coordinates of the load is always
|
||||
defined locally relative to the structural member the activity is
|
||||
assigned to. However, the directions of the applied load may either
|
||||
be specified globally or locally depending on how this argument is
|
||||
set. Choose from GLOBAL_COORDS or LOCAL_COORDS.
|
||||
:type global_or_local: str
|
||||
:param applied_load: The IfcStructuralLoad that is applied in this
|
||||
activity.
|
||||
:type applied_load: ifcopenshell.entity_instance
|
||||
:param structural_member: The IfcStructuralMember that the load is
|
||||
applied to.
|
||||
:type structural_member: ifcopenshell.entity_instance
|
||||
:return: The newly created entity based on the ifc_class
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
"""
|
||||
settings = {
|
||||
"ifc_class": ifc_class,
|
||||
"predefined_type": predefined_type,
|
||||
"global_or_local": global_or_local,
|
||||
"applied_load": applied_load,
|
||||
"structural_member": structural_member,
|
||||
}
|
||||
|
||||
activity = ifcopenshell.api.root.create_entity(
|
||||
file,
|
||||
ifc_class=settings["ifc_class"],
|
||||
predefined_type=settings["predefined_type"],
|
||||
ifc_class=ifc_class,
|
||||
predefined_type=predefined_type,
|
||||
)
|
||||
activity.AppliedLoad = settings["applied_load"]
|
||||
activity.GlobalOrLocal = settings["global_or_local"]
|
||||
activity.AppliedLoad = applied_load
|
||||
activity.GlobalOrLocal = global_or_local
|
||||
|
||||
rel = ifcopenshell.api.root.create_entity(file, ifc_class="IfcRelConnectsStructuralActivity")
|
||||
rel.RelatingElement = settings["structural_member"]
|
||||
rel.RelatingElement = structural_member
|
||||
rel.RelatedStructuralActivity = activity
|
||||
return activity
|
||||
|
||||
+7
-13
@@ -32,18 +32,14 @@ def add_structural_boundary_condition(
|
||||
edge condition, and surface connections will have a face condition.
|
||||
|
||||
:param name: The name of the boundary condition.
|
||||
:type name: str,optional
|
||||
:param connection: The IfcStructuralConnection to apply the boundary
|
||||
condition to. This will determine the type of condition that is
|
||||
created. If no connection is supplied, an orphan boundary condition
|
||||
will be created using the ifc_class that you specify.
|
||||
:type connection: ifcopenshell.entity_instance,optional
|
||||
:param ifc_class: The class of IfcBoundaryCondition to create, only
|
||||
relevant if you do not specify a connection and want to create an
|
||||
orphaned boundary condition.
|
||||
:type ifc_class: str,optional
|
||||
:return: The newly created IfcBoundaryCondition
|
||||
:rtype: ifcopenshell.entity_instance
|
||||
|
||||
Example:
|
||||
|
||||
@@ -51,14 +47,12 @@ def add_structural_boundary_condition(
|
||||
|
||||
ifcopenshell.api.structural.add_structural_boundary_condition(model, connection=connection)
|
||||
"""
|
||||
settings = {"name": name, "connection": connection, "ifc_class": ifc_class}
|
||||
|
||||
if settings["connection"]:
|
||||
if connection:
|
||||
# assign boundary condition to a connection
|
||||
if settings["connection"].is_a("IfcRelConnectsStructuralMember"):
|
||||
related_connection = settings["connection"].RelatedStructuralConnection
|
||||
if connection.is_a("IfcRelConnectsStructuralMember"):
|
||||
related_connection = connection.RelatedStructuralConnection
|
||||
else:
|
||||
related_connection = settings["connection"]
|
||||
related_connection = connection
|
||||
|
||||
if related_connection.is_a("IfcStructuralPointConnection"):
|
||||
boundary_class = "IfcBoundaryNodeCondition"
|
||||
@@ -67,9 +61,9 @@ def add_structural_boundary_condition(
|
||||
elif related_connection.is_a("IfcStructuralSurfaceConnection"):
|
||||
boundary_class = "IfcBoundaryFaceCondition"
|
||||
|
||||
condition = file.create_entity(boundary_class, Name=settings["name"])
|
||||
settings["connection"].AppliedCondition = condition
|
||||
condition = file.create_entity(boundary_class, Name=name)
|
||||
connection.AppliedCondition = condition
|
||||
return condition
|
||||
else:
|
||||
# add an orphan boundary condition
|
||||
return file.create_entity(settings["ifc_class"], Name=settings["name"])
|
||||
return file.create_entity(ifc_class, Name=name)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user