Changes are now synchronised prior to switching representations.

This commit is contained in:
Dion Moult
2021-11-09 20:49:25 +11:00
parent cbf7b0a77a
commit 66fa3f0096
13 changed files with 185 additions and 26 deletions
@@ -101,6 +101,7 @@ class SwitchRepresentation(bpy.types.Operator, Operator):
should_reload=self.should_reload,
enable_dynamic_voids=self.disable_opening_subtractions,
is_global=self.should_switch_all_meshes,
should_sync_changes_first=True,
)
@@ -217,7 +218,7 @@ class UpdateRepresentation(bpy.types.Operator):
core.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_representation)
Data.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
if obj.data.BIMMeshProperties.ifc_parameters:
bpy.ops.bim.get_representation_ifc_parameters()
core.get_representation_ifc_parameters(tool.Geometry, obj=obj)
class UpdateParametricRepresentation(bpy.types.Operator):
@@ -244,35 +245,20 @@ class UpdateParametricRepresentation(bpy.types.Operator):
should_reload=True,
enable_dynamic_voids=False,
is_global=True,
should_sync_changes_first=False,
)
if show_representation_parameters:
bpy.ops.bim.get_representation_ifc_parameters()
core.get_representation_ifc_parameters(tool.Geometry, obj=obj)
return {"FINISHED"}
class GetRepresentationIfcParameters(bpy.types.Operator):
class GetRepresentationIfcParameters(bpy.types.Operator, Operator):
bl_idname = "bim.get_representation_ifc_parameters"
bl_label = "Get Representation IFC Parameters"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
self.file = IfcStore.get_file()
obj = context.active_object
props = obj.data.BIMMeshProperties
elements = self.file.traverse(self.file.by_id(props.ifc_definition_id))
props.ifc_parameters.clear()
for element in elements:
if element.is_a("IfcRepresentationItem") or element.is_a("IfcParameterizedProfileDef"):
for i in range(0, len(element)):
if element.attribute_type(i) == "DOUBLE":
new = props.ifc_parameters.add()
new.name = "{}/{}".format(element.is_a(), element.attribute_name(i))
new.step_id = element.id()
new.type = element.attribute_type(i)
new.index = i
if element[i]:
new.value = element[i]
return {"FINISHED"}
def _execute(self, context):
core.get_representation_ifc_parameters(tool.Geometry, obj=context.active_object)
class CopyRepresentation(bpy.types.Operator, Operator):
@@ -66,6 +66,7 @@ def mode_callback(obj, data):
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=False,
)
IfcStore.edited_objs.add(obj)
bm = bmesh.from_edit_mesh(obj.data)
@@ -238,6 +238,7 @@ class DynamicallyVoidProduct(bpy.types.Operator):
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=False,
)
if was_edit_mode:
bpy.ops.object.mode_set(mode="EDIT")
@@ -296,6 +297,7 @@ def regenerate_profile_usage(usecase_path, ifc_file, settings):
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=False,
)
@@ -220,6 +220,7 @@ class DumbProfileRegenerator:
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=False,
)
def sync_object(self, element):
@@ -1087,7 +1087,7 @@ class ImportP6(bpy.types.Operator, ImportHelper):
def poll(cls, context):
ifc_file = IfcStore.get_file()
return ifc_file is not None
def execute(self, context):
from ifc4d.p62ifc import P62Ifc
@@ -1114,7 +1114,7 @@ class ImportP6XER(bpy.types.Operator, ImportHelper):
def poll(cls, context):
ifc_file = IfcStore.get_file()
return ifc_file is not None
def execute(self, context):
from ifc4d.p6xer2ifc import P6XER2Ifc
@@ -1141,7 +1141,7 @@ class ImportPP(bpy.types.Operator, ImportHelper):
def poll(cls, context):
ifc_file = IfcStore.get_file()
return ifc_file is not None
def execute(self, context):
from ifc4d.pp2ifc import PP2Ifc
@@ -1168,7 +1168,7 @@ class ImportMSP(bpy.types.Operator, ImportHelper):
def poll(cls, context):
ifc_file = IfcStore.get_file()
return ifc_file is not None
def execute(self, context):
from ifc4d.msp2ifc import MSP2Ifc
@@ -129,6 +129,7 @@ class RemoveOpening(bpy.types.Operator):
should_reload=True,
enable_dynamic_voids=False,
is_global=True,
should_sync_changes_first=False,
)
Data.load(IfcStore.get_file(), obj.BIMObjectProperties.ifc_definition_id)
+14 -1
View File
@@ -75,8 +75,17 @@ def add_representation(
def switch_representation(
geometry, obj=None, representation=None, should_reload=True, enable_dynamic_voids=True, is_global=True
geometry,
obj=None,
representation=None,
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=False,
):
if should_sync_changes_first and geometry.is_edited(obj) and not geometry.is_box_representation(representation):
geometry.run_geometry_update_representation(obj=obj)
representation = geometry.resolve_mapped_representation(representation)
existing_data = geometry.get_representation_data(representation)
@@ -98,6 +107,10 @@ def switch_representation(
geometry.create_dynamic_voids(obj)
def get_representation_ifc_parameters(geometry, obj=None, should_sync_changes_first=False):
geometry.import_representation_parameters(geometry.get_object_data(obj))
def remove_representation(ifc, geometry, obj=None, representation=None):
element = ifc.get_entity(obj)
if geometry.is_mapped_representation(representation) or geometry.is_type_product(element):
+4
View File
@@ -89,13 +89,17 @@ class Geometry:
def get_total_representation_items(cls, obj): pass
def has_data_users(cls, data): pass
def import_representation(cls, obj, representation, enable_dynamic_voids=False): pass
def import_representation_parameters(cls, data): pass
def is_body_representation(cls, representation): pass
def is_box_representation(cls, representation): pass
def is_edited(cls, obj): pass
def is_mapped_representation(cls, representation): pass
def is_type_product(cls, element): pass
def link(cls, element, obj): pass
def rename_object(cls, obj, name): pass
def replace_object_with_empty(cls, obj): pass
def resolve_mapped_representation(cls, representation): pass
def run_geometry_update_representation(cls, obj=None): pass
def should_force_faceted_brep(cls): pass
def should_force_triangulation(cls): pass
def should_use_presentation_style_assignment(cls): pass
@@ -160,10 +160,35 @@ class Geometry(blenderbim.core.tool.Geometry):
ifc_importer.material_creator.create(element, obj, mesh)
return mesh
@classmethod
def import_representation_parameters(cls, data):
props = data.BIMMeshProperties
elements = tool.Ifc.get().traverse(tool.Ifc.get().by_id(props.ifc_definition_id))
props.ifc_parameters.clear()
for element in elements:
if element.is_a("IfcRepresentationItem") or element.is_a("IfcParameterizedProfileDef"):
for i in range(0, len(element)):
if element.attribute_type(i) == "DOUBLE":
new = props.ifc_parameters.add()
new.name = "{}/{}".format(element.is_a(), element.attribute_name(i))
new.step_id = element.id()
new.type = element.attribute_type(i)
new.index = i
if element[i]:
new.value = element[i]
@classmethod
def is_body_representation(cls, representation):
return representation.ContextOfItems.ContextIdentifier == "Body"
@classmethod
def is_box_representation(cls, representation):
return representation.ContextOfItems.ContextIdentifier == "Box"
@classmethod
def is_edited(cls, obj):
return list(obj.scale) != [1.0, 1.0, 1.0] or obj in IfcStore.edited_objs
@classmethod
def is_mapped_representation(cls, representation):
return representation.RepresentationType == "MappedRepresentation"
@@ -200,6 +225,10 @@ class Geometry(blenderbim.core.tool.Geometry):
return cls.resolve_mapped_representation(representation.Items[0].MappingSource.MappedRepresentation)
return representation
@classmethod
def run_geometry_update_representation(cls, obj=None):
bpy.ops.bim.update_representation(obj=obj.name, ifc_representation_class="")
@classmethod
def should_force_faceted_brep(cls):
return bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep
+1
View File
@@ -114,4 +114,5 @@ class Type(blenderbim.core.tool.Type):
should_reload=should_reload,
enable_dynamic_voids=enable_dynamic_voids,
is_global=is_global,
should_sync_changes_first=False,
)
@@ -73,6 +73,37 @@ Scenario: Switch representation
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation})"
Then nothing happens
Scenario: Switch representation - current edited representation is updated prior to switch
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 "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
And I set "scene.BIMProperties.contexts" to "{context}"
And I press "bim.add_representation"
When the object "IfcWall/Cube" is scaled to "2"
And the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation}, should_reload=True)"
And the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[-1].id()"
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation}, should_reload=True)"
When I press "export_ifc.bim(filepath='{cwd}/test/files/export.ifc')"
Then the object "IfcWall/Cube" dimensions are "4,4,0"
Scenario: Switch representation - current edited representation is discarded if switching to a box
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"
When the object "IfcWall/Cube" is scaled to "2"
And the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[-1].id()"
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation}, should_reload=True)"
And the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation}, should_reload=True)"
When I press "export_ifc.bim(filepath='{cwd}/test/files/export.ifc')"
Then the object "IfcWall/Cube" dimensions are "2,2,2"
Scenario: Switch representation - existing Blender modifiers must be purged
Given an empty IFC project
And I add a cube
@@ -210,6 +241,16 @@ Scenario: Update representation - updating a profiled extrusion
When I press "bim.update_representation(obj='IfcWall/Cube')"
Then the object "IfcWall/Cube" has a "SweptSolid" representation of "Model/Body/MODEL_VIEW"
Scenario: Get representation IFC parameters
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 I press "bim.update_representation(ifc_representation_class='IfcExtrudedAreaSolid/IfcRectangleProfileDef')"
When I press "bim.get_representation_ifc_parameters"
Then nothing happens
Scenario: Copy representation
Given an empty IFC project
And I add a cube
+33
View File
@@ -182,6 +182,7 @@ class TestAddRepresentation:
class TestSwitchRepresentation:
def test_switching_to_a_freshly_loaded_representation(self, geometry):
geometry.is_edited("obj").should_be_called().will_return(False)
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return(None)
geometry.import_representation(
@@ -201,9 +202,11 @@ class TestSwitchRepresentation:
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=True,
)
def test_switching_to_a_reloaded_representation_and_deleting_the_existing_data(self, geometry):
geometry.is_edited("obj").should_be_called().will_return(False)
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("existing_data")
geometry.import_representation(
@@ -224,9 +227,11 @@ class TestSwitchRepresentation:
should_reload=True,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=True,
)
def test_switching_to_an_existing_representation(self, geometry):
geometry.is_edited("obj").should_be_called().will_return(False)
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "data", is_global=True).should_be_called()
@@ -240,9 +245,11 @@ class TestSwitchRepresentation:
should_reload=False,
enable_dynamic_voids=True,
is_global=True,
should_sync_changes_first=True,
)
def test_switching_to_non_dynamic_baked_voids(self, geometry):
geometry.is_edited("obj").should_be_called().will_return(False)
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "data", is_global=False).should_be_called()
@@ -254,8 +261,34 @@ class TestSwitchRepresentation:
should_reload=False,
enable_dynamic_voids=False,
is_global=False,
should_sync_changes_first=True,
)
def test_updating_a_representation_if_the_blender_object_has_been_edited_prior_to_switching(self, geometry):
geometry.is_edited("obj").should_be_called().will_return(True)
geometry.is_box_representation("mapped_rep").should_be_called().will_return(False)
geometry.run_geometry_update_representation(obj="obj").should_be_called()
geometry.resolve_mapped_representation("mapped_rep").should_be_called().will_return("representation")
geometry.get_representation_data("representation").should_be_called().will_return("data")
geometry.change_object_data("obj", "data", is_global=False).should_be_called()
geometry.clear_modifiers("obj").should_be_called()
subject.switch_representation(
geometry,
obj="obj",
representation="mapped_rep",
should_reload=False,
enable_dynamic_voids=False,
is_global=False,
should_sync_changes_first=True,
)
class TestGetRepresentationIfcParameters:
def test_run(self, geometry):
geometry.get_object_data("obj").should_be_called().will_return("data")
geometry.import_representation_parameters("data").should_be_called()
subject.get_representation_ifc_parameters(geometry, obj="obj", should_sync_changes_first=False)
class TestRemoveRepresentation:
def test_removing_an_actively_used_mapped_representation_by_remapping_usages_to_an_empty(self, ifc, geometry):
+47
View File
@@ -248,6 +248,24 @@ class TestImportRepresentation(NewFile):
assert len(mesh.edges) == 4
class TestImportRepresentationParameters(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
tool.Ifc.set(ifc)
swept_area = ifc.createIfcCircleProfileDef(Radius=1)
item = ifc.createIfcExtrudedAreaSolid(SweptArea=swept_area, Depth=2)
representation = ifc.createIfcShapeRepresentation(Items=[item])
data = bpy.data.meshes.new("Mesh")
data.BIMMeshProperties.ifc_definition_id = representation.id()
subject.import_representation_parameters(data)
assert data.BIMMeshProperties.ifc_parameters[0].name == "IfcExtrudedAreaSolid/Depth"
assert data.BIMMeshProperties.ifc_parameters[0].step_id == item.id()
assert data.BIMMeshProperties.ifc_parameters[0].index == 3
assert data.BIMMeshProperties.ifc_parameters[1].name == "IfcCircleProfileDef/Radius"
assert data.BIMMeshProperties.ifc_parameters[1].step_id == swept_area.id()
assert data.BIMMeshProperties.ifc_parameters[1].index == 3
class TestIsBodyRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
@@ -260,6 +278,30 @@ class TestIsBodyRepresentation(NewFile):
assert subject.is_body_representation(representation) is False
class TestIsBoxRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
context = ifc.createIfcGeometricRepresentationContext()
representation = ifc.createIfcShapeRepresentation()
representation.ContextOfItems = context
context.ContextIdentifier = "Box"
assert subject.is_box_representation(representation) is True
context.ContextIdentifier = "Clearance"
assert subject.is_box_representation(representation) is False
class TestIsEdited(NewFile):
def test_run(self):
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
assert subject.is_edited(obj) is False
obj.scale[0] = 2
assert subject.is_edited(obj) is True
obj.scale[0] = 1
assert subject.is_edited(obj) is False
IfcStore.edited_objs.add(obj)
assert subject.is_edited(obj) is True
class TestIsMappedRepresentation(NewFile):
def test_run(self):
ifc = ifcopenshell.file()
@@ -327,6 +369,11 @@ class TestResolveMappedRepresentation(NewFile):
assert subject.resolve_mapped_representation(representation) == representation
class TestRunGeometryUpdateRepresentation(NewFile):
def test_nothing(self):
pass
class TestShouldForceFacetedBrep(NewFile):
def test_run(self):
result = bpy.context.scene.BIMGeometryProperties.should_force_faceted_brep