mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
New unassign aggregate feature.
This commit is contained in:
@@ -21,6 +21,7 @@ from . import ui, prop, operator
|
|||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
operator.AssignObject,
|
operator.AssignObject,
|
||||||
|
operator.UnassignObject,
|
||||||
operator.EnableEditingAggregate,
|
operator.EnableEditingAggregate,
|
||||||
operator.DisableEditingAggregate,
|
operator.DisableEditingAggregate,
|
||||||
operator.AddAggregate,
|
operator.AddAggregate,
|
||||||
|
|||||||
@@ -13,7 +13,11 @@ class AggregateData:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.data = {"has_aggregate": cls.has_aggregate(), "label": cls.get_label()}
|
cls.data = {
|
||||||
|
"has_aggregate": cls.has_aggregate(),
|
||||||
|
"label": cls.get_label(),
|
||||||
|
"relating_object_id": cls.get_relating_object_id(),
|
||||||
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -25,3 +29,9 @@ class AggregateData:
|
|||||||
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get_entity(bpy.context.active_object))
|
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get_entity(bpy.context.active_object))
|
||||||
if aggregate:
|
if aggregate:
|
||||||
return f"{aggregate.is_a()}/{aggregate.Name or ''}"
|
return f"{aggregate.is_a()}/{aggregate.Name or ''}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_relating_object_id(cls):
|
||||||
|
aggregate = ifcopenshell.util.element.get_aggregate(tool.Ifc.get_entity(bpy.context.active_object))
|
||||||
|
if aggregate:
|
||||||
|
return aggregate.id()
|
||||||
|
|||||||
@@ -47,6 +47,22 @@ class AssignObject(bpy.types.Operator, Operator):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class UnassignObject(bpy.types.Operator, Operator):
|
||||||
|
bl_idname = "bim.unassign_object"
|
||||||
|
bl_label = "Unassign Object"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
relating_object: bpy.props.IntProperty()
|
||||||
|
related_object: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
core.unassign_object(
|
||||||
|
tool.Ifc,
|
||||||
|
tool.Collector,
|
||||||
|
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
|
||||||
|
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnableEditingAggregate(bpy.types.Operator, Operator):
|
class EnableEditingAggregate(bpy.types.Operator, Operator):
|
||||||
bl_idname = "bim.enable_editing_aggregate"
|
bl_idname = "bim.enable_editing_aggregate"
|
||||||
bl_label = "Enable Editing Aggregate"
|
bl_label = "Enable Editing Aggregate"
|
||||||
|
|||||||
@@ -60,7 +60,12 @@ class BIM_PT_aggregate(Panel):
|
|||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
if AggregateData.data["has_aggregate"]:
|
if AggregateData.data["has_aggregate"]:
|
||||||
row.label(text=AggregateData.data["label"])
|
row.label(text=AggregateData.data["label"])
|
||||||
|
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||||
|
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||||
|
op = row.operator("bim.unassign_object", icon="X", text="")
|
||||||
|
op.relating_object = AggregateData.data["relating_object_id"]
|
||||||
|
op.related_object = context.active_object.BIMObjectProperties.ifc_definition_id
|
||||||
else:
|
else:
|
||||||
row.label(text="No Aggregate Found")
|
row.label(text="No Aggregate Found")
|
||||||
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||||
|
|||||||
@@ -16,3 +16,10 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non
|
|||||||
collector.assign(related_obj)
|
collector.assign(related_obj)
|
||||||
aggregator.disable_editing(related_obj)
|
aggregator.disable_editing(related_obj)
|
||||||
return rel
|
return rel
|
||||||
|
|
||||||
|
|
||||||
|
def unassign_object(ifc, collector, relating_obj=None, related_obj=None):
|
||||||
|
rel = ifc.run("aggregate.unassign_object", product=ifc.get_entity(related_obj))
|
||||||
|
collector.assign(relating_obj)
|
||||||
|
collector.assign(related_obj)
|
||||||
|
return rel
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ class Collector(blenderbim.core.tool.Collector):
|
|||||||
object_collection.objects.link(obj)
|
object_collection.objects.link(obj)
|
||||||
|
|
||||||
if collection_collection and collection_collection.children.find(object_collection.name) == -1:
|
if collection_collection and collection_collection.children.find(object_collection.name) == -1:
|
||||||
|
if bpy.context.scene.collection.children.find(object_collection.name) != -1:
|
||||||
|
bpy.context.scene.collection.children.unlink(object_collection)
|
||||||
for collection in bpy.data.collections:
|
for collection in bpy.data.collections:
|
||||||
if collection.children.find(object_collection.name) != -1:
|
if collection.children.find(object_collection.name) != -1:
|
||||||
collection.children.unlink(object_collection)
|
collection.children.unlink(object_collection)
|
||||||
|
|||||||
@@ -23,6 +23,18 @@ Scenario: Assign object
|
|||||||
And the variable "related_object" is "tool.Ifc.get().by_type('IfcBuildingStorey')[0].id()"
|
And the variable "related_object" is "tool.Ifc.get().by_type('IfcBuildingStorey')[0].id()"
|
||||||
When I press "bim.assign_object(relating_object={relating_object}, related_object={related_object})"
|
When I press "bim.assign_object(relating_object={relating_object}, related_object={related_object})"
|
||||||
Then the object "IfcSite/My Site" is in the collection "IfcSite/My Site"
|
Then the object "IfcSite/My Site" is in the collection "IfcSite/My Site"
|
||||||
Then the object "IfcBuildingStorey/My Storey" is in the collection "IfcBuildingStorey/My Storey"
|
And the object "IfcBuildingStorey/My Storey" is in the collection "IfcBuildingStorey/My Storey"
|
||||||
Then the collection "IfcBuildingStorey/My Storey" is in the collection "IfcSite/My Site"
|
And the collection "IfcBuildingStorey/My Storey" is in the collection "IfcSite/My Site"
|
||||||
|
|
||||||
|
Scenario: Unassign object
|
||||||
|
Given an empty IFC project
|
||||||
|
And the object "IfcBuildingStorey/My Storey" is selected
|
||||||
|
And I press "bim.enable_editing_aggregate"
|
||||||
|
And the variable "relating_object" is "tool.Ifc.get().by_type('IfcSite')[0].id()"
|
||||||
|
And the variable "related_object" is "tool.Ifc.get().by_type('IfcBuildingStorey')[0].id()"
|
||||||
|
And I press "bim.assign_object(relating_object={relating_object}, related_object={related_object})"
|
||||||
|
And the object "IfcBuildingStorey/My Storey" is selected
|
||||||
|
When I press "bim.unassign_object(relating_object={relating_object}, related_object={related_object})"
|
||||||
|
Then the object "IfcSite/My Site" is in the collection "IfcSite/My Site"
|
||||||
|
And the object "IfcBuildingStorey/My Storey" is in the collection "IfcBuildingStorey/My Storey"
|
||||||
|
And the collection "IfcBuildingStorey/My Storey" is in the collection "IfcProject/My Project"
|
||||||
|
|||||||
@@ -29,3 +29,12 @@ class TestAssignObject:
|
|||||||
subject.assign_object(ifc, aggregator, collector, relating_obj="relating_obj", related_obj="related_obj")
|
subject.assign_object(ifc, aggregator, collector, relating_obj="relating_obj", related_obj="related_obj")
|
||||||
== "rel"
|
== "rel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnassignObject:
|
||||||
|
def test_run(self, ifc, collector):
|
||||||
|
ifc.get_entity("related_obj").should_be_called().will_return("related_object")
|
||||||
|
ifc.run("aggregate.unassign_object", product="related_object").should_be_called().will_return("rel")
|
||||||
|
collector.assign("relating_obj").should_be_called()
|
||||||
|
collector.assign("related_obj").should_be_called()
|
||||||
|
assert subject.unassign_object(ifc, collector, relating_obj="relating_obj", related_obj="related_obj") == "rel"
|
||||||
|
|||||||
@@ -99,3 +99,21 @@ class TestAssign(NewFile):
|
|||||||
subject.assign(element_obj)
|
subject.assign(element_obj)
|
||||||
assert len(element_obj.users_collection) == 1
|
assert len(element_obj.users_collection) == 1
|
||||||
assert element_obj.users_collection[0].name == element_obj.name
|
assert element_obj.users_collection[0].name == element_obj.name
|
||||||
|
|
||||||
|
def test_in_decomposition_mode_existing_collections_are_reassigned_to_the_correct_place_in_the_hierarchy(self):
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
space_obj = bpy.data.objects.new("IfcSpace/Name", None)
|
||||||
|
space_element = tool.Ifc.get().createIfcSpace()
|
||||||
|
tool.Ifc.link(space_element, space_obj)
|
||||||
|
space_collection = bpy.data.collections.new("IfcSpace/Name")
|
||||||
|
bpy.context.scene.collection.children.link(space_collection)
|
||||||
|
space_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 bpy.context.scene.collection.children.find(space_collection.name) == -1
|
||||||
|
assert bpy.data.collections.get("IfcSite/My Site").children.find(space_collection.name) != -1
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ class Usecase:
|
|||||||
def __init__(self, file, **settings):
|
def __init__(self, file, **settings):
|
||||||
self.file = file
|
self.file = file
|
||||||
self.settings = {
|
self.settings = {
|
||||||
"relating_object": None,
|
|
||||||
"product": None,
|
"product": None,
|
||||||
}
|
}
|
||||||
for key, value in settings.items():
|
for key, value in settings.items():
|
||||||
@@ -14,7 +13,7 @@ class Usecase:
|
|||||||
|
|
||||||
def execute(self):
|
def execute(self):
|
||||||
for rel in self.settings["product"].Decomposes or []:
|
for rel in self.settings["product"].Decomposes or []:
|
||||||
if not rel.is_a("IfcRelAggregates") or rel.RelatingObject != self.settings["relating_object"]:
|
if not rel.is_a("IfcRelAggregates"):
|
||||||
continue
|
continue
|
||||||
if len(rel.RelatedObjects) == 1:
|
if len(rel.RelatedObjects) == 1:
|
||||||
return self.file.remove(rel)
|
return self.file.remove(rel)
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import pytest
|
||||||
|
import test.bootstrap
|
||||||
|
import ifcopenshell.api
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnassignObject(test.bootstrap.IFC4):
|
||||||
|
def test_unassigning_an_object(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite")
|
||||||
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
|
||||||
|
ifcopenshell.api.run("aggregate.unassign_object", self.file, product=subelement)
|
||||||
|
assert ifcopenshell.util.element.get_aggregate(subelement) is None
|
||||||
|
|
||||||
|
def test_the_rel_is_kept_if_there_are_more_decomposed_elements(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite")
|
||||||
|
subelement2 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
subelement1 = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement1, relating_object=element)
|
||||||
|
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement2, relating_object=element)
|
||||||
|
ifcopenshell.api.run("aggregate.unassign_object", self.file, product=subelement1)
|
||||||
|
assert len(self.file.by_type("IfcRelAggregates")) == 1
|
||||||
|
|
||||||
|
def test_the_rel_is_purged_if_there_are_no_more_decomposed_elements(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcSite")
|
||||||
|
subelement = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcBuilding")
|
||||||
|
ifcopenshell.api.run("aggregate.assign_object", self.file, product=subelement, relating_object=element)
|
||||||
|
ifcopenshell.api.run("aggregate.unassign_object", self.file, product=subelement)
|
||||||
|
assert len(self.file.by_type("IfcRelAggregates")) == 0
|
||||||
Reference in New Issue
Block a user