Fix Image empty interpreted as Mesh #5194

Apparently we can't trust `obj.data == None` to identify empties since in Blender there are empties that can use `bpy.types.Image` as their `obj.data`.
This commit is contained in:
Andrej730
2024-08-21 14:47:07 +05:00
parent 45fed747c2
commit ef04402ed8
6 changed files with 36 additions and 9 deletions
@@ -364,7 +364,8 @@ class UpdateRepresentation(bpy.types.Operator, tool.Ifc.Operator):
# TODO: write unit tests to see how this bulk operation handles # TODO: write unit tests to see how this bulk operation handles
# contradictory ifc_representation_class values and when # contradictory ifc_representation_class values and when
# ifc_representation_class is IfcTextLiteral # ifc_representation_class is IfcTextLiteral
if not obj.data: data = obj.data
if not tool.Geometry.is_data_supported_for_adding_representation(data):
continue continue
self.update_obj_mesh_representation(context, obj) self.update_obj_mesh_representation(context, obj)
tool.Ifc.finish_edit(obj) tool.Ifc.finish_edit(obj)
@@ -172,7 +172,13 @@ class AssignClass(bpy.types.Operator, tool.Ifc.Operator):
def _execute(self, context): def _execute(self, context):
props = context.scene.BIMRootProperties props = context.scene.BIMRootProperties
objects = [bpy.data.objects.get(self.obj)] if self.obj else context.selected_objects or [context.active_object] objects: list[bpy.types.Object] = []
if self.obj:
objects = [bpy.data.objects[self.obj]]
elif objects := context.selected_objects:
pass
elif obj := context.active_object:
objects = [obj]
if not objects: if not objects:
self.report({"INFO"}, "No objects selected.") self.report({"INFO"}, "No objects selected.")
+4 -3
View File
@@ -17,7 +17,7 @@
# along with Bonsai. If not, see <http://www.gnu.org/licenses/>. # along with Bonsai. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Optional, Sequence from typing import TYPE_CHECKING, Optional, Sequence, Union
if TYPE_CHECKING: if TYPE_CHECKING:
import bpy import bpy
@@ -47,7 +47,8 @@ def add_representation(
context: ifcopenshell.entity_instance, context: ifcopenshell.entity_instance,
ifc_representation_class: Optional[str] = None, ifc_representation_class: Optional[str] = None,
profile_set_usage: Optional[ifcopenshell.entity_instance] = None, profile_set_usage: Optional[ifcopenshell.entity_instance] = None,
) -> ifcopenshell.entity_instance: ) -> Union[ifcopenshell.entity_instance, None]:
"""Add IFC representation based on object `.data`."""
element = ifc.get_entity(obj) element = ifc.get_entity(obj)
if not element: if not element:
return return
@@ -55,7 +56,7 @@ def add_representation(
edit_object_placement(ifc, geometry, surveyor, obj=obj) edit_object_placement(ifc, geometry, surveyor, obj=obj)
data = geometry.get_object_data(obj) data = geometry.get_object_data(obj)
if not data and ifc_representation_class != "IfcTextLiteral": if not geometry.is_data_supported_for_adding_representation(data) and ifc_representation_class != "IfcTextLiteral":
return return
representation = ifc.run( representation = ifc.run(
+1
View File
@@ -407,6 +407,7 @@ class Geometry:
def import_representation_parameters(cls, data): pass def import_representation_parameters(cls, data): pass
def is_body_representation(cls, representation): pass def is_body_representation(cls, representation): pass
def is_box_representation(cls, representation): pass def is_box_representation(cls, representation): pass
def is_data_supported_for_adding_representation(cls, data): pass
def is_edited(cls, obj): pass def is_edited(cls, obj): pass
def is_mapped_representation(cls, representation): pass def is_mapped_representation(cls, representation): pass
def is_type_product(cls, element): pass def is_type_product(cls, element): pass
+18 -3
View File
@@ -41,8 +41,8 @@ from collections import defaultdict
from math import radians, pi from math import radians, pi
from mathutils import Vector, Matrix from mathutils import Vector, Matrix
from bonsai.bim.ifc import IfcStore from bonsai.bim.ifc import IfcStore
from typing import Union, Iterable, Optional, Literal from typing import Union, Iterable, Optional, Literal, Iterator
from typing import Iterator from typing_extensions import TypeIs
class Geometry(bonsai.core.tool.Geometry): class Geometry(bonsai.core.tool.Geometry):
@@ -477,7 +477,7 @@ class Geometry(bonsai.core.tool.Geometry):
return hasher.hexdigest() return hasher.hexdigest()
@classmethod @classmethod
def get_object_data(cls, obj: bpy.types.Object) -> bpy.types.ID: def get_object_data(cls, obj: bpy.types.Object) -> Union[bpy.types.ID, None]:
return obj.data return obj.data
@classmethod @classmethod
@@ -646,6 +646,21 @@ class Geometry(bonsai.core.tool.Geometry):
def is_box_representation(cls, representation: ifcopenshell.entity_instance) -> bool: def is_box_representation(cls, representation: ifcopenshell.entity_instance) -> bool:
return representation.ContextOfItems.ContextIdentifier == "Box" return representation.ContextOfItems.ContextIdentifier == "Box"
@classmethod
def is_data_supported_for_adding_representation(cls, data: Union[bpy.types.ID, None]) -> TypeIs[
Union[
bpy.types.Mesh,
bpy.types.Curve,
]
]:
supported_types = (
bpy.types.Mesh,
bpy.types.Curve,
)
if not data:
return False
return isinstance(data, supported_types)
@classmethod @classmethod
def is_edited(cls, obj: bpy.types.Object) -> bool: def is_edited(cls, obj: bpy.types.Object) -> bool:
return not all([tool.Cad.is_x(o, 1.0) for o in obj.scale]) or obj in IfcStore.edited_objs return not all([tool.Cad.is_x(o, 1.0) for o in obj.scale]) or obj in IfcStore.edited_objs
+4 -1
View File
@@ -42,6 +42,7 @@ class TestAddRepresentation:
# Add representation # Add representation
geometry.get_object_data("obj").should_be_called().will_return("data") geometry.get_object_data("obj").should_be_called().will_return("data")
geometry.is_data_supported_for_adding_representation("data").should_be_called().will_return(True)
geometry.get_cartesian_point_coordinate_offset("obj").should_be_called().will_return("coordinate_offset") geometry.get_cartesian_point_coordinate_offset("obj").should_be_called().will_return("coordinate_offset")
geometry.get_total_representation_items("obj").should_be_called().will_return(1) geometry.get_total_representation_items("obj").should_be_called().will_return(1)
geometry.should_force_faceted_brep().should_be_called().will_return(False) geometry.should_force_faceted_brep().should_be_called().will_return(False)
@@ -108,6 +109,7 @@ class TestAddRepresentation:
# Add representation # Add representation
geometry.get_object_data("obj").should_be_called().will_return("data") geometry.get_object_data("obj").should_be_called().will_return("data")
geometry.is_data_supported_for_adding_representation("data").should_be_called().will_return(True)
geometry.get_cartesian_point_coordinate_offset("obj").should_be_called().will_return("coordinate_offset") geometry.get_cartesian_point_coordinate_offset("obj").should_be_called().will_return("coordinate_offset")
geometry.get_total_representation_items("obj").should_be_called().will_return(1) geometry.get_total_representation_items("obj").should_be_called().will_return(1)
geometry.should_force_faceted_brep().should_be_called().will_return(False) geometry.should_force_faceted_brep().should_be_called().will_return(False)
@@ -158,7 +160,8 @@ class TestAddRepresentation:
TestEditObjectPlacement.predict(self, ifc, geometry, surveyor) TestEditObjectPlacement.predict(self, ifc, geometry, surveyor)
# Add representation # Add representation
geometry.get_object_data("obj").should_be_called().will_return(None) geometry.get_object_data("obj").should_be_called().will_return("data")
geometry.is_data_supported_for_adding_representation("data").should_be_called().will_return(False)
assert ( assert (
subject.add_representation( subject.add_representation(
ifc, ifc,