See #2140. You can now bulk unassign aggregates, and they are auto directly contained in any previous indirect containers.

This commit is contained in:
Dion Moult
2023-03-11 16:31:09 +11:00
parent c51aa910cb
commit 0dad93bf00
10 changed files with 73 additions and 46 deletions
@@ -34,7 +34,6 @@ class AggregateData:
cls.data = {
"has_relating_object": cls.has_relating_object(),
"relating_object_label": cls.get_relating_object_label(),
"relating_object_id": cls.get_relating_object_id(),
"has_related_objects": cls.has_related_objects(),
"related_objects_amount": cls.get_related_objects_amount(),
"ifc_class": cls.ifc_class(),
@@ -55,12 +54,6 @@ class AggregateData:
if aggregate:
return f"{aggregate.is_a()}/{aggregate.Name or ''}"
@classmethod
def get_relating_object_id(cls) -> int:
aggregate = cls.get_relating_object()
if aggregate:
return aggregate.id()
@classmethod
def get_related_objects(cls):
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
@@ -58,16 +58,22 @@ class BIM_OT_unassign_object(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)),
)
for obj in bpy.context.selected_objects:
element = tool.Ifc.get_entity(obj)
if not element:
continue
aggregate = ifcopenshell.util.element.get_aggregate(element)
if not aggregate:
continue
core.unassign_object(
tool.Ifc,
tool.Aggregate,
tool.Collector,
relating_obj=tool.Ifc.get_object(aggregate),
related_obj=tool.Ifc.get_object(element),
)
class BIM_OT_enable_editing_aggregate(bpy.types.Operator, Operator):
@@ -67,8 +67,6 @@ class BIM_PT_aggregate(Panel):
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:
row.label(text="No Aggregate", icon="TRIA_UP")
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
@@ -53,7 +53,7 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
class FilledOpeningGenerator:
def generate(self, filling_obj, voided_obj):
def generate(self, filling_obj, voided_obj, target=None):
props = bpy.context.scene.BIMModelProperties
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
@@ -62,8 +62,14 @@ class FilledOpeningGenerator:
if not voided_obj or not filling_obj:
return
if filling.FillsVoids:
ifcopenshell.api.run(
"void.remove_opening", tool.Ifc.get(), opening=filling.FillsVoids[0].RelatingOpeningElement
)
if target is None:
target = bpy.context.scene.cursor.location
element = tool.Ifc.get_entity(voided_obj)
target = bpy.context.scene.cursor.location
raycast = voided_obj.closest_point_on_mesh(voided_obj.matrix_world.inverted() @ target, distance=0.01)
if not raycast[0]:
target = filling_obj.matrix_world.col[3].to_3d().copy()
+6 -3
View File
@@ -37,11 +37,14 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non
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))
def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=None):
related_element = ifc.get_entity(related_obj)
container = aggregate.get_container(related_element)
ifc.run("aggregate.unassign_object", product=related_element)
if container:
ifc.run("spatial.assign_container", product=related_element, relating_structure=container)
collector.assign(relating_obj)
collector.assign(related_obj)
return rel
def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None):
+1
View File
@@ -67,6 +67,7 @@ class Aggregate:
def can_aggregate(cls, relating_object, related_object): pass
def disable_editing(cls, obj): pass
def enable_editing(cls, obj): pass
def get_container(cls, element): pass
@interface
+13 -8
View File
@@ -18,17 +18,10 @@
import blenderbim.core.tool
import blenderbim.tool as tool
import ifcopenshell.util.element
class Aggregate(blenderbim.core.tool.Aggregate):
@classmethod
def enable_editing(cls, obj):
obj.BIMObjectAggregateProperties.is_editing = True
@classmethod
def disable_editing(cls, obj):
obj.BIMObjectAggregateProperties.is_editing = False
@classmethod
def can_aggregate(cls, relating_obj, related_obj):
relating_object = tool.Ifc.get_entity(relating_obj)
@@ -50,3 +43,15 @@ class Aggregate(blenderbim.core.tool.Aggregate):
if relating_object.is_a("IfcProject") and related_object.is_a("IfcSpatialElement"):
return True
return False
@classmethod
def disable_editing(cls, obj):
obj.BIMObjectAggregateProperties.is_editing = False
@classmethod
def enable_editing(cls, obj):
obj.BIMObjectAggregateProperties.is_editing = True
@classmethod
def get_container(cls, element):
return ifcopenshell.util.element.get_container(element)
@@ -34,7 +34,7 @@ Scenario: Unassign object
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})"
When I press "bim.unassign_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"
+6 -4
View File
@@ -50,9 +50,11 @@ class TestAssignObject:
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")
def test_run(self, ifc, aggregate, collector):
ifc.get_entity("related_obj").should_be_called().will_return("element")
aggregate.get_container("element").should_be_called().will_return("container")
ifc.run("spatial.assign_container", product="element", relating_structure="container").should_be_called()
ifc.run("aggregate.unassign_object", product="element").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"
subject.unassign_object(ifc, aggregate, collector, relating_obj="relating_obj", related_obj="related_obj")
+24 -11
View File
@@ -29,19 +29,8 @@ class TestImplementsTool(NewFile):
assert isinstance(subject(), blenderbim.core.tool.Aggregate)
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
assert obj.BIMObjectAggregateProperties.is_editing is True
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
assert obj.BIMObjectAggregateProperties.is_editing is False
class TestCanAggregate(NewFile):
@@ -117,3 +106,27 @@ class TestCanAggregate(NewFile):
subelement_obj = bpy.data.objects.new("Object", None)
tool.Ifc.link(subelement, subelement_obj)
assert subject.can_aggregate(element_obj, subelement_obj) is False
class TestDisableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
subject.disable_editing(obj)
assert obj.BIMObjectAggregateProperties.is_editing is False
class TestEnableEditing(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", None)
subject.enable_editing(obj)
assert obj.BIMObjectAggregateProperties.is_editing is True
class TestGetContainer(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
element = ifc.createIfcWall()
container = ifc.createIfcBuildingStorey()
ifcopenshell.api.run("spatial.assign_container", ifc, product=element, relating_structure=container)
assert subject.get_container(element) == container