mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-20 15:08:51 +00:00
Fix representation where removing a representation left objects with an empty mesh. Now a proper empty is used.
This commit is contained in:
@@ -108,37 +108,15 @@ class RemoveRepresentation(bpy.types.Operator, Operator):
|
|||||||
bl_idname = "bim.remove_representation"
|
bl_idname = "bim.remove_representation"
|
||||||
bl_label = "Remove Representation"
|
bl_label = "Remove Representation"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
obj: bpy.props.StringProperty()
|
|
||||||
representation_id: bpy.props.IntProperty()
|
representation_id: bpy.props.IntProperty()
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
self.file = IfcStore.get_file()
|
core.remove_representation(
|
||||||
representation = self.file.by_id(self.representation_id)
|
tool.Ifc,
|
||||||
obj = bpy.data.objects.get(self.obj) if self.obj else context.active_object
|
tool.Geometry,
|
||||||
is_mapped_representation = representation.RepresentationType == "MappedRepresentation"
|
obj=context.active_object,
|
||||||
if is_mapped_representation:
|
representation=tool.Ifc.get().by_id(self.representation_id),
|
||||||
mesh_name = "{}/{}".format(
|
|
||||||
representation.ContextOfItems.id(), representation.Items[0].MappingSource.MappedRepresentation.id()
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
mesh_name = "{}/{}".format(representation.ContextOfItems.id(), representation.id())
|
|
||||||
mesh = bpy.data.meshes.get(mesh_name)
|
|
||||||
if mesh:
|
|
||||||
if obj.data == mesh:
|
|
||||||
# TODO we can do better than this
|
|
||||||
void_mesh = bpy.data.meshes.get("Void")
|
|
||||||
if not void_mesh:
|
|
||||||
void_mesh = bpy.data.meshes.new("Void")
|
|
||||||
obj.data = void_mesh
|
|
||||||
if not is_mapped_representation:
|
|
||||||
bpy.data.meshes.remove(mesh)
|
|
||||||
product = self.file.by_id(obj.BIMObjectProperties.ifc_definition_id)
|
|
||||||
ifcopenshell.api.run(
|
|
||||||
"geometry.unassign_representation", self.file, **{"product": product, "representation": representation}
|
|
||||||
)
|
)
|
||||||
ifcopenshell.api.run("geometry.remove_representation", self.file, **{"representation": representation})
|
|
||||||
Data.load(self.file, product.id())
|
|
||||||
return {"FINISHED"}
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateRepresentation(bpy.types.Operator):
|
class UpdateRepresentation(bpy.types.Operator):
|
||||||
@@ -236,7 +214,7 @@ class UpdateRepresentation(bpy.types.Operator):
|
|||||||
|
|
||||||
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
obj.data.BIMMeshProperties.ifc_definition_id = int(new_representation.id())
|
||||||
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
obj.data.name = f"{old_representation.ContextOfItems.id()}/{new_representation.id()}"
|
||||||
bpy.ops.bim.remove_representation(representation_id=old_representation.id(), obj=obj.name)
|
core.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_representation)
|
||||||
Data.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
|
Data.load(self.file, obj.BIMObjectProperties.ifc_definition_id)
|
||||||
if obj.data.BIMMeshProperties.ifc_parameters:
|
if obj.data.BIMMeshProperties.ifc_parameters:
|
||||||
bpy.ops.bim.get_representation_ifc_parameters()
|
bpy.ops.bim.get_representation_ifc_parameters()
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ def generate_box(usecase_path, ifc_file, settings):
|
|||||||
old_box = ifcopenshell.util.representation.get_representation(product, "Model", "Box", "MODEL_VIEW")
|
old_box = ifcopenshell.util.representation.get_representation(product, "Model", "Box", "MODEL_VIEW")
|
||||||
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
||||||
if old_box:
|
if old_box:
|
||||||
bpy.ops.bim.remove_representation(representation_id=old_box.id(), obj=obj.name)
|
blenderbim.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_box)
|
||||||
|
|
||||||
new_settings = settings.copy()
|
new_settings = settings.copy()
|
||||||
new_settings["context"] = box_context
|
new_settings["context"] = box_context
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import ifcopenshell.util.element
|
|||||||
import mathutils.geometry
|
import mathutils.geometry
|
||||||
import blenderbim.bim.handler
|
import blenderbim.bim.handler
|
||||||
import blenderbim.core.type
|
import blenderbim.core.type
|
||||||
|
import blenderbim.core.geometry
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from math import pi, degrees
|
from math import pi, degrees
|
||||||
@@ -112,7 +113,9 @@ def generate_footprint(usecase_path, ifc_file, settings):
|
|||||||
old_footprint = ifcopenshell.util.representation.get_representation(product, "Plan", "FootPrint", "SKETCH_VIEW")
|
old_footprint = ifcopenshell.util.representation.get_representation(product, "Plan", "FootPrint", "SKETCH_VIEW")
|
||||||
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
||||||
if old_footprint:
|
if old_footprint:
|
||||||
bpy.ops.bim.remove_representation(representation_id=old_footprint.id(), obj=obj.name)
|
blenderbim.core.geometry.remove_representation(
|
||||||
|
tool.Ifc, tool.Geometry, obj=obj, representation=old_footprint
|
||||||
|
)
|
||||||
|
|
||||||
helper = Helper(ifc_file)
|
helper = Helper(ifc_file)
|
||||||
indices = helper.auto_detect_arbitrary_profile_with_voids_extruded_area_solid(settings["geometry"])
|
indices = helper.auto_detect_arbitrary_profile_with_voids_extruded_area_solid(settings["geometry"])
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import mathutils.geometry
|
|||||||
import blenderbim.bim.handler
|
import blenderbim.bim.handler
|
||||||
import blenderbim.core.type
|
import blenderbim.core.type
|
||||||
import blenderbim.core.root
|
import blenderbim.core.root
|
||||||
|
import blenderbim.core.geometry
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
from blenderbim.bim.ifc import IfcStore
|
from blenderbim.bim.ifc import IfcStore
|
||||||
from ifcopenshell.api.pset.data import Data as PsetData
|
from ifcopenshell.api.pset.data import Data as PsetData
|
||||||
@@ -828,7 +829,7 @@ def generate_axis(usecase_path, ifc_file, settings):
|
|||||||
old_axis = ifcopenshell.util.representation.get_representation(product, "Model", "Axis", "GRAPH_VIEW")
|
old_axis = ifcopenshell.util.representation.get_representation(product, "Model", "Axis", "GRAPH_VIEW")
|
||||||
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
if settings["context"].ContextType == "Model" and getattr(settings["context"], "ContextIdentifier") == "Body":
|
||||||
if old_axis:
|
if old_axis:
|
||||||
bpy.ops.bim.remove_representation(representation_id=old_axis.id(), obj=obj.name)
|
blenderbim.core.geometry.remove_representation(tool.Ifc, tool.Geometry, obj=obj, representation=old_axis)
|
||||||
|
|
||||||
new_settings = settings.copy()
|
new_settings = settings.copy()
|
||||||
new_settings["context"] = axis_context
|
new_settings["context"] = axis_context
|
||||||
|
|||||||
@@ -76,6 +76,14 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_element_type(cls, element):
|
||||||
|
return ifcopenshell.util.element.get_type(element)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_elements_of_type(cls, type):
|
||||||
|
return ifcopenshell.util.element.get_types(type)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_ifc_representation_class(cls, element, representation):
|
def get_ifc_representation_class(cls, element, representation):
|
||||||
material = ifcopenshell.util.element.get_material(element)
|
material = ifcopenshell.util.element.get_material(element)
|
||||||
@@ -124,6 +132,10 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
def get_total_representation_items(cls, obj):
|
def get_total_representation_items(cls, obj):
|
||||||
return max(1, len(obj.material_slots))
|
return max(1, len(obj.material_slots))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def has_data_users(cls, data):
|
||||||
|
return data.users != 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def import_representation(cls, obj, representation, enable_dynamic_voids=False):
|
def import_representation(cls, obj, representation, enable_dynamic_voids=False):
|
||||||
logger = logging.getLogger("ImportIFC")
|
logger = logging.getLogger("ImportIFC")
|
||||||
@@ -152,6 +164,14 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
def is_body_representation(cls, representation):
|
def is_body_representation(cls, representation):
|
||||||
return representation.ContextOfItems.ContextIdentifier == "Body"
|
return representation.ContextOfItems.ContextIdentifier == "Body"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_mapped_representation(cls, representation):
|
||||||
|
return representation.RepresentationType == "MappedRepresentation"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_type_product(cls, element):
|
||||||
|
return element.is_a("IfcTypeProduct")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def link(cls, element, obj):
|
def link(cls, element, obj):
|
||||||
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
obj.BIMMeshProperties.ifc_definition_id = element.id()
|
||||||
@@ -160,6 +180,20 @@ class Geometry(blenderbim.core.tool.Geometry):
|
|||||||
def rename_object(cls, obj, name):
|
def rename_object(cls, obj, name):
|
||||||
obj.name = name
|
obj.name = name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def replace_object_with_empty(cls, obj):
|
||||||
|
element = tool.Ifc.get_entity(obj)
|
||||||
|
name = obj.name
|
||||||
|
tool.Ifc.unlink(obj)
|
||||||
|
obj.name = ifcopenshell.guid.new()
|
||||||
|
new_obj = bpy.data.objects.new(name, None)
|
||||||
|
if element:
|
||||||
|
tool.Ifc.link(element, new_obj)
|
||||||
|
for collection in obj.users_collection:
|
||||||
|
collection.objects.link(new_obj)
|
||||||
|
new_obj.matrix_world = obj.matrix_world
|
||||||
|
bpy.data.objects.remove(obj)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def resolve_mapped_representation(cls, representation):
|
def resolve_mapped_representation(cls, representation):
|
||||||
if representation.RepresentationType == "MappedRepresentation":
|
if representation.RepresentationType == "MappedRepresentation":
|
||||||
|
|||||||
@@ -84,6 +84,66 @@ Scenario: Switch representation - existing Blender modifiers must be purged
|
|||||||
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation})"
|
And I press "bim.switch_representation(obj='IfcWall/Cube', ifc_definition_id={representation})"
|
||||||
Then the object "IfcWall/Cube" has no modifiers
|
Then the object "IfcWall/Cube" has no modifiers
|
||||||
|
|
||||||
|
Scenario: Remove representation - remove an active representation
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I add an array modifier
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[0].id()"
|
||||||
|
And I press "bim.remove_representation(representation_id={representation})"
|
||||||
|
Then the object "IfcWall/Cube" has no data
|
||||||
|
|
||||||
|
Scenario: Remove representation - remove an unloaded representation
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I add an array modifier
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
When the variable "representation" is "{ifc}.by_type('IfcShapeRepresentation')[1].id()"
|
||||||
|
And I press "bim.remove_representation(representation_id={representation})"
|
||||||
|
Then the object "IfcWall/Cube" has data which is an IFC representation
|
||||||
|
|
||||||
|
Scenario: Remove representation - remove an instanced representation from an active type object
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
|
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 I press "bim.add_type_instance"
|
||||||
|
And the object "IfcWallType/Cube" is selected
|
||||||
|
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
|
||||||
|
|
||||||
|
Scenario: Remove representation - remove an instanced representation from an active instance object
|
||||||
|
Given an empty IFC project
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_product" to "IfcElementType"
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWallType"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And I set "scene.BIMModelProperties.ifc_class" to "IfcWallType"
|
||||||
|
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 I press "bim.add_type_instance"
|
||||||
|
And the object "IfcWall/Instance" 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
|
||||||
|
|
||||||
Scenario: Update representation - updating a tessellation
|
Scenario: Update representation - updating a tessellation
|
||||||
Given an empty IFC project
|
Given an empty IFC project
|
||||||
And I add a cube
|
And I add a cube
|
||||||
|
|||||||
@@ -356,6 +356,11 @@ def the_object_name_is_not_an_ifc_element(name):
|
|||||||
assert id == 0, f"The ID is {id}"
|
assert id == 0, f"The ID is {id}"
|
||||||
|
|
||||||
|
|
||||||
|
@then(parsers.parse('the object "{name}" has no data'))
|
||||||
|
def the_object_name_has_no_data(name):
|
||||||
|
assert the_object_name_exists(name).data is None
|
||||||
|
|
||||||
|
|
||||||
@then(parsers.parse('the object "{name}" has data which is an IFC representation'))
|
@then(parsers.parse('the object "{name}" has data which is an IFC representation'))
|
||||||
def the_object_name_is_not_an_ifc_element(name):
|
def the_object_name_is_not_an_ifc_element(name):
|
||||||
id = the_object_name_exists(name).data.BIMMeshProperties.ifc_definition_id
|
id = the_object_name_exists(name).data.BIMMeshProperties.ifc_definition_id
|
||||||
|
|||||||
@@ -184,6 +184,26 @@ class TestGetCartesianPointCoordinateOffset(NewFile):
|
|||||||
assert subject.get_cartesian_point_coordinate_offset(obj) is None
|
assert subject.get_cartesian_point_coordinate_offset(obj) is None
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetElementType(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
element = ifc.createIfcWall()
|
||||||
|
type = ifc.createIfcWallType()
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=element, relating_type=type)
|
||||||
|
assert subject.get_element_type(element) == type
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetElementsOfType(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
bpy.ops.bim.create_project()
|
||||||
|
ifc = tool.Ifc.get()
|
||||||
|
element = ifc.createIfcWall()
|
||||||
|
type = ifc.createIfcWallType()
|
||||||
|
ifcopenshell.api.run("type.assign_type", ifc, related_object=element, relating_type=type)
|
||||||
|
assert subject.get_elements_of_type(type) == (element,)
|
||||||
|
|
||||||
|
|
||||||
class TestGetTotalRepresentationItems(NewFile):
|
class TestGetTotalRepresentationItems(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
material1 = bpy.data.materials.new("Material")
|
material1 = bpy.data.materials.new("Material")
|
||||||
@@ -194,6 +214,14 @@ class TestGetTotalRepresentationItems(NewFile):
|
|||||||
assert subject.get_total_representation_items(obj) == 2
|
assert subject.get_total_representation_items(obj) == 2
|
||||||
|
|
||||||
|
|
||||||
|
class TestHasDataUsers(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
data = bpy.data.meshes.new("Mesh")
|
||||||
|
assert subject.has_data_users(data) is False
|
||||||
|
bpy.data.objects.new("Object", data)
|
||||||
|
assert subject.has_data_users(data) is True
|
||||||
|
|
||||||
|
|
||||||
class TestImportRepresentation(NewFile):
|
class TestImportRepresentation(NewFile):
|
||||||
def test_importing_a_normal_shape(self):
|
def test_importing_a_normal_shape(self):
|
||||||
ifc = ifcopenshell.open("test/files/basic.ifc")
|
ifc = ifcopenshell.open("test/files/basic.ifc")
|
||||||
@@ -232,6 +260,22 @@ class TestIsBodyRepresentation(NewFile):
|
|||||||
assert subject.is_body_representation(representation) is False
|
assert subject.is_body_representation(representation) is False
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsMappedRepresentation(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
representation = ifc.createIfcShapeRepresentation()
|
||||||
|
assert subject.is_mapped_representation(representation) is False
|
||||||
|
representation.RepresentationType = "MappedRepresentation"
|
||||||
|
assert subject.is_mapped_representation(representation) is True
|
||||||
|
|
||||||
|
|
||||||
|
class TestIsTypeProduct(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
assert subject.is_type_product(ifc.createIfcWall()) is False
|
||||||
|
assert subject.is_type_product(ifc.createIfcWallType()) is True
|
||||||
|
|
||||||
|
|
||||||
class TestLink(NewFile):
|
class TestLink(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
@@ -241,13 +285,31 @@ class TestLink(NewFile):
|
|||||||
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
|
assert obj.BIMMeshProperties.ifc_definition_id == element.id()
|
||||||
|
|
||||||
|
|
||||||
class TestRenameObjectData(NewFile):
|
class TestRenameObject(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
obj = bpy.data.meshes.new("Mesh")
|
obj = bpy.data.meshes.new("Mesh")
|
||||||
subject.rename_object(obj, "name")
|
subject.rename_object(obj, "name")
|
||||||
assert obj.name == "name"
|
assert obj.name == "name"
|
||||||
|
|
||||||
|
|
||||||
|
class TestReplaceObjectWithEmpty(NewFile):
|
||||||
|
def test_run(self):
|
||||||
|
ifc = ifcopenshell.file()
|
||||||
|
tool.Ifc.set(ifc)
|
||||||
|
obj = bpy.data.objects.new("Object", bpy.data.meshes.new("Mesh"))
|
||||||
|
obj.matrix_world[0][3] = 1
|
||||||
|
bpy.context.scene.collection.objects.link(obj)
|
||||||
|
element = ifc.createIfcWall()
|
||||||
|
tool.Ifc.link(element, obj)
|
||||||
|
subject.replace_object_with_empty(obj)
|
||||||
|
obj = bpy.data.objects.get("Object")
|
||||||
|
assert obj.users_collection[0] == bpy.context.scene.collection
|
||||||
|
assert tool.Ifc.get_entity(obj) == element
|
||||||
|
assert tool.Ifc.get_object(element) == obj
|
||||||
|
assert obj.data is None
|
||||||
|
assert obj.matrix_world[0][3] == 1
|
||||||
|
|
||||||
|
|
||||||
class TestResolveMappedRepresentation(NewFile):
|
class TestResolveMappedRepresentation(NewFile):
|
||||||
def test_run(self):
|
def test_run(self):
|
||||||
ifc = ifcopenshell.file()
|
ifc = ifcopenshell.file()
|
||||||
|
|||||||
Reference in New Issue
Block a user