mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-11 02:02:22 +00:00
Fix bug where editing then switching to the current representation crashes.
This commit is contained in:
@@ -86,7 +86,10 @@ def switch_representation(
|
||||
should_sync_changes_first=False,
|
||||
):
|
||||
if should_sync_changes_first and geometry.is_edited(obj) and not geometry.is_box_representation(representation):
|
||||
representation_id = geometry.get_representation_id(representation)
|
||||
geometry.run_geometry_update_representation(obj=obj)
|
||||
if not geometry.does_representation_id_exist(representation_id):
|
||||
return
|
||||
|
||||
representation = geometry.resolve_mapped_representation(representation)
|
||||
existing_data = geometry.get_representation_data(representation)
|
||||
|
||||
@@ -79,6 +79,14 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
def does_object_have_mesh_with_faces(cls, obj):
|
||||
return bool(isinstance(obj.data, bpy.types.Mesh) and len(obj.data.polygons))
|
||||
|
||||
@classmethod
|
||||
def does_representation_id_exist(cls, representation_id):
|
||||
try:
|
||||
tool.Ifc.get().by_id(representation_id)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def duplicate_object_data(cls, obj):
|
||||
return obj.data.copy()
|
||||
@@ -149,6 +157,10 @@ class Geometry(blenderbim.core.tool.Geometry):
|
||||
def get_representation_data(cls, representation):
|
||||
return bpy.data.meshes.get(cls.get_representation_name(representation))
|
||||
|
||||
@classmethod
|
||||
def get_representation_id(cls, representation):
|
||||
return representation.id()
|
||||
|
||||
@classmethod
|
||||
def get_representation_name(cls, representation):
|
||||
return f"{representation.ContextOfItems.id()}/{representation.id()}"
|
||||
|
||||
@@ -33,14 +33,14 @@ Scenario: Add representation - add a new representation to a typed instance
|
||||
And I press "bim.assign_class"
|
||||
And I press "bim.add_type_instance"
|
||||
And I press "bim.add_type_instance"
|
||||
Then the object "IfcWall/Instance" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
And the object "IfcWall/Instance.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
When the object "IfcWall/Instance" is selected
|
||||
Then the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
And the object "IfcWall/Wall.001" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
When the object "IfcWall/Wall" is selected
|
||||
And the variable "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
|
||||
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
||||
And I press "bim.add_representation"
|
||||
Then the object "IfcWall/Instance" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
And the object "IfcWall/Instance.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
|
||||
Scenario: Add representation - add a representation with a scale factor applied
|
||||
Given an empty IFC project
|
||||
@@ -79,14 +79,14 @@ Scenario: Switch representation - current edited representation is updated prior
|
||||
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 the variable "context" is "[c for c in {ifc}.by_type('IfcGeometricRepresentationSubContext') if c.ContextIdentifier=='Annotation'][0].id()"
|
||||
And I set "scene.BIMRootProperties.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)"
|
||||
And the variable "representation" is "[r for r in {ifc}.by_type('IfcShapeRepresentation') if r.RepresentationType=='Tessellation'][0].id()"
|
||||
And I press "bim.switch_representation(ifc_definition_id={representation}, should_reload=True)"
|
||||
And the variable "representation" is "[r for r in {ifc}.by_type('IfcShapeRepresentation') if r.RepresentationType=='Annotation2D'][0].id()"
|
||||
And I press "bim.switch_representation(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"
|
||||
|
||||
@@ -153,8 +153,8 @@ Scenario: Remove representation - remove an instanced representation from an act
|
||||
When the variable "representation" is "{ifc}.by_type('IfcWallType')[0].RepresentationMaps[1].MappedRepresentation.id()"
|
||||
And I press "bim.remove_representation(representation_id={representation})"
|
||||
Then the object "IfcWallType/Cube" has no data
|
||||
Then the object "IfcWall/Instance" has no data
|
||||
Then the object "IfcWall/Instance.001" has no data
|
||||
Then the object "IfcWall/Wall" has no data
|
||||
Then the object "IfcWall/Wall.001" has no data
|
||||
|
||||
Scenario: Remove representation - remove an instanced representation from an active instance object
|
||||
Given an empty IFC project
|
||||
@@ -168,12 +168,12 @@ Scenario: Remove representation - remove an instanced representation from an act
|
||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
||||
And I press "bim.add_type_instance"
|
||||
And I press "bim.add_type_instance"
|
||||
And the object "IfcWall/Instance" is selected
|
||||
And the object "IfcWall/Wall" is selected
|
||||
When the variable "representation" is "{ifc}.by_type('IfcWall')[0].Representation.Representations[1].id()"
|
||||
And I press "bim.remove_representation(representation_id={representation})"
|
||||
Then the object "IfcWallType/Cube" has no data
|
||||
Then the object "IfcWall/Instance" has no data
|
||||
Then the object "IfcWall/Instance.001" has no data
|
||||
Then the object "IfcWall/Wall" has no data
|
||||
Then the object "IfcWall/Wall.001" has no data
|
||||
|
||||
Scenario: Update representation - updating a tessellation
|
||||
Given an empty IFC project
|
||||
@@ -341,10 +341,10 @@ Scenario: Override duplicate move - copying a type instance with a representatio
|
||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
||||
And I press "bim.add_type_instance"
|
||||
And the object "IfcWall/Instance" is selected
|
||||
And the object "IfcWall/Wall" is selected
|
||||
When I press "object.duplicate_move"
|
||||
Then the object "IfcWall/Instance.001" exists
|
||||
And the object "IfcWall/Instance.001" has a "MappedRepresentation" representation of "Model/Body/MODEL_VIEW"
|
||||
Then the object "IfcWall/Wall.001" exists
|
||||
And the object "IfcWall/Wall.001" has a "MappedRepresentation" representation of "Model/Body/MODEL_VIEW"
|
||||
|
||||
Scenario: Override duplicate move - copying a layered extrusion
|
||||
Given an empty IFC project
|
||||
|
||||
@@ -12,7 +12,7 @@ Scenario: Add type instance - add from a mesh
|
||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
||||
When I press "bim.add_type_instance"
|
||||
Then the object "IfcWall/Instance" exists
|
||||
Then the object "IfcWall/Wall" exists
|
||||
|
||||
Scenario: Add type instance - add from an empty
|
||||
Given an empty IFC project
|
||||
@@ -25,7 +25,7 @@ Scenario: Add type instance - add from an empty
|
||||
And the variable "empty" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type" to "{empty}"
|
||||
When I press "bim.add_type_instance"
|
||||
Then the object "IfcWall/Instance" exists
|
||||
Then the object "IfcWall/Wall" exists
|
||||
|
||||
Scenario: Add type instance - add a mesh where existing instances have changed context
|
||||
Given an empty IFC project
|
||||
@@ -38,15 +38,15 @@ Scenario: Add type instance - add a mesh where existing instances have changed c
|
||||
And the variable "cube" is "{ifc}.by_type('IfcWallType')[0].id()"
|
||||
And I set "scene.BIMModelProperties.relating_type" to "{cube}"
|
||||
And I press "bim.add_type_instance"
|
||||
And the object "IfcWall/Instance" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
And the object "IfcWall/Instance" is selected
|
||||
And the object "IfcWall/Wall" data is a "Tessellation" representation of "Model/Body/MODEL_VIEW"
|
||||
And the object "IfcWall/Wall" is selected
|
||||
And the variable "context" is "{ifc}.by_type('IfcGeometricRepresentationSubContext')[-1].id()"
|
||||
And I set "scene.BIMRootProperties.contexts" to "{context}"
|
||||
And I press "bim.add_representation"
|
||||
And the object "IfcWall/Instance" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
And the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
When I press "bim.add_type_instance"
|
||||
Then the object "IfcWall/Instance" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
And the object "IfcWall/Instance.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
Then the object "IfcWall/Wall" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
And the object "IfcWall/Wall.001" data is a "Annotation2D" representation of "Plan/Annotation/PLAN_VIEW"
|
||||
|
||||
Scenario: Add grid
|
||||
Given an empty IFC project
|
||||
|
||||
@@ -272,7 +272,9 @@ class TestSwitchRepresentation:
|
||||
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.get_representation_id("mapped_rep").should_be_called().will_return("representation_id")
|
||||
geometry.run_geometry_update_representation(obj="obj").should_be_called()
|
||||
geometry.does_representation_id_exist("representation_id").should_be_called().will_return(True)
|
||||
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()
|
||||
@@ -287,6 +289,22 @@ class TestSwitchRepresentation:
|
||||
should_sync_changes_first=True,
|
||||
)
|
||||
|
||||
def test_not_switching_if_an_updated_representation_is_the_same_one_we_were_going_to_switch_to(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.get_representation_id("mapped_rep").should_be_called().will_return("representation_id")
|
||||
geometry.run_geometry_update_representation(obj="obj").should_be_called()
|
||||
geometry.does_representation_id_exist("representation_id").should_be_called().will_return(False)
|
||||
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):
|
||||
|
||||
@@ -121,6 +121,15 @@ class TestDoesObjectHaveMeshWithFaces(NewFile):
|
||||
assert subject.does_object_have_mesh_with_faces(obj) is True
|
||||
|
||||
|
||||
class TestDoesRepresentationIdExist(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
tool.Ifc.set(ifc)
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
assert subject.does_representation_id_exist(representation.id()) is True
|
||||
assert subject.does_representation_id_exist(12345) is False
|
||||
|
||||
|
||||
class TestDuplicateObjectData(NewFile):
|
||||
def test_run(self):
|
||||
data = bpy.data.meshes.new("Mesh")
|
||||
@@ -160,6 +169,13 @@ class TestGetRepresentationData(NewFile):
|
||||
assert subject.get_representation_data(representation) == data
|
||||
|
||||
|
||||
class TestGetRepresentationId(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
representation = ifc.createIfcShapeRepresentation()
|
||||
assert subject.get_representation_id(representation) == representation.id()
|
||||
|
||||
|
||||
class TestGetRepresentationName(NewFile):
|
||||
def test_run(self):
|
||||
ifc = ifcopenshell.file()
|
||||
|
||||
Reference in New Issue
Block a user