mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-25 00:09:59 +00:00
See #2520. You can now duplicate drawings.
This commit is contained in:
@@ -42,6 +42,7 @@ classes = (
|
|||||||
operator.DisableEditingSchedules,
|
operator.DisableEditingSchedules,
|
||||||
operator.DisableEditingSheets,
|
operator.DisableEditingSheets,
|
||||||
operator.DisableEditingText,
|
operator.DisableEditingText,
|
||||||
|
operator.DuplicateDrawing,
|
||||||
operator.EditAssignedProduct,
|
operator.EditAssignedProduct,
|
||||||
operator.EditText,
|
operator.EditText,
|
||||||
operator.EditVectorStyle,
|
operator.EditVectorStyle,
|
||||||
|
|||||||
@@ -103,6 +103,22 @@ class AddDrawing(bpy.types.Operator, Operator):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateDrawing(bpy.types.Operator, Operator):
|
||||||
|
bl_idname = "bim.duplicate_drawing"
|
||||||
|
bl_label = "Duplicate Drawing"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
drawing: bpy.props.IntProperty()
|
||||||
|
|
||||||
|
def _execute(self, context):
|
||||||
|
self.props = context.scene.DocProperties
|
||||||
|
core.duplicate_drawing(tool.Ifc, tool.Drawing, drawing=tool.Ifc.get().by_id(self.drawing))
|
||||||
|
try:
|
||||||
|
drawing = tool.Ifc.get().by_id(self.props.active_drawing_id)
|
||||||
|
core.sync_references(tool.Ifc, tool.Collector, tool.Drawing, drawing=drawing)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CreateDrawing(bpy.types.Operator):
|
class CreateDrawing(bpy.types.Operator):
|
||||||
"""Creates/refreshes a .svg drawing
|
"""Creates/refreshes a .svg drawing
|
||||||
|
|
||||||
@@ -385,7 +401,9 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||||
)
|
)
|
||||||
geom_settings.set_context_ids(body_context)
|
geom_settings.set_context_ids(body_context)
|
||||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
it = ifcopenshell.geom.iterator(
|
||||||
|
geom_settings, ifc, multiprocessing.cpu_count(), include=elements
|
||||||
|
)
|
||||||
it.set_cache(cache)
|
it.set_cache(cache)
|
||||||
processed = set()
|
processed = set()
|
||||||
for elem in self.yield_from_iterator(it):
|
for elem in self.yield_from_iterator(it):
|
||||||
@@ -401,7 +419,9 @@ class CreateDrawing(bpy.types.Operator):
|
|||||||
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
DISABLE_TRIANGULATION=True, STRICT_TOLERANCE=True, INCLUDE_CURVES=True
|
||||||
)
|
)
|
||||||
geom_settings.set_context_ids(annotation_context)
|
geom_settings.set_context_ids(annotation_context)
|
||||||
it = ifcopenshell.geom.iterator(geom_settings, ifc, multiprocessing.cpu_count(), include=elements)
|
it = ifcopenshell.geom.iterator(
|
||||||
|
geom_settings, ifc, multiprocessing.cpu_count(), include=elements
|
||||||
|
)
|
||||||
it.set_cache(cache)
|
it.set_cache(cache)
|
||||||
processed = set()
|
processed = set()
|
||||||
for elem in self.yield_from_iterator(it):
|
for elem in self.yield_from_iterator(it):
|
||||||
|
|||||||
@@ -172,7 +172,9 @@ class BIM_PT_drawings(Panel):
|
|||||||
row = self.layout.row(align=True)
|
row = self.layout.row(align=True)
|
||||||
col = row.column()
|
col = row.column()
|
||||||
col.alignment = "LEFT"
|
col.alignment = "LEFT"
|
||||||
col.operator("bim.remove_drawing", icon="X", text="").drawing = active_drawing.ifc_definition_id
|
row2 = col.row(align=True)
|
||||||
|
row2.operator("bim.remove_drawing", icon="X", text="").drawing = active_drawing.ifc_definition_id
|
||||||
|
row2.operator("bim.duplicate_drawing", icon="COPYDOWN", text="").drawing = active_drawing.ifc_definition_id
|
||||||
col = row.column()
|
col = row.column()
|
||||||
col.alignment = "RIGHT"
|
col.alignment = "RIGHT"
|
||||||
op = row.operator("bim.open_view", icon="URL", text="")
|
op = row.operator("bim.open_view", icon="URL", text="")
|
||||||
|
|||||||
@@ -167,6 +167,19 @@ def add_drawing(ifc, collector, drawing, target_view=None, location_hint=None):
|
|||||||
drawing.import_drawings()
|
drawing.import_drawings()
|
||||||
|
|
||||||
|
|
||||||
|
def duplicate_drawing(ifc, drawing_tool, drawing=None):
|
||||||
|
drawing_name = drawing_tool.ensure_unique_drawing_name(drawing_tool.get_name(drawing))
|
||||||
|
new_drawing = ifc.run("root.copy_class", product=drawing)
|
||||||
|
drawing_tool.copy_drawing_representation(drawing, new_drawing)
|
||||||
|
drawing_tool.set_name(new_drawing, drawing_name)
|
||||||
|
ifc.run("group.unassign_group", group=drawing_tool.get_drawing_group(new_drawing), product=new_drawing)
|
||||||
|
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, products=[new_drawing])
|
||||||
|
drawing_tool.import_drawings()
|
||||||
|
return new_drawing
|
||||||
|
|
||||||
|
|
||||||
def remove_drawing(ifc, drawing_tool, drawing=None):
|
def remove_drawing(ifc, drawing_tool, drawing=None):
|
||||||
collection = drawing_tool.get_drawing_collection(drawing)
|
collection = drawing_tool.get_drawing_collection(drawing)
|
||||||
group = drawing_tool.get_drawing_group(drawing)
|
group = drawing_tool.get_drawing_group(drawing)
|
||||||
|
|||||||
@@ -154,6 +154,8 @@ class Document:
|
|||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Drawing:
|
class Drawing:
|
||||||
|
def activate_view(cls, camera): pass
|
||||||
|
def copy_drawing_representation(cls, source, dest): pass
|
||||||
def create_annotation_object(cls, drawing, object_type): pass
|
def create_annotation_object(cls, drawing, object_type): pass
|
||||||
def create_camera(cls, name, matrix): pass
|
def create_camera(cls, name, matrix): pass
|
||||||
def create_svg_schedule(cls, schedule): pass
|
def create_svg_schedule(cls, schedule): pass
|
||||||
@@ -195,17 +197,17 @@ class Drawing:
|
|||||||
def import_schedules(cls): pass
|
def import_schedules(cls): pass
|
||||||
def import_sheets(cls): pass
|
def import_sheets(cls): pass
|
||||||
def import_text_attributes(cls, obj): pass
|
def import_text_attributes(cls, obj): pass
|
||||||
|
def is_camera_orthographic(cls): pass
|
||||||
|
def is_drawing_active(cls): pass
|
||||||
def open_spreadsheet(cls, uri): pass
|
def open_spreadsheet(cls, uri): pass
|
||||||
def open_svg(cls, filepath): pass
|
def open_svg(cls, filepath): pass
|
||||||
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
def run_root_assign_class(cls, obj=None, ifc_class=None, predefined_type=None, should_add_representation=True, context=None, ifc_representation_class=None): pass
|
||||||
def select_assigned_product(cls, drawing): pass
|
def select_assigned_product(cls, drawing): pass
|
||||||
def set_drawing_collection_name(cls, group, collection): pass
|
def set_drawing_collection_name(cls, group, collection): pass
|
||||||
|
def set_name(cls, element, name): pass
|
||||||
def show_decorations(cls): pass
|
def show_decorations(cls): pass
|
||||||
def sync_object_placement(cls, obj): pass
|
def sync_object_placement(cls, obj): pass
|
||||||
def update_text_value(cls, obj): pass
|
def update_text_value(cls, obj): pass
|
||||||
def is_drawing_active(cls): pass
|
|
||||||
def is_camera_orthographic(cls): pass
|
|
||||||
def activate_view(cls, camera): pass
|
|
||||||
|
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ import blenderbim.bim.module.drawing.helper as helper
|
|||||||
|
|
||||||
|
|
||||||
class Drawing(blenderbim.core.tool.Drawing):
|
class Drawing(blenderbim.core.tool.Drawing):
|
||||||
|
@classmethod
|
||||||
|
def copy_drawing_representation(cls, source, dest):
|
||||||
|
dest.Representation = ifcopenshell.util.element.copy_deep(
|
||||||
|
tool.Ifc.get(), source.Representation, exclude=["IfcGeometricRepresentationContext"]
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def create_annotation_object(cls, drawing, object_type):
|
def create_annotation_object(cls, drawing, object_type):
|
||||||
data_type = {
|
data_type = {
|
||||||
@@ -410,6 +416,10 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
def set_drawing_collection_name(cls, group, collection):
|
def set_drawing_collection_name(cls, group, collection):
|
||||||
collection.name = f"IfcGroup/{group.Name}"
|
collection.name = f"IfcGroup/{group.Name}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_name(cls, element, name):
|
||||||
|
element.Name = name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def show_decorations(cls):
|
def show_decorations(cls):
|
||||||
bpy.context.scene.DocProperties.should_draw_decorations = True
|
bpy.context.scene.DocProperties.should_draw_decorations = True
|
||||||
@@ -948,4 +958,4 @@ class Drawing(blenderbim.core.tool.Drawing):
|
|||||||
|
|
||||||
project_collection.children["Views"].children[camera.users_collection[0].name].hide_viewport = False
|
project_collection.children["Views"].children[camera.users_collection[0].name].hide_viewport = False
|
||||||
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
|
bpy.data.collections.get(camera.users_collection[0].name).hide_render = False
|
||||||
tool.Spatial.set_active_object(camera)
|
tool.Spatial.set_active_object(camera)
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
@drawing
|
@drawing
|
||||||
Feature: Drawing
|
Feature: Drawing
|
||||||
|
|
||||||
|
Scenario: Duplicate drawing
|
||||||
|
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 variable "wall1" is "IfcStore.get_file().by_type('IfcWall')[-1].id()"
|
||||||
|
And I press "bim.add_drawing"
|
||||||
|
And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()"
|
||||||
|
When I press "bim.duplicate_drawing(drawing={drawing})"
|
||||||
|
Then nothing happens
|
||||||
|
|
||||||
Scenario: Create drawing
|
Scenario: Create drawing
|
||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
And I add a cube
|
And I add a cube
|
||||||
@@ -12,7 +24,7 @@ Scenario: Create drawing
|
|||||||
And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()"
|
And the variable "drawing" is "IfcStore.get_file().by_type('IfcAnnotation')[0].id()"
|
||||||
And I set "scene.DocProperties.active_drawing_index" to "0"
|
And I set "scene.DocProperties.active_drawing_index" to "0"
|
||||||
And I press "bim.activate_view(drawing={drawing})"
|
And I press "bim.activate_view(drawing={drawing})"
|
||||||
And I press "bim.create_drawing"
|
When I press "bim.create_drawing"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
Scenario: Create drawing after deleting a duplicated object
|
Scenario: Create drawing after deleting a duplicated object
|
||||||
@@ -33,5 +45,5 @@ Scenario: Create drawing after deleting a duplicated object
|
|||||||
And I press "bim.create_drawing"
|
And I press "bim.create_drawing"
|
||||||
And the object "IfcWall/Cube" is selected
|
And the object "IfcWall/Cube" is selected
|
||||||
And I press "object.delete(use_global=False)"
|
And I press "object.delete(use_global=False)"
|
||||||
And I press "bim.create_drawing"
|
When I press "bim.create_drawing"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|||||||
@@ -243,6 +243,24 @@ class TestAddDrawing:
|
|||||||
subject.add_drawing(ifc, collector, drawing, target_view="target_view", location_hint="location_hint")
|
subject.add_drawing(ifc, collector, drawing, target_view="target_view", location_hint="location_hint")
|
||||||
|
|
||||||
|
|
||||||
|
class TestDuplicateDrawing:
|
||||||
|
def test_run(self, ifc, drawing):
|
||||||
|
drawing.get_name("drawing").should_be_called().will_return("name")
|
||||||
|
drawing.ensure_unique_drawing_name("name").should_be_called().will_return("unique_name")
|
||||||
|
ifc.run("root.copy_class", product="drawing").should_be_called().will_return("new_drawing")
|
||||||
|
drawing.copy_drawing_representation("drawing", "new_drawing").should_be_called()
|
||||||
|
drawing.set_name("new_drawing", "unique_name").should_be_called()
|
||||||
|
drawing.get_drawing_group("new_drawing").should_be_called().will_return("group")
|
||||||
|
ifc.run("group.unassign_group", group="group", product="new_drawing").should_be_called()
|
||||||
|
ifc.run("group.add_group").should_be_called().will_return("new_group")
|
||||||
|
ifc.run(
|
||||||
|
"group.edit_group", group="new_group", attributes={"Name": "unique_name", "ObjectType": "DRAWING"}
|
||||||
|
).should_be_called()
|
||||||
|
ifc.run("group.assign_group", group="new_group", products=["new_drawing"]).should_be_called()
|
||||||
|
drawing.import_drawings().should_be_called()
|
||||||
|
subject.duplicate_drawing(ifc, drawing, drawing="drawing")
|
||||||
|
|
||||||
|
|
||||||
class TestRemoveDrawing:
|
class TestRemoveDrawing:
|
||||||
def test_run(self, ifc, drawing):
|
def test_run(self, ifc, drawing):
|
||||||
drawing.get_drawing_collection("drawing").should_be_called().will_return("collection")
|
drawing.get_drawing_collection("drawing").should_be_called().will_return("collection")
|
||||||
|
|||||||
@@ -32,6 +32,16 @@ class TestImplementsTool(NewFile):
|
|||||||
assert isinstance(subject(), blenderbim.core.tool.Drawing)
|
assert isinstance(subject(), blenderbim.core.tool.Drawing)
|
||||||
|
|
||||||
|
|
||||||
|
class TestCopyDrawingRepresentation(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
source = ifc.createIfcAnnotation(Representation=ifc.createIfcProductDefinitionShape())
|
||||||
|
dest = ifc.createIfcAnnotation()
|
||||||
|
subject.copy_drawing_representation(source, dest)
|
||||||
|
assert dest.Representation.is_a("IfcProductDefinitionShape")
|
||||||
|
|
||||||
|
|
||||||
class TestCreateAnnotationObject(NewFile):
|
class TestCreateAnnotationObject(NewFile):
|
||||||
def test_nothing(self):
|
def test_nothing(self):
|
||||||
pass
|
pass
|
||||||
@@ -562,6 +572,14 @@ class TestSetDrawingCollectionName(NewFile):
|
|||||||
assert collection.name == "IfcGroup/Foobaz"
|
assert collection.name == "IfcGroup/Foobaz"
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetName(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
drawing = ifc.createIfcAnnotation()
|
||||||
|
subject.set_name(drawing, "Name")
|
||||||
|
assert drawing.Name == "Name"
|
||||||
|
|
||||||
|
|
||||||
class TestShowDecorations(NewFile):
|
class TestShowDecorations(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
bpy.context.scene.DocProperties.should_draw_decorations = False
|
bpy.context.scene.DocProperties.should_draw_decorations = False
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ class Usecase:
|
|||||||
* Voids are duplicated too
|
* Voids are duplicated too
|
||||||
* The copy will have the same material as the original. Parametric
|
* The copy will have the same material as the original. Parametric
|
||||||
material set usages will be copied.
|
material set usages will be copied.
|
||||||
|
* The copy will be part of the same groups as the original.
|
||||||
|
|
||||||
Be warned that:
|
Be warned that:
|
||||||
|
|
||||||
|
|||||||
@@ -502,8 +502,7 @@ def get_decomposition(element):
|
|||||||
|
|
||||||
|
|
||||||
def get_grouped_by(element):
|
def get_grouped_by(element):
|
||||||
"""
|
"""Retrieves all subelements of an element based on the group.
|
||||||
Retrieves all subelements of an element based on the group.
|
|
||||||
|
|
||||||
:param element: The IFC element
|
:param element: The IFC element
|
||||||
:return: All subelements of the group
|
:return: All subelements of the group
|
||||||
@@ -513,7 +512,7 @@ def get_grouped_by(element):
|
|||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
element = file.by_type("IfcGroup")[0]
|
element = file.by_type("IfcGroup")[0]
|
||||||
subelements = ifcopenshell.util.element.get_group(element)
|
subelements = ifcopenshell.util.element.get_grouped_by(element)
|
||||||
"""
|
"""
|
||||||
queue = [element]
|
queue = [element]
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
@@ -198,3 +198,11 @@ class TestCopyClass(test.bootstrap.IFC4):
|
|||||||
assert element2.ConnectedFrom
|
assert element2.ConnectedFrom
|
||||||
assert not new.ConnectedTo
|
assert not new.ConnectedTo
|
||||||
assert not new.ConnectedFrom
|
assert not new.ConnectedFrom
|
||||||
|
|
||||||
|
def test_maintaining_group_relationships(self):
|
||||||
|
element = ifcopenshell.api.run("root.create_entity", self.file, ifc_class="IfcWall")
|
||||||
|
group = ifcopenshell.api.run("group.add_group", self.file)
|
||||||
|
ifcopenshell.api.run("group.assign_group", self.file, group=group, products=[element])
|
||||||
|
new = ifcopenshell.api.run("root.copy_class", self.file, product=element)
|
||||||
|
assert len(self.file.by_type("IfcRelAssignsToGroup")) == 1
|
||||||
|
assert new.HasAssignments[0].RelatingGroup == group
|
||||||
|
|||||||
Reference in New Issue
Block a user