mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 09:32:37 +00:00
See #2140. You can now bulk unassign aggregates, and they are auto directly contained in any previous indirect containers.
This commit is contained in:
@@ -34,7 +34,6 @@ class AggregateData:
|
|||||||
cls.data = {
|
cls.data = {
|
||||||
"has_relating_object": cls.has_relating_object(),
|
"has_relating_object": cls.has_relating_object(),
|
||||||
"relating_object_label": cls.get_relating_object_label(),
|
"relating_object_label": cls.get_relating_object_label(),
|
||||||
"relating_object_id": cls.get_relating_object_id(),
|
|
||||||
"has_related_objects": cls.has_related_objects(),
|
"has_related_objects": cls.has_related_objects(),
|
||||||
"related_objects_amount": cls.get_related_objects_amount(),
|
"related_objects_amount": cls.get_related_objects_amount(),
|
||||||
"ifc_class": cls.ifc_class(),
|
"ifc_class": cls.ifc_class(),
|
||||||
@@ -55,12 +54,6 @@ class AggregateData:
|
|||||||
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) -> int:
|
|
||||||
aggregate = cls.get_relating_object()
|
|
||||||
if aggregate:
|
|
||||||
return aggregate.id()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_related_objects(cls):
|
def get_related_objects(cls):
|
||||||
return ifcopenshell.util.element.get_parts(tool.Ifc.get_entity(bpy.context.active_object))
|
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_idname = "bim.unassign_object"
|
||||||
bl_label = "Unassign Object"
|
bl_label = "Unassign Object"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
relating_object: bpy.props.IntProperty()
|
|
||||||
related_object: bpy.props.IntProperty()
|
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.unassign_object(
|
for obj in bpy.context.selected_objects:
|
||||||
tool.Ifc,
|
element = tool.Ifc.get_entity(obj)
|
||||||
tool.Collector,
|
if not element:
|
||||||
relating_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.relating_object)),
|
continue
|
||||||
related_obj=tool.Ifc.get_object(tool.Ifc.get().by_id(self.related_object)),
|
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):
|
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.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||||
row.operator("bim.add_aggregate", icon="ADD", text="")
|
row.operator("bim.add_aggregate", icon="ADD", text="")
|
||||||
op = row.operator("bim.unassign_object", icon="X", 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", icon="TRIA_UP")
|
row.label(text="No Aggregate", icon="TRIA_UP")
|
||||||
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
row.operator("bim.enable_editing_aggregate", icon="GREASEPENCIL", text="")
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class AddFilledOpening(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
|
|
||||||
|
|
||||||
class FilledOpeningGenerator:
|
class FilledOpeningGenerator:
|
||||||
def generate(self, filling_obj, voided_obj):
|
def generate(self, filling_obj, voided_obj, target=None):
|
||||||
props = bpy.context.scene.BIMModelProperties
|
props = bpy.context.scene.BIMModelProperties
|
||||||
unit_scale = ifcopenshell.util.unit.calculate_unit_scale(tool.Ifc.get())
|
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:
|
if not voided_obj or not filling_obj:
|
||||||
return
|
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)
|
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)
|
raycast = voided_obj.closest_point_on_mesh(voided_obj.matrix_world.inverted() @ target, distance=0.01)
|
||||||
if not raycast[0]:
|
if not raycast[0]:
|
||||||
target = filling_obj.matrix_world.col[3].to_3d().copy()
|
target = filling_obj.matrix_world.col[3].to_3d().copy()
|
||||||
|
|||||||
@@ -37,11 +37,14 @@ def assign_object(ifc, aggregator, collector, relating_obj=None, related_obj=Non
|
|||||||
return rel
|
return rel
|
||||||
|
|
||||||
|
|
||||||
def unassign_object(ifc, collector, relating_obj=None, related_obj=None):
|
def unassign_object(ifc, aggregate, collector, relating_obj=None, related_obj=None):
|
||||||
rel = ifc.run("aggregate.unassign_object", product=ifc.get_entity(related_obj))
|
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(relating_obj)
|
||||||
collector.assign(related_obj)
|
collector.assign(related_obj)
|
||||||
return rel
|
|
||||||
|
|
||||||
|
|
||||||
def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None):
|
def add_part_to_object(ifc, aggregator, collector, blender, obj, part_class, part_name=None):
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ class Aggregate:
|
|||||||
def can_aggregate(cls, relating_object, related_object): pass
|
def can_aggregate(cls, relating_object, related_object): pass
|
||||||
def disable_editing(cls, obj): pass
|
def disable_editing(cls, obj): pass
|
||||||
def enable_editing(cls, obj): pass
|
def enable_editing(cls, obj): pass
|
||||||
|
def get_container(cls, element): pass
|
||||||
|
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
|
|||||||
@@ -18,17 +18,10 @@
|
|||||||
|
|
||||||
import blenderbim.core.tool
|
import blenderbim.core.tool
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
import ifcopenshell.util.element
|
||||||
|
|
||||||
|
|
||||||
class Aggregate(blenderbim.core.tool.Aggregate):
|
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
|
@classmethod
|
||||||
def can_aggregate(cls, relating_obj, related_obj):
|
def can_aggregate(cls, relating_obj, related_obj):
|
||||||
relating_object = tool.Ifc.get_entity(relating_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"):
|
if relating_object.is_a("IfcProject") and related_object.is_a("IfcSpatialElement"):
|
||||||
return True
|
return True
|
||||||
return False
|
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 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 I press "bim.assign_object(relating_object={relating_object}, related_object={related_object})"
|
||||||
And the object "IfcBuildingStorey/My Storey" is selected
|
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"
|
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 object "IfcBuildingStorey/My Storey" is in the collection "IfcBuildingStorey/My Storey"
|
||||||
And the collection "IfcBuildingStorey/My Storey" is in the collection "IfcProject/My Project"
|
And the collection "IfcBuildingStorey/My Storey" is in the collection "IfcProject/My Project"
|
||||||
|
|||||||
@@ -50,9 +50,11 @@ class TestAssignObject:
|
|||||||
|
|
||||||
|
|
||||||
class TestUnassignObject:
|
class TestUnassignObject:
|
||||||
def test_run(self, ifc, collector):
|
def test_run(self, ifc, aggregate, collector):
|
||||||
ifc.get_entity("related_obj").should_be_called().will_return("related_object")
|
ifc.get_entity("related_obj").should_be_called().will_return("element")
|
||||||
ifc.run("aggregate.unassign_object", product="related_object").should_be_called().will_return("rel")
|
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("relating_obj").should_be_called()
|
||||||
collector.assign("related_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")
|
||||||
|
|||||||
@@ -29,19 +29,8 @@ class TestImplementsTool(NewFile):
|
|||||||
assert isinstance(subject(), blenderbim.core.tool.Aggregate)
|
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):
|
class TestCanAggregate(NewFile):
|
||||||
@@ -117,3 +106,27 @@ class TestCanAggregate(NewFile):
|
|||||||
subelement_obj = bpy.data.objects.new("Object", None)
|
subelement_obj = bpy.data.objects.new("Object", None)
|
||||||
tool.Ifc.link(subelement, subelement_obj)
|
tool.Ifc.link(subelement, subelement_obj)
|
||||||
assert subject.can_aggregate(element_obj, subelement_obj) is False
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user