mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-21 04:32:23 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b3f964000 | |||
| de6b578d9c | |||
| f50e790b11 | |||
| 9821c3af67 | |||
| 0844d6f52e | |||
| 520e7653e4 | |||
| 9512075506 | |||
| bdd61713ea | |||
| 97cf1da4ff | |||
| 836a162d57 |
@@ -326,7 +326,11 @@ class IfcImporter:
|
||||
self.elements = [e for e in self.elements if e.Representation and not e.is_a("IfcFeatureElement")]
|
||||
|
||||
self.elements = set(self.elements[offset:offset_limit])
|
||||
self.element_types = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
|
||||
|
||||
if self.ifc_import_settings.has_filter or offset or offset_limit < len(self.elements):
|
||||
self.element_types = set([ifcopenshell.util.element.get_type(e) for e in self.elements])
|
||||
else:
|
||||
self.element_types = set(self.file.by_type("IfcElementType"))
|
||||
|
||||
if self.ifc_import_settings.has_filter and self.ifc_import_settings.should_filter_spatial_elements:
|
||||
self.spatial_elements = self.get_spatial_elements_filtered_by_elements(self.elements)
|
||||
|
||||
@@ -33,7 +33,11 @@ class DiffData:
|
||||
|
||||
@classmethod
|
||||
def load(cls):
|
||||
cls.data = {"diff_json": cls.diff_json(), "changes": cls.changes()}
|
||||
cls.data["diff_json"] = cls.diff_json()
|
||||
cls.data["total_added"] = cls.total_added()
|
||||
cls.data["total_deleted"] = cls.total_deleted()
|
||||
cls.data["total_changed"] = cls.total_changed()
|
||||
cls.data["changes"] = cls.changes()
|
||||
cls.is_loaded = True
|
||||
|
||||
@classmethod
|
||||
@@ -48,6 +52,24 @@ class DiffData:
|
||||
cls.diff = json.load(file)
|
||||
return cls.diff
|
||||
|
||||
@classmethod
|
||||
def total_added(cls):
|
||||
diff = cls.diff_json()
|
||||
if diff:
|
||||
return len(diff["added"])
|
||||
|
||||
@classmethod
|
||||
def total_deleted(cls):
|
||||
diff = cls.diff_json()
|
||||
if diff:
|
||||
return len(diff["deleted"])
|
||||
|
||||
@classmethod
|
||||
def total_changed(cls):
|
||||
diff = cls.diff_json()
|
||||
if diff:
|
||||
return len(diff["changed"].keys())
|
||||
|
||||
@classmethod
|
||||
def changes(cls):
|
||||
diff = cls.diff_json()
|
||||
@@ -56,4 +78,9 @@ class DiffData:
|
||||
element = tool.Ifc.get_entity(bpy.context.active_object)
|
||||
if not element or not hasattr(element, "GlobalId"):
|
||||
return {}
|
||||
return {k.upper().replace("_", " "): str(v) for k, v in diff["changed"].get(element.GlobalId, {}).items()}
|
||||
changes = {k.upper().replace("_", " "): str(v) for k, v in diff["changed"].get(element.GlobalId, {}).items()}
|
||||
if element.GlobalId in diff["added"]:
|
||||
changes["Added"] = True
|
||||
elif element.GlobalId in diff["deleted"]:
|
||||
changes["Deleted"] = True
|
||||
return changes
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import bpy
|
||||
import json
|
||||
import ifccsv
|
||||
import ifcopenshell
|
||||
import json
|
||||
import blenderbim.bim.handler
|
||||
import blenderbim.tool as tool
|
||||
from blenderbim.bim.ifc import IfcStore
|
||||
|
||||
|
||||
class SelectDiffJsonFile(bpy.types.Operator):
|
||||
@@ -29,6 +29,7 @@ class SelectDiffJsonFile(bpy.types.Operator):
|
||||
bl_label = "Select Diff JSON File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.json", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DiffProperties.diff_json_file = self.filepath
|
||||
@@ -72,9 +73,10 @@ class SelectDiffOldFile(bpy.types.Operator):
|
||||
bl_label = "Select Diff Old File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DiffProperties.diff_old_file = self.filepath
|
||||
context.scene.DiffProperties.old_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -87,9 +89,10 @@ class SelectDiffNewFile(bpy.types.Operator):
|
||||
bl_label = "Select Diff New File"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
|
||||
filter_glob: bpy.props.StringProperty(default="*.ifc", options={"HIDDEN"})
|
||||
|
||||
def execute(self, context):
|
||||
context.scene.DiffProperties.diff_new_file = self.filepath
|
||||
context.scene.DiffProperties.new_file = self.filepath
|
||||
return {"FINISHED"}
|
||||
|
||||
def invoke(self, context, event):
|
||||
@@ -113,14 +116,14 @@ class ExecuteIfcDiff(bpy.types.Operator):
|
||||
import ifcdiff
|
||||
|
||||
ifc_diff = ifcdiff.IfcDiff(
|
||||
context.scene.DiffProperties.diff_old_file,
|
||||
context.scene.DiffProperties.diff_new_file,
|
||||
context.scene.DiffProperties.old_file,
|
||||
context.scene.DiffProperties.new_file,
|
||||
self.filepath,
|
||||
[r.relationship for r in context.scene.DiffProperties.diff_relationships],
|
||||
context.scene.DiffProperties.diff_filter_elements,
|
||||
)
|
||||
diff = ifc_diff.diff()
|
||||
ifc_diff.diff()
|
||||
ifc_diff.export()
|
||||
context.scene.DiffProperties.diff_json_file = self.filepath
|
||||
context.scene.DiffProperties.diff_result = diff
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
|
||||
@@ -44,9 +44,8 @@ class Relationships(PropertyGroup):
|
||||
|
||||
|
||||
class DiffProperties(PropertyGroup):
|
||||
diff_json_file: StringProperty(default="", name="Diff JSON File", update=update_diff_json_file)
|
||||
diff_old_file: StringProperty(default="", name="Diff Old IFC File")
|
||||
diff_new_file: StringProperty(default="", name="Diff New IFC File")
|
||||
diff_relationships: CollectionProperty(type=Relationships, name="Diff Relationships")
|
||||
diff_filter_elements: StringProperty(default="", name="Diff Filter")
|
||||
diff_result: StringProperty(default="", name="Diff Result")
|
||||
diff_json_file: StringProperty(default="", name="JSON Output", update=update_diff_json_file)
|
||||
old_file: StringProperty(default="", name="Old IFC File")
|
||||
new_file: StringProperty(default="", name="New IFC File")
|
||||
diff_relationships: CollectionProperty(type=Relationships, name="Relationships")
|
||||
diff_filter_elements: StringProperty(default="", name="Filter")
|
||||
|
||||
@@ -40,55 +40,63 @@ class BIM_PT_diff(Panel):
|
||||
layout.use_property_split = True
|
||||
|
||||
scene = context.scene
|
||||
bim_properties = scene.DiffProperties
|
||||
props = scene.DiffProperties
|
||||
|
||||
layout.label(text="IFC Diff Setup:")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(bim_properties, "diff_old_file")
|
||||
row.prop(props, "old_file")
|
||||
row.operator("bim.select_diff_old_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(bim_properties, "diff_new_file")
|
||||
row.prop(props, "new_file")
|
||||
row.operator("bim.select_diff_new_file", icon="FILE_FOLDER", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(bim_properties, "diff_relationships")
|
||||
row.context_pointer_set("bim_prop_group", bim_properties)
|
||||
row.prop(props, "diff_relationships")
|
||||
row.context_pointer_set("bim_prop_group", props)
|
||||
add = row.operator("bim.edit_blender_collection", icon="ADD", text="")
|
||||
add.option = "add"
|
||||
add.collection = "diff_relationships"
|
||||
|
||||
for index, r in enumerate(bim_properties.diff_relationships):
|
||||
|
||||
for index, r in enumerate(props.diff_relationships):
|
||||
row = layout.row(align=True)
|
||||
row.context_pointer_set("bim_prop_group", bim_properties)
|
||||
row.context_pointer_set("bim_prop_group", props)
|
||||
row.prop(r, "relationship", text=" ")
|
||||
remove = row.operator("bim.edit_blender_collection", icon="REMOVE", text="")
|
||||
remove.option = "remove"
|
||||
remove.collection = "diff_relationships"
|
||||
remove.index = index
|
||||
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(bim_properties, "diff_filter_elements")
|
||||
row.prop(props, "diff_filter_elements")
|
||||
row.operator("bim.ifc_selector", icon="FILTER", text="")
|
||||
|
||||
row = layout.row()
|
||||
row.operator("bim.execute_ifc_diff")
|
||||
if bim_properties.diff_result:
|
||||
row = layout.row()
|
||||
row.alignment = "CENTER"
|
||||
row.label(text=bim_properties.diff_result)
|
||||
|
||||
# TODO: show if there ifc diff operation is sucessful
|
||||
row = layout.row(align=True)
|
||||
row.prop(bim_properties, "diff_json_file")
|
||||
row.prop(props, "diff_json_file")
|
||||
row.operator("bim.select_diff_json_file", icon="FILE_FOLDER", text="")
|
||||
row.operator("bim.visualise_diff", icon="HIDE_OFF", text="")
|
||||
|
||||
if DiffData.data["changes"]:
|
||||
|
||||
if DiffData.data["diff_json"]:
|
||||
row = layout.row()
|
||||
row.label(text="Diff Results:")
|
||||
row.alignment = "CENTER"
|
||||
row.label(text=f"{DiffData.data['total_added']} added")
|
||||
row.label(text=f"{DiffData.data['total_deleted']} deleted")
|
||||
row.label(text=f"{DiffData.data['total_changed']} changed")
|
||||
|
||||
if DiffData.data["changes"]:
|
||||
box = layout.box()
|
||||
row = box.row()
|
||||
row.label(text="Active Object Changes:")
|
||||
for key, value in DiffData.data["changes"].items():
|
||||
row = layout.row()
|
||||
row.label(text=key)
|
||||
row.label(text=value)
|
||||
row = box.row()
|
||||
if key == "Added":
|
||||
icon = "ADD"
|
||||
elif key == "Deleted":
|
||||
icon = "X"
|
||||
else:
|
||||
icon = "GREASEPENCIL"
|
||||
row.label(text=key, icon=icon)
|
||||
|
||||
@@ -41,7 +41,7 @@ class LoadGroups(bpy.types.Operator):
|
||||
context.scene.ExpandedGroups.json_string = "{}"
|
||||
|
||||
for ifc_definition_id, group in Data.groups.items():
|
||||
if not group["HasAssignments"]:
|
||||
if not group["HasAssignments"]:
|
||||
new = self.props.groups.add()
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
new.name = group["Name"]
|
||||
@@ -50,7 +50,7 @@ class LoadGroups(bpy.types.Operator):
|
||||
|
||||
if group["IsGroupedBy"]:
|
||||
# assumes 1:1 cardinality, will need to be updated to reflect IFC4 changes
|
||||
# where the cardinality is 0:? - vulevukusej
|
||||
# where the cardinality is 0:? - vulevukusej
|
||||
sub_groups = [g for g in group["IsGroupedBy"][0].RelatedObjects if g.is_a("IfcGroup")]
|
||||
new.has_children = True if len(sub_groups) != 0 else False
|
||||
|
||||
@@ -146,7 +146,7 @@ class AddGroupToGroup(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
result = ifcopenshell.api.run("group.add_group", self.file)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group", IfcStore.get_file(), **{"product": [result], "group": self.file.by_id(self.group)}
|
||||
"group.assign_group", IfcStore.get_file(), products=[result], group=self.file.by_id(self.group)
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.load_groups(is_refresh=True)
|
||||
@@ -255,10 +255,8 @@ class AssignGroup(bpy.types.Operator):
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group",
|
||||
self.file,
|
||||
**{
|
||||
"product": [self.file.by_id(product.BIMObjectProperties.ifc_definition_id)],
|
||||
"group": self.file.by_id(self.group),
|
||||
}
|
||||
products=[self.file.by_id(product.BIMObjectProperties.ifc_definition_id)],
|
||||
group=self.file.by_id(self.group),
|
||||
)
|
||||
Data.load(self.file)
|
||||
return {"FINISHED"}
|
||||
@@ -332,7 +330,6 @@ class UpdateGroup(bpy.types.Operator):
|
||||
**{
|
||||
"group": group,
|
||||
"products": new_products,
|
||||
|
||||
}
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
|
||||
@@ -89,7 +89,7 @@ class BimTool(WorkSpaceTool):
|
||||
row = layout.row(align=True)
|
||||
if relating_types_ids:
|
||||
row.label(text="", icon="FILE_3D")
|
||||
row.prop(data=props, property="relating_type_id", text="")
|
||||
prop_with_search(row, props, "relating_type_id", text="")
|
||||
else:
|
||||
row.label(text="No Construction Type", icon="FILE_3D")
|
||||
if ifc_classes:
|
||||
|
||||
@@ -169,14 +169,14 @@ class MaterialPsetProperties(PropertyGroup):
|
||||
class TaskPsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
qto_name: EnumProperty(items=getTaskQtoNames, name="Qto Name")
|
||||
|
||||
|
||||
class ResourcePsetProperties(PropertyGroup):
|
||||
active_pset_id: IntProperty(name="Active Pset ID")
|
||||
active_pset_name: StringProperty(name="Pset Name")
|
||||
properties: CollectionProperty(name="Properties", type=Attribute)
|
||||
properties: CollectionProperty(name="Properties", type=IfcProperty)
|
||||
pset_name: EnumProperty(items=getResourcePsetNames, name="Pset Name")
|
||||
qto_name: EnumProperty(items=getResourceQtoNames, name="Qto Name")
|
||||
|
||||
|
||||
@@ -59,12 +59,8 @@ class AddWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_label = "Add Work Plan"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
core.add_work_plan(tool.Ifc, tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EditWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -72,12 +68,8 @@ class EditWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = "Edit Work Plan"
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
core.edit_work_plan(tool.Ifc, tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class RemoveWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
@@ -86,33 +78,27 @@ class RemoveWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
work_plan: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
return IfcStore.execute_ifc_operator(self, context)
|
||||
|
||||
def _execute(self, context):
|
||||
core.remove_work_plan(tool.Ifc, tool.Sequence, work_plan=self.work_plan)
|
||||
return {"FINISHED"}
|
||||
core.remove_work_plan(tool.Ifc, tool.Sequence, work_plan=tool.Ifc.get().by_id(self.work_plan))
|
||||
|
||||
|
||||
class EnableEditingWorkPlan(bpy.types.Operator):
|
||||
class EnableEditingWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_work_plan"
|
||||
bl_label = "Enable Editing Work Plan"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
work_plan: bpy.props.IntProperty()
|
||||
|
||||
def execute(self, context):
|
||||
core.enable_editing_work_plan(tool.Sequence, work_plan=self.work_plan)
|
||||
return {"FINISHED"}
|
||||
def _execute(self, context):
|
||||
core.enable_editing_work_plan(tool.Sequence, work_plan=tool.Ifc.get().by_id(self.work_plan))
|
||||
|
||||
|
||||
class DisableEditingWorkPlan(bpy.types.Operator):
|
||||
class DisableEditingWorkPlan(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_work_plan"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
bl_label = "Disable Editing Work Plan"
|
||||
|
||||
def execute(self, context):
|
||||
def _execute(self, context):
|
||||
core.disable_editing_work_plan(tool.Sequence)
|
||||
return {"FINISHED"}
|
||||
|
||||
|
||||
class EnableEditingWorkPlanSchedules(bpy.types.Operator):
|
||||
|
||||
@@ -52,7 +52,7 @@ class BIM_PT_work_plans(Panel):
|
||||
text="{} Work Plans Found".format(SequenceData.number_of_work_plans_loaded),
|
||||
icon="TEXT",
|
||||
)
|
||||
row.operator("bim.add_work_plan", icon="ADD")
|
||||
row.operator("bim.add_work_plan", icon="ADD", text="")
|
||||
|
||||
for work_plan_id, work_plan in SequenceData.work_plans.items():
|
||||
self.draw_work_plan_ui(work_plan_id, work_plan)
|
||||
|
||||
@@ -23,8 +23,10 @@ classes = (
|
||||
operator.AssignContainer,
|
||||
operator.ChangeSpatialLevel,
|
||||
operator.CopyToContainer,
|
||||
operator.DereferenceStructure,
|
||||
operator.DisableEditingContainer,
|
||||
operator.EnableEditingContainer,
|
||||
operator.ReferenceStructure,
|
||||
operator.RemoveContainer,
|
||||
operator.SelectContainer,
|
||||
operator.SelectSimilarContainer,
|
||||
|
||||
@@ -33,9 +33,9 @@ class SpatialData:
|
||||
def load(cls):
|
||||
cls.data = {
|
||||
"parent_container_id": cls.get_parent_container_id(),
|
||||
"is_contained": cls.is_contained(),
|
||||
"is_directly_contained": cls.is_directly_contained(),
|
||||
"label": cls.get_label(),
|
||||
"label": cls.label(),
|
||||
"references": cls.references(),
|
||||
}
|
||||
cls.is_loaded = True
|
||||
|
||||
@@ -50,11 +50,7 @@ class SpatialData:
|
||||
return container.Decomposes[0].RelatingObject.id()
|
||||
|
||||
@classmethod
|
||||
def is_contained(cls):
|
||||
return ifcopenshell.util.element.get_container(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
|
||||
@classmethod
|
||||
def get_label(cls):
|
||||
def label(cls):
|
||||
container = ifcopenshell.util.element.get_container(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
if container:
|
||||
label = f"{container.is_a()}/{container.Name or ''}"
|
||||
@@ -62,6 +58,11 @@ class SpatialData:
|
||||
label += "*"
|
||||
return label
|
||||
|
||||
@classmethod
|
||||
def references(cls):
|
||||
results = ifcopenshell.util.element.get_referenced_structures(tool.Ifc.get_entity(bpy.context.active_object))
|
||||
return sorted([f"{r.is_a()}/{r.Name or ''}" for r in results])
|
||||
|
||||
@classmethod
|
||||
def is_directly_contained(cls):
|
||||
return bool(getattr(tool.Ifc.get_entity(bpy.context.active_object), "ContainedInStructure", False))
|
||||
|
||||
@@ -27,14 +27,41 @@ from blenderbim.bim.ifc import IfcStore
|
||||
from blenderbim.bim.module.spatial.data import SpatialData
|
||||
|
||||
|
||||
class Operator:
|
||||
def execute(self, context):
|
||||
IfcStore.execute_ifc_operator(self, context)
|
||||
blenderbim.bim.handler.refresh_ui_data()
|
||||
return {"FINISHED"}
|
||||
class ReferenceStructure(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.reference_structure"
|
||||
bl_label = "Reference Structure"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
structure: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
sprops = context.scene.BIMSpatialProperties
|
||||
containers = [tool.Ifc.get().by_id(c.ifc_definition_id) for c in sprops.containers if c.is_selected]
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
for container in containers:
|
||||
core.reference_structure(tool.Ifc, tool.Spatial, structure=container, element=element)
|
||||
|
||||
|
||||
class AssignContainer(bpy.types.Operator, Operator):
|
||||
class DereferenceStructure(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.dereference_structure"
|
||||
bl_label = "Dereference Structure"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
structure: bpy.props.IntProperty()
|
||||
|
||||
def _execute(self, context):
|
||||
sprops = context.scene.BIMSpatialProperties
|
||||
containers = [tool.Ifc.get().by_id(c.ifc_definition_id) for c in sprops.containers if c.is_selected]
|
||||
for obj in context.selected_objects:
|
||||
element = tool.Ifc.get_entity(obj)
|
||||
if not element:
|
||||
continue
|
||||
for container in containers:
|
||||
core.dereference_structure(tool.Ifc, tool.Spatial, structure=container, element=element)
|
||||
|
||||
|
||||
class AssignContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.assign_container"
|
||||
bl_label = "Assign Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -48,7 +75,7 @@ class AssignContainer(bpy.types.Operator, Operator):
|
||||
)
|
||||
|
||||
|
||||
class EnableEditingContainer(bpy.types.Operator, Operator):
|
||||
class EnableEditingContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.enable_editing_container"
|
||||
bl_label = "Enable Editing Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -57,7 +84,7 @@ class EnableEditingContainer(bpy.types.Operator, Operator):
|
||||
core.enable_editing_container(tool.Spatial, obj=context.active_object)
|
||||
|
||||
|
||||
class ChangeSpatialLevel(bpy.types.Operator, Operator):
|
||||
class ChangeSpatialLevel(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.change_spatial_level"
|
||||
bl_label = "Change Spatial Level"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -67,7 +94,7 @@ class ChangeSpatialLevel(bpy.types.Operator, Operator):
|
||||
core.change_spatial_level(tool.Spatial, parent=tool.Ifc.get().by_id(self.parent))
|
||||
|
||||
|
||||
class DisableEditingContainer(bpy.types.Operator, Operator):
|
||||
class DisableEditingContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.disable_editing_container"
|
||||
bl_label = "Disable Editing Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -76,7 +103,7 @@ class DisableEditingContainer(bpy.types.Operator, Operator):
|
||||
core.disable_editing_container(tool.Spatial, obj=context.active_object)
|
||||
|
||||
|
||||
class RemoveContainer(bpy.types.Operator, Operator):
|
||||
class RemoveContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.remove_container"
|
||||
bl_label = "Remove Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -86,11 +113,16 @@ class RemoveContainer(bpy.types.Operator, Operator):
|
||||
core.remove_container(tool.Ifc, tool.Collector, obj=obj)
|
||||
|
||||
|
||||
class CopyToContainer(bpy.types.Operator, Operator):
|
||||
class CopyToContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
"""
|
||||
Copies selected objects to selected containers
|
||||
Check the mark next to a container in the container list to select it
|
||||
Several containers can be selected at a time
|
||||
Copies selected 3D elements in the viewport to checkmarked spatial containers
|
||||
|
||||
Example: bulk copy a wall to multiple storeys
|
||||
|
||||
1. Select one or more 3D elements in the viewport
|
||||
2. Enable the checkmark next to one or more containers in the container list below to select it
|
||||
3. Press this button
|
||||
4. The copied elements will have a new position relative to the destination containers
|
||||
"""
|
||||
|
||||
bl_idname = "bim.copy_to_container"
|
||||
@@ -105,7 +137,7 @@ class CopyToContainer(bpy.types.Operator, Operator):
|
||||
blenderbim.bim.handler.purge_module_data()
|
||||
|
||||
|
||||
class SelectContainer(bpy.types.Operator, Operator):
|
||||
class SelectContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_container"
|
||||
bl_label = "Select Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
@@ -114,7 +146,7 @@ class SelectContainer(bpy.types.Operator, Operator):
|
||||
core.select_container(tool.Ifc, tool.Spatial, obj=context.active_object)
|
||||
|
||||
|
||||
class SelectSimilarContainer(bpy.types.Operator, Operator):
|
||||
class SelectSimilarContainer(bpy.types.Operator, tool.Ifc.Operator):
|
||||
bl_idname = "bim.select_similar_container"
|
||||
bl_label = "Select Similar Container"
|
||||
bl_options = {"REGISTER", "UNDO"}
|
||||
|
||||
@@ -55,13 +55,15 @@ class BIM_PT_spatial(Panel):
|
||||
if props.containers and props.active_container_index < len(props.containers):
|
||||
op = row.operator("bim.assign_container", icon="CHECKMARK")
|
||||
op.structure = props.containers[props.active_container_index].ifc_definition_id
|
||||
row.operator("bim.reference_structure", icon="LINKED", text="")
|
||||
row.operator("bim.dereference_structure", icon="UNLINKED", text="")
|
||||
row.operator("bim.copy_to_container", icon="COPYDOWN", text="")
|
||||
row.operator("bim.disable_editing_container", icon="CANCEL", text="")
|
||||
|
||||
self.layout.template_list("BIM_UL_containers", "", props, "containers", props, "active_container_index")
|
||||
else:
|
||||
row = self.layout.row(align=True)
|
||||
if SpatialData.data["is_contained"]:
|
||||
if SpatialData.data["label"]:
|
||||
row.label(text=SpatialData.data["label"])
|
||||
row.operator("bim.select_container", icon="TRACKER", text="")
|
||||
row.operator("bim.select_similar_container", icon="RESTRICT_SELECT_OFF", text="")
|
||||
@@ -71,6 +73,9 @@ class BIM_PT_spatial(Panel):
|
||||
else:
|
||||
row.label(text="This object is not spatially contained")
|
||||
row.operator("bim.enable_editing_container", icon="GREASEPENCIL", text="")
|
||||
for reference in SpatialData.data["references"]:
|
||||
row = self.layout.row()
|
||||
row.label(text=reference, icon="LINKED")
|
||||
|
||||
|
||||
class BIM_UL_containers(UIList):
|
||||
|
||||
@@ -663,7 +663,7 @@ class AddStructuralLoadGroup(bpy.types.Operator):
|
||||
self.file = IfcStore.get_file()
|
||||
load_group = ifcopenshell.api.run("structural.add_structural_load_group", self.file)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group", self.file, product=[load_group], group=self.file.by_id(self.load_case)
|
||||
"group.assign_group", self.file, products=[load_group], group=self.file.by_id(self.load_case)
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
return {"FINISHED"}
|
||||
@@ -756,7 +756,7 @@ class AddStructuralActivity(bpy.types.Operator):
|
||||
structural_member=element,
|
||||
)
|
||||
ifcopenshell.api.run(
|
||||
"group.assign_group", self.file, product=[activity], group=self.file.by_id(self.load_group)
|
||||
"group.assign_group", self.file, products=[activity], group=self.file.by_id(self.load_group)
|
||||
)
|
||||
Data.load(IfcStore.get_file())
|
||||
bpy.ops.bim.enable_editing_structural_load_group_activities(load_group=self.load_group)
|
||||
|
||||
@@ -148,7 +148,7 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
|
||||
)
|
||||
group = ifc.run("group.add_group")
|
||||
ifc.run("group.edit_group", group=group, attributes={"Name": drawing_name, "ObjectType": "DRAWING"})
|
||||
ifc.run("group.assign_group", group=group, product=[element])
|
||||
ifc.run("group.assign_group", group=group, products=[element])
|
||||
collector.assign(camera)
|
||||
pset = ifc.run("pset.add_pset", product=element, name="EPset_Drawing")
|
||||
ifc.run(
|
||||
@@ -203,7 +203,7 @@ def add_annotation(ifc, collector, drawing_tool, drawing=None, object_type=None)
|
||||
context=context,
|
||||
ifc_representation_class=drawing_tool.get_ifc_representation_class(object_type),
|
||||
)
|
||||
ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), product=[element])
|
||||
ifc.run("group.assign_group", group=drawing_tool.get_drawing_group(drawing), products=[element])
|
||||
collector.assign(obj)
|
||||
drawing_tool.enable_editing(obj)
|
||||
|
||||
@@ -251,7 +251,7 @@ def sync_references(ifc, collector, drawing_tool, drawing=None):
|
||||
annotation = drawing_tool.generate_reference_annotation(drawing, reference_element, context)
|
||||
if annotation:
|
||||
ifc.run("drawing.assign_product", relating_product=reference_element, related_object=annotation)
|
||||
ifc.run("group.assign_group", group=group, product=[annotation])
|
||||
ifc.run("group.assign_group", group=group, products=[annotation])
|
||||
collector.assign(ifc.get_object(annotation))
|
||||
|
||||
if reference_obj and ifc.is_moved(reference_obj):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# BlenderBIM Add-on - OpenBIM Blender Add-on
|
||||
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>, 2022 Yassine Oualid <yassine@sigmadimensions.com>
|
||||
# Copyright (C) 2021, 2022 Dion Moult <dion@thinkmoult.com>, Yassine Oualid <yassine@sigmadimensions.com>
|
||||
#
|
||||
# This file is part of BlenderBIM Add-on.
|
||||
#
|
||||
@@ -23,17 +23,12 @@ def add_work_plan(ifc, sequence):
|
||||
|
||||
|
||||
def remove_work_plan(ifc, sequence, work_plan=None):
|
||||
ifc.run("sequence.remove_work_plan", **{"work_plan": ifc.get().by_id(work_plan)})
|
||||
ifc.run("sequence.remove_work_plan", work_plan=work_plan)
|
||||
sequence.load_work_plans()
|
||||
|
||||
|
||||
def load_work_plan_attributes(sequence, work_plan=None):
|
||||
data = sequence.get_ifc_work_plan_attributes(work_plan)
|
||||
sequence.load_work_plan_attributes(data)
|
||||
|
||||
|
||||
def enable_editing_work_plan(sequence, work_plan=None):
|
||||
load_work_plan_attributes(sequence, work_plan)
|
||||
sequence.load_work_plan_attributes(work_plan)
|
||||
sequence.enable_editing_work_plan(work_plan)
|
||||
|
||||
|
||||
@@ -44,6 +39,6 @@ def disable_editing_work_plan(sequence):
|
||||
def edit_work_plan(ifc, sequence):
|
||||
work_plan = sequence.get_current_ifc_work_plan()
|
||||
attributes = sequence.get_work_plan_attributes()
|
||||
ifc.run("sequence.edit_work_plan", **{"work_plan": work_plan, "attributes": attributes})
|
||||
ifc.run("sequence.edit_work_plan", work_plan=work_plan, attributes=attributes)
|
||||
sequence.disable_editing_work_plan()
|
||||
sequence.load_work_plans()
|
||||
|
||||
@@ -19,6 +19,16 @@
|
||||
import blenderbim.core
|
||||
|
||||
|
||||
def reference_structure(ifc, spatial, structure=None, element=None):
|
||||
if spatial.can_reference(structure, element):
|
||||
return ifc.run("spatial.reference_structure", product=element, relating_structure=structure)
|
||||
|
||||
|
||||
def dereference_structure(ifc, spatial, structure=None, element=None):
|
||||
if spatial.can_reference(structure, element):
|
||||
return ifc.run("spatial.dereference_structure", product=element, relating_structure=structure)
|
||||
|
||||
|
||||
def assign_container(ifc, collector, spatial, structure_obj=None, element_obj=None):
|
||||
if not spatial.can_contain(structure_obj, element_obj):
|
||||
return
|
||||
|
||||
@@ -423,20 +423,19 @@ class Selector:
|
||||
|
||||
@interface
|
||||
class Sequence:
|
||||
def get_work_plans(cls): pass
|
||||
def load_work_plans(cls): pass
|
||||
def enable_editing_work_plan(cls, work_plan): pass
|
||||
def disable_editing_work_plan(cls): pass
|
||||
def enable_editing_work_plan(cls, work_plan): pass
|
||||
def export_attributes(cls): pass
|
||||
def get_current_ifc_work_plan(cls): pass
|
||||
def get_ifc_work_plan_attributes(cls): pass
|
||||
def load_work_plan_attributes(cls): pass
|
||||
def import_attributes(cls): pass
|
||||
def export_attributes(cls): pass
|
||||
def load_work_plans(cls): pass
|
||||
|
||||
|
||||
@interface
|
||||
class Spatial:
|
||||
def can_contain(cls, structure_obj, element_obj): pass
|
||||
def can_reference(cls, structure, element): pass
|
||||
def disable_editing(cls, obj): pass
|
||||
def duplicate_object_and_data(cls, obj): pass
|
||||
def enable_editing(cls, obj): pass
|
||||
|
||||
@@ -102,7 +102,7 @@ class Collector(blenderbim.core.tool.Collector):
|
||||
if element.is_a("IfcSpatialStructureElement"):
|
||||
return bpy.data.collections.get(obj.name, bpy.data.collections.new(obj.name))
|
||||
else:
|
||||
if element.is_a("IfcSpatialElement"):
|
||||
if element.is_a("IfcSpatialStructureElement") or element.is_a("IfcExternalSpatialStructureElement"):
|
||||
return bpy.data.collections.get(obj.name, bpy.data.collections.new(obj.name))
|
||||
|
||||
if element.is_a("IfcGrid"):
|
||||
|
||||
@@ -26,27 +26,19 @@ import blenderbim.bim.module.sequence.helper as helper
|
||||
|
||||
|
||||
class Sequence(blenderbim.core.tool.Sequence):
|
||||
@classmethod
|
||||
def get_work_plans(cls):
|
||||
work_plans = {}
|
||||
for work_plan in tool.Ifc.get().by_type("IfcWorkPlan"):
|
||||
work_plans[work_plan.id()] = {"Name": work_plan.Name}
|
||||
return work_plans
|
||||
|
||||
@classmethod
|
||||
def load_work_plans(cls):
|
||||
work_plans = tool.Sequence.get_work_plans()
|
||||
props = bpy.context.scene.BIMWorkPlanProperties
|
||||
props.work_plans.clear()
|
||||
for ifc_definition_id, work_plan in work_plans.items():
|
||||
for work_plan in tool.Ifc.get().by_type("IfcWorkPlan"):
|
||||
new = props.work_plans.add()
|
||||
new.ifc_definition_id = ifc_definition_id
|
||||
new.name = work_plan["Name"] or "Unnamed"
|
||||
new.ifc_definition_id = work_plan.id()
|
||||
new.name = work_plan.Name or "Unnamed"
|
||||
|
||||
@classmethod
|
||||
def enable_editing_work_plan(cls, work_plan):
|
||||
if work_plan:
|
||||
bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan
|
||||
bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id = work_plan.id()
|
||||
bpy.context.scene.BIMWorkPlanProperties.editing_type = "ATTRIBUTES"
|
||||
|
||||
@classmethod
|
||||
@@ -55,60 +47,34 @@ class Sequence(blenderbim.core.tool.Sequence):
|
||||
|
||||
@classmethod
|
||||
def get_current_ifc_work_plan(cls):
|
||||
active_work_plan = bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id
|
||||
ifc_work_plan = tool.Ifc.get().by_id(active_work_plan)
|
||||
return ifc_work_plan
|
||||
return tool.Ifc.get().by_id(bpy.context.scene.BIMWorkPlanProperties.active_work_plan_id)
|
||||
|
||||
@classmethod
|
||||
def get_ifc_work_plan_attributes(cls, work_plan):
|
||||
if work_plan:
|
||||
ifc_work_plan = tool.Ifc.get().by_id(work_plan)
|
||||
data = ifc_work_plan.get_info()
|
||||
del data["OwnerHistory"]
|
||||
if data["Creators"]:
|
||||
data["Creators"] = [p.id() for p in data["Creators"]]
|
||||
data["CreationDate"] = ifcopenshell.util.date.ifc2datetime(data["CreationDate"])
|
||||
data["StartTime"] = ifcopenshell.util.date.ifc2datetime(data["StartTime"])
|
||||
if data["FinishTime"]:
|
||||
data["FinishTime"] = ifcopenshell.util.date.ifc2datetime(data["FinishTime"])
|
||||
data["IsDecomposedBy"] = []
|
||||
for rel in ifc_work_plan.IsDecomposedBy:
|
||||
data["IsDecomposedBy"].extend([o.id() for o in rel.RelatedObjects])
|
||||
return data
|
||||
def load_work_plan_attributes(cls, work_plan):
|
||||
def callback(name, prop, data):
|
||||
if name in ["CreationDate", "StartTime", "FinishTime"]:
|
||||
prop.string_value = "" if prop.is_null else data[name]
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def load_work_plan_attributes(cls, data):
|
||||
props = bpy.context.scene.BIMWorkPlanProperties
|
||||
props.work_plan_attributes.clear()
|
||||
blenderbim.bim.helper.import_attributes(
|
||||
"IfcWorkPlan", props.work_plan_attributes, data, tool.Sequence.import_attributes
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def import_attributes(name, prop, data):
|
||||
if name in ["CreationDate", "StartTime", "FinishTime"]:
|
||||
prop.string_value = "" if prop.is_null else data[name].isoformat()
|
||||
return True
|
||||
blenderbim.bim.helper.import_attributes2(work_plan, props.work_plan_attributes, callback)
|
||||
|
||||
@classmethod
|
||||
def get_work_plan_attributes(cls):
|
||||
props = bpy.context.scene.BIMWorkPlanProperties
|
||||
attributes = blenderbim.bim.helper.export_attributes(
|
||||
props.work_plan_attributes, tool.Sequence.export_attributes
|
||||
)
|
||||
return attributes
|
||||
def callback(attributes, prop):
|
||||
if "Date" in prop.name or "Time" in prop.name:
|
||||
if prop.is_null:
|
||||
attributes[prop.name] = None
|
||||
return True
|
||||
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
||||
return True
|
||||
elif prop.name == "Duration" or prop.name == "TotalFloat":
|
||||
if prop.is_null:
|
||||
attributes[prop.name] = None
|
||||
return True
|
||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def export_attributes(attributes, prop):
|
||||
if "Date" in prop.name or "Time" in prop.name:
|
||||
if prop.is_null:
|
||||
attributes[prop.name] = None
|
||||
return True
|
||||
attributes[prop.name] = helper.parse_datetime(prop.string_value)
|
||||
return True
|
||||
elif prop.name == "Duration" or prop.name == "TotalFloat":
|
||||
if prop.is_null:
|
||||
attributes[prop.name] = None
|
||||
return True
|
||||
attributes[prop.name] = helper.parse_duration(prop.string_value)
|
||||
return True
|
||||
props = bpy.context.scene.BIMWorkPlanProperties
|
||||
return blenderbim.bim.helper.export_attributes(props.work_plan_attributes, callback)
|
||||
|
||||
@@ -35,12 +35,28 @@ class Spatial(blenderbim.core.tool.Spatial):
|
||||
if not structure.is_a("IfcSpatialStructureElement"):
|
||||
return False
|
||||
else:
|
||||
if not structure.is_a("IfcSpatialElement"):
|
||||
if not structure.is_a("IfcSpatialStructureElement") and not structure.is_a(
|
||||
"IfcExternalSpatialStructureElement"
|
||||
):
|
||||
return False
|
||||
if not hasattr(element, "ContainedInStructure"):
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def can_reference(cls, structure, element):
|
||||
if not structure or not element:
|
||||
return False
|
||||
if tool.Ifc.get_schema() == "IFC2X3":
|
||||
if not structure.is_a("IfcSpatialStructureElement"):
|
||||
return False
|
||||
else:
|
||||
if not structure.is_a("IfcSpatialElement"):
|
||||
return False
|
||||
if not hasattr(element, "ReferencedInStructures"):
|
||||
return False
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def disable_editing(cls, obj):
|
||||
obj.BIMObjectSpatialProperties.is_editing = False
|
||||
|
||||
@@ -30,9 +30,9 @@ class Obj2Ifc:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def execute(self):
|
||||
def execute(self, version="IFC4"):
|
||||
self.basename = Path(self.path).stem
|
||||
self.create_ifc_file()
|
||||
self.create_ifc_file(version)
|
||||
mesh_set = pymeshlab.MeshSet()
|
||||
mesh_set.load_new_mesh(self.path)
|
||||
|
||||
@@ -87,10 +87,10 @@ class Obj2Ifc:
|
||||
# OBJ swaps Y and Z axis
|
||||
return [coordinates[0], -coordinates[2], coordinates[1]]
|
||||
|
||||
def create_ifc_file(self):
|
||||
self.file = ifcopenshell.api.run("project.create_file", version="IFC2X3")
|
||||
def create_ifc_file(self, version):
|
||||
self.file = ifcopenshell.api.run("project.create_file", version=version)
|
||||
person = ifcopenshell.api.run("owner.add_person", self.file)
|
||||
person.Id = person.GivenName = None
|
||||
person[0] = person.GivenName = None
|
||||
person.FamilyName = "user"
|
||||
org = ifcopenshell.api.run("owner.add_organisation", self.file)
|
||||
org.Id = None
|
||||
|
||||
@@ -30,9 +30,9 @@ class Obj2Ifc:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
|
||||
def execute(self):
|
||||
def execute(self, version="IFC4"):
|
||||
self.basename = Path(self.path).stem
|
||||
self.create_ifc_file()
|
||||
self.create_ifc_file(version)
|
||||
self.scene = pywavefront.Wavefront(self.path, create_materials=True, collect_faces=True)
|
||||
for mesh in self.scene.mesh_list:
|
||||
ifc_faces = []
|
||||
@@ -73,10 +73,10 @@ class Obj2Ifc:
|
||||
ifcopenshell.api.run("spatial.assign_container", self.file, product=product, relating_structure=self.storey)
|
||||
self.file.write(self.path.replace(".obj", ".ifc"))
|
||||
|
||||
def create_ifc_file(self):
|
||||
self.file = ifcopenshell.api.run("project.create_file", version="IFC2X3")
|
||||
def create_ifc_file(self, version):
|
||||
self.file = ifcopenshell.api.run("project.create_file", version=version)
|
||||
person = ifcopenshell.api.run("owner.add_person", self.file)
|
||||
person.Id = person.GivenName = None
|
||||
person[0] = person.GivenName = None
|
||||
person.FamilyName = "user"
|
||||
org = ifcopenshell.api.run("owner.add_organisation", self.file)
|
||||
org.Id = None
|
||||
|
||||
@@ -63,7 +63,7 @@ Scenario: Enable pset editing - work schedule
|
||||
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='WorkSchedule')"
|
||||
|
||||
Scenario: Enable pset editing - resource
|
||||
Scenario: Enable pset editing - resource with time series properties
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_resources"
|
||||
And I press "bim.add_resource(ifc_class='IfcSubContractResource', resource=0)"
|
||||
@@ -73,6 +73,31 @@ Scenario: Enable pset editing - resource
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='Resource')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable pset editing - resource with regular single properties
|
||||
Given an empty IFC project
|
||||
And I press "bim.load_resources"
|
||||
And I press "bim.add_resource(ifc_class='IfcCrewResource', resource=0)"
|
||||
And the variable "resource" is "{ifc}.by_type('IfcCrewResource')[-1].id()"
|
||||
And I press "bim.add_resource(ifc_class='IfcLaborResource', resource={resource})"
|
||||
And I set "scene.BIMResourceProperties.active_resource_index" to "1"
|
||||
And I set "scene.ResourcePsetProperties.pset_name" to "EPset_Productivity"
|
||||
And I press "bim.add_pset(obj_type='Resource')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcPropertySet')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='Resource')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Enable pset editing - task
|
||||
Given an empty IFC project
|
||||
And I press "bim.add_work_schedule"
|
||||
And the variable "work_schedule" is "IfcStore.get_file().by_type('IfcWorkSchedule')[0].id()"
|
||||
And I press "bim.enable_editing_tasks(work_schedule={work_schedule})"
|
||||
And I press "bim.add_summary_task(work_schedule={work_schedule})"
|
||||
And I set "scene.TaskPsetProperties.qto_name" to "Qto_TaskBaseQuantities"
|
||||
And I press "bim.add_qto(obj_type='Task')"
|
||||
And the variable "pset" is "{ifc}.by_type('IfcElementQuantity')[-1].id()"
|
||||
When I press "bim.enable_pset_editing(pset_id={pset}, obj='', obj_type='Task')"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Copy property to selected - copy property
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
|
||||
@@ -41,6 +41,31 @@ Scenario: Copy to container
|
||||
And I press "bim.copy_to_container"
|
||||
Then the object "IfcWall/Cube.001" is in the collection "IfcSite/My Site"
|
||||
|
||||
Scenario: Reference structure
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
When I set "scene.BIMSpatialProperties.containers[0].is_selected" to "True"
|
||||
And I press "bim.reference_structure"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Dereference structure
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
And the object "Cube" is selected
|
||||
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||
And I press "bim.assign_class"
|
||||
And the object "IfcWall/Cube" is selected
|
||||
And I press "bim.enable_editing_container"
|
||||
When I set "scene.BIMSpatialProperties.containers[0].is_selected" to "True"
|
||||
And I press "bim.reference_structure"
|
||||
And I press "bim.dereference_structure"
|
||||
Then nothing happens
|
||||
|
||||
Scenario: Select container
|
||||
Given an empty IFC project
|
||||
And I add a cube
|
||||
|
||||
@@ -223,7 +223,7 @@ class TestAddDrawing:
|
||||
ifc.run(
|
||||
"group.edit_group", group="group", attributes={"Name": "name", "ObjectType": "DRAWING"}
|
||||
).should_be_called()
|
||||
ifc.run("group.assign_group", group="group", product=["element"]).should_be_called()
|
||||
ifc.run("group.assign_group", group="group", products=["element"]).should_be_called()
|
||||
collector.assign("obj").should_be_called()
|
||||
ifc.run("pset.add_pset", product="element", name="EPset_Drawing").should_be_called().will_return("pset")
|
||||
ifc.run(
|
||||
@@ -293,7 +293,7 @@ class TestAddAnnotation:
|
||||
ifc_representation_class="ifc_representation_class",
|
||||
).should_be_called().will_return("element")
|
||||
drawing.get_drawing_group("drawing").should_be_called().will_return("group")
|
||||
ifc.run("group.assign_group", group="group", product=["element"]).should_be_called()
|
||||
ifc.run("group.assign_group", group="group", products=["element"]).should_be_called()
|
||||
collector.assign("obj").should_be_called()
|
||||
drawing.enable_editing("obj").should_be_called()
|
||||
subject.add_annotation(ifc, collector, drawing, drawing="drawing", object_type="object_type")
|
||||
|
||||
@@ -20,6 +20,20 @@ import blenderbim.core.spatial as subject
|
||||
from test.core.bootstrap import ifc, collector, spatial
|
||||
|
||||
|
||||
class TestReferenceStructure:
|
||||
def test_run(self, ifc, spatial):
|
||||
spatial.can_reference("structure", "element").should_be_called().will_return(True)
|
||||
ifc.run("spatial.reference_structure", product="element", relating_structure="structure").should_be_called()
|
||||
subject.reference_structure(ifc, spatial, structure="structure", element="element")
|
||||
|
||||
|
||||
class TestDereferenceStructure:
|
||||
def test_run(self, ifc, spatial):
|
||||
spatial.can_reference("structure", "element").should_be_called().will_return(True)
|
||||
ifc.run("spatial.dereference_structure", product="element", relating_structure="structure").should_be_called()
|
||||
subject.dereference_structure(ifc, spatial, structure="structure", element="element")
|
||||
|
||||
|
||||
class TestAssignContainer:
|
||||
def test_run(self, ifc, collector, spatial):
|
||||
spatial.can_contain("structure_obj", "element_obj").should_be_called().will_return(True)
|
||||
|
||||
@@ -56,7 +56,7 @@ class TestAssign(NewFile):
|
||||
assert len(wall_obj.users_collection) == 1
|
||||
assert "IfcProject" in wall_obj.users_collection[0].name
|
||||
|
||||
def test_in_decomposition_mode_spatial_elements_are_placed_in_a_collection_of_the_same_name(self):
|
||||
def test_in_decomposition_mode_spatial_structure_elements_are_placed_in_a_collection_of_the_same_name(self):
|
||||
bpy.ops.bim.create_project()
|
||||
space_obj = bpy.data.objects.new("IfcSpace/Name", None)
|
||||
space_element = tool.Ifc.get().createIfcSpace()
|
||||
@@ -72,6 +72,22 @@ class TestAssign(NewFile):
|
||||
assert len(space_obj.users_collection) == 1
|
||||
assert space_obj.users_collection[0].name == space_obj.name
|
||||
|
||||
def test_in_decomposition_mode_spatial_zone_elements_are_not_placed_in_a_collection_of_the_same_name(self):
|
||||
bpy.ops.bim.create_project()
|
||||
space_obj = bpy.data.objects.new("IfcSpaceZone/Name", None)
|
||||
space_element = tool.Ifc.get().createIfcSpatialZone()
|
||||
tool.Ifc.link(space_element, space_obj)
|
||||
bpy.context.scene.collection.objects.link(space_obj)
|
||||
ifcopenshell.api.run(
|
||||
"aggregate.assign_object",
|
||||
tool.Ifc.get(),
|
||||
relating_object=tool.Ifc.get().by_type("IfcSite")[0],
|
||||
product=space_element,
|
||||
)
|
||||
subject.assign(space_obj)
|
||||
assert len(space_obj.users_collection) == 1
|
||||
assert space_obj.users_collection[0].name != space_obj.name
|
||||
|
||||
def test_in_decomposition_mode_aggregates_are_placed_in_a_collection_of_the_same_name(self):
|
||||
bpy.ops.bim.create_project()
|
||||
element_obj = bpy.data.objects.new("IfcElementAssembly/Name", None)
|
||||
@@ -222,7 +238,7 @@ class TestAssign(NewFile):
|
||||
tool.Ifc.link(element, element_obj)
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get())
|
||||
group.ObjectType = "DRAWING"
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), product=[element], group=group)
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=group)
|
||||
subject.assign(element_obj)
|
||||
assert element_obj.users_collection[0].name == "IfcGroup/Unnamed"
|
||||
assert bpy.data.collections.get("Views").children.get("IfcGroup/Unnamed")
|
||||
@@ -235,7 +251,7 @@ class TestAssign(NewFile):
|
||||
tool.Ifc.link(element, element_obj)
|
||||
group = ifcopenshell.api.run("group.add_group", tool.Ifc.get())
|
||||
group.ObjectType = "DRAWING"
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), product=[element], group=group)
|
||||
ifcopenshell.api.run("group.assign_group", tool.Ifc.get(), products=[element], group=group)
|
||||
subject.assign(element_obj)
|
||||
assert element_obj.users_collection[0].name == "IfcGroup/Unnamed"
|
||||
assert bpy.data.collections.get("Views").children.get("IfcGroup/Unnamed")
|
||||
|
||||
@@ -260,7 +260,7 @@ class TestGetDrawingGroup(NewFile):
|
||||
tool.Ifc.set(ifc)
|
||||
element = ifc.createIfcAnnotation()
|
||||
group = ifcopenshell.api.run("group.add_group", ifc)
|
||||
ifcopenshell.api.run("group.assign_group", ifc, product=[element], group=group)
|
||||
ifcopenshell.api.run("group.assign_group", ifc, products=[element], group=group)
|
||||
assert subject.get_drawing_group(element) == group
|
||||
|
||||
|
||||
@@ -280,7 +280,7 @@ class TestGetGroupElements(NewFile):
|
||||
tool.Ifc.set(ifc)
|
||||
element = ifc.createIfcAnnotation()
|
||||
group = ifcopenshell.api.run("group.add_group", ifc)
|
||||
ifcopenshell.api.run("group.assign_group", ifc, product=[element], group=group)
|
||||
ifcopenshell.api.run("group.assign_group", ifc, products=[element], group=group)
|
||||
assert subject.get_group_elements(group) == (element,)
|
||||
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ class TestImplementsTool(NewFile):
|
||||
|
||||
|
||||
class TestCanContain(NewFile):
|
||||
def test_a_spatial_element_can_contain_an_element(self):
|
||||
def test_a_spatial_structure_element_can_contain_an_element(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
structure = ifc.createIfcSite()
|
||||
@@ -42,7 +42,7 @@ class TestCanContain(NewFile):
|
||||
tool.Ifc.link(element, element_obj)
|
||||
assert subject.can_contain(structure_obj, element_obj) is True
|
||||
|
||||
def test_a_spatial_element_can_contain_an_element_ifc2x3(self):
|
||||
def test_a_spatial_structure_element_can_contain_an_element_ifc2x3(self):
|
||||
ifc = ifcopenshell.file(schema="IFC2X3")
|
||||
tool.Ifc.set(ifc)
|
||||
structure = ifc.createIfcSite()
|
||||
@@ -53,6 +53,17 @@ class TestCanContain(NewFile):
|
||||
tool.Ifc.link(element, element_obj)
|
||||
assert subject.can_contain(structure_obj, element_obj) is True
|
||||
|
||||
def test_a_spatial_zone_element_cannot_contain_an_element(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
structure = ifc.createIfcSpatialZone()
|
||||
structure_obj = bpy.data.objects.new("Object", None)
|
||||
tool.Ifc.link(structure, structure_obj)
|
||||
element = ifc.createIfcWall()
|
||||
element_obj = bpy.data.objects.new("Object", None)
|
||||
tool.Ifc.link(element, element_obj)
|
||||
assert subject.can_contain(structure_obj, element_obj) is False
|
||||
|
||||
def test_unlinked_elements_cannot_contain_anything(self):
|
||||
structure_obj = bpy.data.objects.new("Object", None)
|
||||
element_obj = bpy.data.objects.new("Object", None)
|
||||
@@ -92,6 +103,25 @@ class TestCanContain(NewFile):
|
||||
assert subject.can_contain(structure_obj, element_obj) is True
|
||||
|
||||
|
||||
class TestCanReference(NewFile):
|
||||
def test_an_element_can_reference_a_spatial_element(self):
|
||||
ifc = ifcopenshell.file()
|
||||
assert subject.can_reference(ifc.createIfcSite(), ifc.createIfcWall()) is True
|
||||
|
||||
def test_an_element_can_reference_a_spatial_element_ifc2x3(self):
|
||||
ifc = ifcopenshell.file(schema="IFC2X3")
|
||||
tool.Ifc.set(ifc)
|
||||
assert subject.can_reference(ifc.createIfcSite(), ifc.createIfcWall()) is True
|
||||
|
||||
def test_a_non_spatial_element_cannot_reference_anything(self):
|
||||
ifc = ifcopenshell.file()
|
||||
assert subject.can_reference(ifc.createIfcWall(), ifc.createIfcWall()) is False
|
||||
|
||||
def test_a_non_element_cannot_reference_anything(self):
|
||||
ifc = ifcopenshell.file()
|
||||
assert subject.can_reference(ifc.createIfcSite(), ifc.createIfcTask()) is False
|
||||
|
||||
|
||||
class TestDisableEditing(NewFile):
|
||||
def test_run(self):
|
||||
obj = bpy.data.objects.new("Object", None)
|
||||
|
||||
@@ -121,7 +121,6 @@ class IfcDiff:
|
||||
print(" - {} item(s) were changed either geometrically or with data".format(len(self.change_register.keys())))
|
||||
print("# Diff finished in {:.2f} seconds".format(time.time() - start))
|
||||
logging.disable(logging.NOTSET)
|
||||
return f"# Diff finished in {time.time() - start:.2f} seconds"
|
||||
|
||||
def export(self):
|
||||
with open(self.output_file, "w", encoding="utf-8") as diff_file:
|
||||
|
||||
@@ -24,7 +24,7 @@ class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"products": None,
|
||||
"group": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
@@ -37,13 +37,13 @@ class Usecase:
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatedObjects": self.settings["product"],
|
||||
"RelatedObjects": self.settings["products"],
|
||||
"RelatingGroup": self.settings["group"],
|
||||
}
|
||||
)
|
||||
rel = self.settings["group"].IsGroupedBy[0]
|
||||
related_objects = set(rel.RelatedObjects) or set()
|
||||
for obj in self.settings["product"]:
|
||||
for obj in self.settings["products"]:
|
||||
related_objects.add(obj)
|
||||
rel.RelatedObjects = list(related_objects)
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {"product": None, "relating_structure": None}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
for rel in self.settings["product"].ReferencedInStructures:
|
||||
if rel.RelatingStructure != self.settings["relating_structure"]:
|
||||
continue
|
||||
related_elements = list(rel.RelatedElements)
|
||||
related_elements.remove(self.settings["product"])
|
||||
if related_elements:
|
||||
rel.RelatedElements = related_elements
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": rel})
|
||||
else:
|
||||
self.file.remove(rel)
|
||||
@@ -0,0 +1,57 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import ifcopenshell
|
||||
import ifcopenshell.api
|
||||
|
||||
|
||||
class Usecase:
|
||||
def __init__(self, file, **settings):
|
||||
self.file = file
|
||||
self.settings = {
|
||||
"product": None,
|
||||
"relating_structure": None,
|
||||
}
|
||||
for key, value in settings.items():
|
||||
self.settings[key] = value
|
||||
|
||||
def execute(self):
|
||||
referenced_in_structures = self.settings["product"].ReferencedInStructures
|
||||
references_elements = self.settings["relating_structure"].ReferencesElements
|
||||
|
||||
for rel in referenced_in_structures:
|
||||
if rel.RelatingStructure == self.settings["relating_structure"]:
|
||||
return
|
||||
|
||||
if references_elements:
|
||||
related_elements = list(references_elements[0].RelatedElements)
|
||||
related_elements.append(self.settings["product"])
|
||||
references_elements[0].RelatedElements = related_elements
|
||||
ifcopenshell.api.run("owner.update_owner_history", self.file, **{"element": references_elements[0]})
|
||||
else:
|
||||
references_elements = self.file.create_entity(
|
||||
"IfcRelReferencedInSpatialStructure",
|
||||
**{
|
||||
"GlobalId": ifcopenshell.guid.new(),
|
||||
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
|
||||
"RelatedElements": [self.settings["product"]],
|
||||
"RelatingStructure": self.settings["relating_structure"],
|
||||
}
|
||||
)
|
||||
|
||||
return references_elements
|
||||
@@ -23,7 +23,7 @@ def get_psets(element, psets_only=False, qtos_only=False, should_inherit=True):
|
||||
"""Retrieve property sets, their related properties' names & values and ids.
|
||||
|
||||
:param element: The IFC Element entity
|
||||
:param psets_only: Default as False. Set to true if only property sets are needed.
|
||||
:param psets_only: Default as False. Set to true if only property sets are needed.
|
||||
:param qtos_only: Default as False. Set to true if only quantities are needed.
|
||||
:param should_inherit: Default as True. Set to false if you don't want to inherit property sets from the Type.
|
||||
:return: dictionnary: key, value pair of psets' names and their properties' names & values
|
||||
@@ -133,7 +133,7 @@ def get_predefined_type(element):
|
||||
def get_type(element):
|
||||
"""
|
||||
Retrieves the Element Type entity related to an element entity.
|
||||
|
||||
|
||||
:param element: The IFC Element entity
|
||||
:return: The Element Type entity defining the element
|
||||
|
||||
@@ -178,7 +178,7 @@ def get_material(element, should_skip_usage=False, should_inherit=True):
|
||||
def get_elements_by_material(ifc_file, material):
|
||||
"""
|
||||
Retrieves the elements related to a material.
|
||||
|
||||
|
||||
:param ifc_file: The IFC file
|
||||
:param material: The IFC Material entity
|
||||
:return: The elements related to the material
|
||||
@@ -212,7 +212,7 @@ def get_elements_by_material(ifc_file, material):
|
||||
def get_elements_by_style(ifc_file, style):
|
||||
"""
|
||||
Retrieves the elements related to a style.
|
||||
|
||||
|
||||
:param ifc_file: The IFC file
|
||||
:param style: The IFC Style entity
|
||||
:return: The elements related to the style
|
||||
@@ -282,17 +282,22 @@ def get_layers(ifc_file, element):
|
||||
|
||||
def get_container(element, should_get_direct=False):
|
||||
"""
|
||||
Retrieves the container of an element.
|
||||
|
||||
:param element: The IFC element
|
||||
:param should_get_direct: If True, the container of the element is returned. If False, the aggregate's container is returned
|
||||
:return: The container of the element, or its aggregate's container.
|
||||
Retrieves the spatial structure container of an element.
|
||||
|
||||
:param element: The IFC element
|
||||
:type element: ifcopenshell.entity_instance.entity_instance
|
||||
:param should_get_direct: If True, a result is only returned if the element
|
||||
is directly contained in a spatial structure element. If False, an
|
||||
indirect spatial container may be returned, such as if an element is a
|
||||
part of an aggregate, and then if that aggregate is contained in a
|
||||
spatial structure element.
|
||||
:type should_get_direct: bool
|
||||
:return: The direct or indirect container of the element or None.
|
||||
|
||||
Example::
|
||||
element = file.by_type("IfcWall")[0]
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
|
||||
element = file.by_type("IfcWall")[0]
|
||||
container = ifcopenshell.util.element.get_container(element)
|
||||
"""
|
||||
if should_get_direct:
|
||||
if hasattr(element, "ContainedInStructure") and element.ContainedInStructure:
|
||||
@@ -305,17 +310,34 @@ def get_container(element, should_get_direct=False):
|
||||
return element.ContainedInStructure[0].RelatingStructure
|
||||
|
||||
|
||||
def get_referenced_structures(element):
|
||||
"""
|
||||
Retreives a list of referenced structural elements
|
||||
|
||||
:param element: The IFC element
|
||||
:type element: ifcopenshell.entity_instance.entity_instance
|
||||
|
||||
Example::
|
||||
|
||||
element = file.by_type("IfcWall")[0]
|
||||
print(ifcopenshell.util.element.get_referenced_structures(element))
|
||||
"""
|
||||
if hasattr(element, "ReferencedInStructures"):
|
||||
return [r.RelatingStructure for r in element.ReferencedInStructures]
|
||||
return []
|
||||
|
||||
|
||||
def get_decomposition(element):
|
||||
"""
|
||||
Retrieves the decomposition of an element.
|
||||
|
||||
|
||||
:param element: The IFC element
|
||||
:return: The decomposition of the element
|
||||
|
||||
Example::
|
||||
element = file.by_type("IfcProject")[0]
|
||||
decomposition = ifcopenshell.util.element.get_decomposition(element)
|
||||
|
||||
element = file.by_type("IfcProject")[0]
|
||||
decomposition = ifcopenshell.util.element.get_decomposition(element)
|
||||
"""
|
||||
queue = [element]
|
||||
results = []
|
||||
@@ -333,7 +355,7 @@ def get_decomposition(element):
|
||||
def get_aggregate(element):
|
||||
"""
|
||||
Retrieves the aggregate of an element.
|
||||
|
||||
|
||||
:param element: The IFC element
|
||||
:return: The aggregate of the element
|
||||
|
||||
@@ -349,14 +371,14 @@ def get_aggregate(element):
|
||||
def get_parts(element):
|
||||
"""
|
||||
Retrieves the parts of an element.
|
||||
|
||||
|
||||
:param element: The IFC element
|
||||
:return: The parts of the element
|
||||
|
||||
Example::
|
||||
element = file.by_type("IfcElementAssembly")[0]
|
||||
parts = ifcopenshell.util.element.get_parts(element)
|
||||
|
||||
|
||||
"""
|
||||
if hasattr(element, "IsDecomposedBy") and element.IsDecomposedBy:
|
||||
return element.IsDecomposedBy[0].RelatedObjects
|
||||
|
||||
@@ -238,8 +238,12 @@ class Selector:
|
||||
def filter_element(cls, element, element_value, comparison, value):
|
||||
if comparison.startswith("not"):
|
||||
return not cls.filter_element(element, element_value, comparison[3:], value)
|
||||
elif comparison == "equal" and isinstance(element_value, list):
|
||||
return value in element_value
|
||||
elif comparison == "equal":
|
||||
return element_value == value
|
||||
elif comparison == "contains" and isinstance(element_value, list):
|
||||
return bool([ev for ev in element_value if value in str(ev)])
|
||||
elif comparison == "contains":
|
||||
return value in str(element_value)
|
||||
elif comparison == "morethan":
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import test.bootstrap
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class TestDereferenceStructure(test.bootstrap.IFC4):
|
||||
def test_removing_a_container(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
||||
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
||||
|
||||
def test_doing_nothing_if_no_container(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
||||
assert ifcopenshell.util.element.get_referenced_structures(subelement) == []
|
||||
|
||||
def test_updating_the_rel_when_a_reference_is_removed_with_multipled_elements(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement1, relating_structure=element)
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement2, relating_structure=element)
|
||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement1, relating_structure=element)
|
||||
assert self.file.by_type("IfcRelReferencedInSpatialStructure")[0].RelatedElements == (subelement2,)
|
||||
|
||||
def test_deleting_the_rel_when_a_container_is_removed_with_no_elements(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
||||
ifcopenshell.api.run("spatial.dereference_structure", self.file, product=subelement, relating_structure=element)
|
||||
assert len(self.file.by_type("IfcRelReferencedInSpatialStructure")) == 0
|
||||
@@ -0,0 +1,50 @@
|
||||
# IfcOpenShell - IFC toolkit and geometry engine
|
||||
# Copyright (C) 2022 Dion Moult <dion@thinkmoult.com>
|
||||
#
|
||||
# This file is part of IfcOpenShell.
|
||||
#
|
||||
# IfcOpenShell is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# IfcOpenShell is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with IfcOpenShell. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import pytest
|
||||
import test.bootstrap
|
||||
import ifcopenshell.api
|
||||
import ifcopenshell.util.element
|
||||
|
||||
|
||||
class TestReferenceStructure(test.bootstrap.IFC4):
|
||||
def test_referencing_a_structure(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
rel = ifcopenshell.api.run(
|
||||
"spatial.reference_structure", self.file, product=subelement, relating_structure=element
|
||||
)
|
||||
assert ifcopenshell.util.element.get_referenced_structures(subelement) == [element]
|
||||
assert rel.is_a("IfcRelReferencedInSpatialStructure")
|
||||
|
||||
def test_doing_nothing_if_the_structure_is_already_referenced(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
||||
total_elements = len([e for e in self.file])
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement, relating_structure=element)
|
||||
assert len([e for e in self.file]) == total_elements
|
||||
|
||||
def test_that_old_relationships_are_updated_if_they_still_contain_elements(self):
|
||||
element1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement1, relating_structure=element1)
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=subelement2, relating_structure=element1)
|
||||
rel = subelement1.ReferencedInStructures[0]
|
||||
assert len(rel.RelatedElements) == 2
|
||||
@@ -538,6 +538,18 @@ class TestGetContainerIFC4(test.bootstrap.IFC4):
|
||||
assert subject.get_container(subelement, should_get_direct=True) is None
|
||||
|
||||
|
||||
class TestGetReferencedStructures(test.bootstrap.IFC4):
|
||||
def test_getting_references_of_an_element(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
assert subject.get_referenced_structures(element) == []
|
||||
building = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=element, relating_structure=building)
|
||||
assert subject.get_referenced_structures(element) == [building]
|
||||
building2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||
ifcopenshell.api.run("spatial.reference_structure", self.file, product=element, relating_structure=building2)
|
||||
assert subject.get_referenced_structures(element) == [building, building2]
|
||||
|
||||
|
||||
class TestGetDecompositionIFC4(test.bootstrap.IFC4):
|
||||
def test_getting_decomposed_subelements_of_an_element(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcElementAssembly")
|
||||
|
||||
@@ -37,8 +37,8 @@ class TestSelector(test.bootstrap.IFC4):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
element.Name = "Foobar"
|
||||
ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSlab")
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Name]') == [element]
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Description]') == []
|
||||
assert subject.Selector.parse(self.file, ".IfcElement[Name]") == [element]
|
||||
assert subject.Selector.parse(self.file, ".IfcElement[Description]") == []
|
||||
|
||||
def test_selecting_by_attribute(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
@@ -51,8 +51,8 @@ class TestSelector(test.bootstrap.IFC4):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo]') == [element]
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Fox]') == []
|
||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Foo]") == [element]
|
||||
assert subject.Selector.parse(self.file, ".IfcElement[Foo_Bar.Fox]") == []
|
||||
|
||||
def test_selecting_by_string_property(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
@@ -60,6 +60,15 @@ class TestSelector(test.bootstrap.IFC4):
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"Foo": "Bar"})
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo="Bar"]') == [element]
|
||||
|
||||
def test_selecting_by_enumerated_property(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Pset_WallCommon")
|
||||
template = ifcopenshell.util.pset.get_template("IFC4").get_by_name("Pset_WallCommon")
|
||||
ifcopenshell.api.run(
|
||||
"pset.edit_pset", self.file, pset=pset, properties={"Status": ["NEW"]}, pset_template=template
|
||||
)
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Pset_WallCommon.Status="NEW"]') == [element]
|
||||
|
||||
def test_selecting_by_integer_property(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="Foo_Bar")
|
||||
@@ -121,17 +130,20 @@ class TestSelector(test.bootstrap.IFC4):
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset_2, properties={"Foo": "BOO"})
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo != "Bar"]') == [element_2]
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[Foo_Bar.Foo != "BOO"]') == [element_1]
|
||||
|
||||
|
||||
def test_selecting_when_attribute_is_none(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[PredefinedType !="non-existent predefined type"]') == [element]
|
||||
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[PredefinedType !="non-existent predefined type"]') == [
|
||||
element
|
||||
]
|
||||
|
||||
def test_selecting_a_property_which_includes_non_standard_characters(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
pset = ifcopenshell.api.run("pset.add_pset", self.file, product=element, name="a !%$§&/()?|*-+,€~#@µ^°a")
|
||||
ifcopenshell.api.run("pset.edit_pset", self.file, pset=pset, properties={"a !%$§&/()?|*-+,€~#@µ^°a": "Bar"})
|
||||
assert subject.Selector.parse(self.file, '.IfcElement[a !%$§&/()?|*-+,€~#@µ^°a.a !%$§&/()?|*-+,€~#@µ^°a="Bar"]') == [element]
|
||||
assert subject.Selector.parse(
|
||||
self.file, '.IfcElement[a !%$§&/()?|*-+,€~#@µ^°a.a !%$§&/()?|*-+,€~#@µ^°a="Bar"]'
|
||||
) == [element]
|
||||
|
||||
def test_comparing_if_value_is_in_a_list(self):
|
||||
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||
|
||||
Reference in New Issue
Block a user