mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-09 21:53:40 +00:00
Copying filled openings is now possible.
This commit is contained in:
@@ -532,6 +532,9 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.new_active_obj = None
|
self.new_active_obj = None
|
||||||
|
# Track decompositions so they can be recreated after the operation
|
||||||
|
relationships = tool.Root.get_decomposition_relationships(context.selected_objects)
|
||||||
|
old_to_new = {}
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
new_obj = obj.copy()
|
new_obj = obj.copy()
|
||||||
if obj.data:
|
if obj.data:
|
||||||
@@ -542,8 +545,12 @@ class OverrideDuplicateMove(bpy.types.Operator):
|
|||||||
collection.objects.link(new_obj)
|
collection.objects.link(new_obj)
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
new_obj.select_set(True)
|
new_obj.select_set(True)
|
||||||
# This is the only difference
|
# Copy the actual class
|
||||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
new = blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
||||||
|
if new:
|
||||||
|
old_to_new[tool.Ifc.get_entity(obj)] = new
|
||||||
|
# Recreate decompositions
|
||||||
|
tool.Root.recreate_decompositions(relationships, old_to_new)
|
||||||
bpy.ops.transform.translate("INVOKE_DEFAULT")
|
bpy.ops.transform.translate("INVOKE_DEFAULT")
|
||||||
blenderbim.bim.handler.purge_module_data()
|
blenderbim.bim.handler.purge_module_data()
|
||||||
|
|
||||||
@@ -580,6 +587,9 @@ class OverrideDuplicateMoveLinked(bpy.types.Operator):
|
|||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.new_active_obj = None
|
self.new_active_obj = None
|
||||||
|
# Track decompositions so they can be recreated after the operation
|
||||||
|
relationships = tool.Root.get_decomposition_relationships(context.selected_objects)
|
||||||
|
old_to_new = {}
|
||||||
for obj in context.selected_objects:
|
for obj in context.selected_objects:
|
||||||
new_obj = obj.copy()
|
new_obj = obj.copy()
|
||||||
if obj == context.active_object:
|
if obj == context.active_object:
|
||||||
@@ -588,8 +598,12 @@ class OverrideDuplicateMoveLinked(bpy.types.Operator):
|
|||||||
collection.objects.link(new_obj)
|
collection.objects.link(new_obj)
|
||||||
obj.select_set(False)
|
obj.select_set(False)
|
||||||
new_obj.select_set(True)
|
new_obj.select_set(True)
|
||||||
# This is the only difference
|
# Copy the actual class
|
||||||
blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
new = blenderbim.core.root.copy_class(tool.Ifc, tool.Collector, tool.Geometry, tool.Root, obj=new_obj)
|
||||||
|
if new:
|
||||||
|
old_to_new[tool.Ifc.get_entity(obj)] = new
|
||||||
|
# Recreate decompositions
|
||||||
|
tool.Root.recreate_decompositions(relationships, old_to_new)
|
||||||
bpy.ops.transform.translate("INVOKE_DEFAULT")
|
bpy.ops.transform.translate("INVOKE_DEFAULT")
|
||||||
blenderbim.bim.handler.purge_module_data()
|
blenderbim.bim.handler.purge_module_data()
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|||||||
@@ -60,8 +60,6 @@ class BIM_PT_voids(Panel):
|
|||||||
obj_name = obj.name
|
obj_name = obj.name
|
||||||
if opening_id and obj_name:
|
if opening_id and obj_name:
|
||||||
op = row.operator("bim.remove_opening", icon="X", text="Remove Opening").opening_id = opening_id
|
op = row.operator("bim.remove_opening", icon="X", text="Remove Opening").opening_id = opening_id
|
||||||
else:
|
|
||||||
row.label(text="Select an opening and an element to modify", icon="HELP")
|
|
||||||
|
|
||||||
opening_ids = Data.products[props.ifc_definition_id]
|
opening_ids = Data.products[props.ifc_definition_id]
|
||||||
if not opening_ids and not active_object_is_an_opening:
|
if not opening_ids and not active_object_is_an_opening:
|
||||||
|
|||||||
@@ -30,9 +30,15 @@ def copy_class(ifc, collector, geometry, root, obj=None):
|
|||||||
root.link_object_data(ifc.get_object(relating_type), obj)
|
root.link_object_data(ifc.get_object(relating_type), obj)
|
||||||
elif representation:
|
elif representation:
|
||||||
root.copy_representation(element, new)
|
root.copy_representation(element, new)
|
||||||
|
new_representation = root.get_element_representation(new, root.get_representation_context(representation))
|
||||||
|
data = geometry.duplicate_object_data(obj)
|
||||||
|
geometry.change_object_data(obj, data, is_global=True)
|
||||||
|
geometry.rename_object(data, geometry.get_representation_name(new_representation))
|
||||||
|
geometry.link(new_representation, data)
|
||||||
collector.assign(obj)
|
collector.assign(obj)
|
||||||
if root.is_opening_element(new):
|
if root.is_opening_element(new):
|
||||||
root.add_tracked_opening(obj)
|
root.add_tracked_opening(obj)
|
||||||
|
return new
|
||||||
|
|
||||||
|
|
||||||
def assign_class(
|
def assign_class(
|
||||||
|
|||||||
@@ -446,12 +446,15 @@ class Root:
|
|||||||
def add_tracked_opening(cls, obj): pass
|
def add_tracked_opening(cls, obj): pass
|
||||||
def copy_representation(cls, source, dest): pass
|
def copy_representation(cls, source, dest): pass
|
||||||
def does_type_have_representations(cls, element): pass
|
def does_type_have_representations(cls, element): pass
|
||||||
|
def get_decomposition_relationships(cls, objs): pass
|
||||||
|
def get_element_representation(cls, element, context): pass
|
||||||
def get_element_type(cls, element): pass
|
def get_element_type(cls, element): pass
|
||||||
def get_object_name(cls, obj): pass
|
def get_object_name(cls, obj): pass
|
||||||
def get_object_representation(cls, obj): pass
|
def get_object_representation(cls, obj): pass
|
||||||
def get_representation_context(cls, representation): pass
|
def get_representation_context(cls, representation): pass
|
||||||
def is_opening_element(cls, element): pass
|
def is_opening_element(cls, element): pass
|
||||||
def link_object_data(cls, source_obj, destination_obj): pass
|
def link_object_data(cls, source_obj, destination_obj): pass
|
||||||
|
def recreate_decompositions(cls, relationships, old_to_new): pass
|
||||||
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
|
def run_geometry_add_representation(cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None): pass
|
||||||
def set_element_specific_display_settings(cls, obj, element): pass
|
def set_element_specific_display_settings(cls, obj, element): pass
|
||||||
def set_object_name(cls, obj, element): pass
|
def set_object_name(cls, obj, element): pass
|
||||||
|
|||||||
@@ -50,6 +50,29 @@ class Root(blenderbim.core.tool.Root):
|
|||||||
def does_type_have_representations(cls, element):
|
def does_type_have_representations(cls, element):
|
||||||
return bool(element.RepresentationMaps)
|
return bool(element.RepresentationMaps)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_decomposition_relationships(cls, objs):
|
||||||
|
relationships = {}
|
||||||
|
for obj in objs:
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
if not element:
|
||||||
|
continue
|
||||||
|
if hasattr(element, "FillsVoids") and element.FillsVoids:
|
||||||
|
building = element.FillsVoids[0].RelatingOpeningElement.VoidsElements[0].RelatingBuildingElement
|
||||||
|
relationships[element] = {"type": "fill", "element": building}
|
||||||
|
return relationships
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_element_representation(cls, element, context):
|
||||||
|
if context.is_a("IfcGeometricRepresentationSubContext"):
|
||||||
|
return ifcopenshell.util.representation.get_representation(
|
||||||
|
element,
|
||||||
|
context=context.ContextType,
|
||||||
|
subcontext=context.ContextIdentifier,
|
||||||
|
target_view=context.TargetView,
|
||||||
|
)
|
||||||
|
return ifcopenshell.util.representation.get_representation(element, context=context.ContextType)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_element_type(cls, element):
|
def get_element_type(cls, element):
|
||||||
return ifcopenshell.util.element.get_type(element)
|
return ifcopenshell.util.element.get_type(element)
|
||||||
@@ -80,6 +103,18 @@ class Root(blenderbim.core.tool.Root):
|
|||||||
def link_object_data(cls, source_obj, destination_obj):
|
def link_object_data(cls, source_obj, destination_obj):
|
||||||
destination_obj.data = source_obj.data
|
destination_obj.data = source_obj.data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def recreate_decompositions(cls, relationships, old_to_new):
|
||||||
|
for subelement, data in relationships.items():
|
||||||
|
subelement = old_to_new.get(subelement)
|
||||||
|
element = old_to_new.get(data["element"])
|
||||||
|
if not subelement or not element:
|
||||||
|
continue
|
||||||
|
if data["type"] == "fill":
|
||||||
|
obj1 = tool.Ifc.get_object(element)
|
||||||
|
obj2 = tool.Ifc.get_object(subelement)
|
||||||
|
bpy.ops.bim.add_filled_opening(voided_obj=obj1.name, filling_obj=obj2.name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run_geometry_add_representation(
|
def run_geometry_add_representation(
|
||||||
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
cls, obj=None, context=None, ifc_representation_class=None, profile_set_usage=None
|
||||||
|
|||||||
@@ -51,6 +51,13 @@ class TestCopyClass:
|
|||||||
root.get_element_type("element").should_be_called().will_return("type")
|
root.get_element_type("element").should_be_called().will_return("type")
|
||||||
root.does_type_have_representations("type").should_be_called().will_return(False)
|
root.does_type_have_representations("type").should_be_called().will_return(False)
|
||||||
root.copy_representation("original_element", "element").should_be_called()
|
root.copy_representation("original_element", "element").should_be_called()
|
||||||
|
root.get_representation_context("representation").should_be_called().will_return("context")
|
||||||
|
root.get_element_representation("element", "context").should_be_called().will_return("new_representation")
|
||||||
|
geometry.change_object_data("obj", "data", is_global=True).should_be_called()
|
||||||
|
geometry.get_representation_name("new_representation").should_be_called().will_return("name")
|
||||||
|
geometry.rename_object("data", "name").should_be_called()
|
||||||
|
geometry.link("new_representation", "data").should_be_called()
|
||||||
|
geometry.duplicate_object_data("obj").should_be_called().will_return("data")
|
||||||
collector.assign("obj").should_be_called()
|
collector.assign("obj").should_be_called()
|
||||||
root.is_opening_element("element").should_be_called().will_return(False)
|
root.is_opening_element("element").should_be_called().will_return(False)
|
||||||
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
subject.copy_class(ifc, collector, geometry, root, obj="obj")
|
||||||
|
|||||||
@@ -64,6 +64,33 @@ class TestDoesTypeHaveRepresentations(NewFile):
|
|||||||
assert subject.does_type_have_representations(element) is True
|
assert subject.does_type_have_representations(element) is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetDecompositionRelationships(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
|
||||||
|
element = ifc.createIfcWall()
|
||||||
|
opening = ifc.createIfcOpeningElement()
|
||||||
|
fill = ifc.createIfcWindow()
|
||||||
|
ifcopenshell.api.run("void.add_opening", ifc, opening=opening, element=element)
|
||||||
|
ifcopenshell.api.run("void.add_filling", ifc, opening=opening, element=fill)
|
||||||
|
|
||||||
|
obj = bpy.data.objects.new("Object", None)
|
||||||
|
tool.Ifc.link(fill, obj)
|
||||||
|
|
||||||
|
assert subject.get_decomposition_relationships([obj]) == {fill: {"type": "fill", "element": element}}
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetElementRepresentation(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
context = ifc.createIfcGeometricRepresentationContext(ContextType="Model")
|
||||||
|
representation = ifc.createIfcShapeRepresentation(ContextOfItems=context)
|
||||||
|
wall = ifc.createIfcWall(Representation=ifc.createIfcProductDefinitionShape(Representations=[representation]))
|
||||||
|
assert subject.get_element_representation(wall, context) == representation
|
||||||
|
|
||||||
|
|
||||||
class TestGetElementType(NewFile):
|
class TestGetElementType(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
bpy.ops.bim.create_project()
|
bpy.ops.bim.create_project()
|
||||||
|
|||||||
Reference in New Issue
Block a user