Store item_ids in imported meshes and set them on toggling edit mode

It will be used later to add more IFC capabilities to EDIT mode.

Example - https://imgur.com/a/HAkjoCY
This commit is contained in:
Andrej730
2024-07-08 17:40:07 +05:00
parent 7c1459b83e
commit acf135a6ed
5 changed files with 73 additions and 0 deletions
@@ -1387,6 +1387,7 @@ class IfcImporter:
#
# we do `.tolist()` because Blender can't assign `np.int32` to it's custom attributes
mesh["ios_edges"] = list(set(tuple(e) for e in ifcopenshell.util.shape.get_edges(geometry).tolist()))
mesh["ios_item_ids"] = ifcopenshell.util.shape.get_representation_item_ids(geometry).tolist()
mesh.vertices.add(num_vertices)
mesh.vertices.foreach_set("co", verts)
@@ -1651,6 +1651,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
should_sync_changes_first=False,
apply_openings=False,
)
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
tool.Geometry.dissolve_triangulated_edges(obj)
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
else:
@@ -37,6 +37,7 @@ import blenderbim.core.system
import blenderbim.core.geometry
import blenderbim.tool as tool
import blenderbim.bim.import_ifc
from collections import defaultdict
from math import radians, pi
from mathutils import Vector, Matrix
from blenderbim.bim.ifc import IfcStore
@@ -173,6 +174,39 @@ class Geometry(blenderbim.core.tool.Geometry):
bm.free()
del obj.data["ios_edges"]
@classmethod
def apply_item_ids_as_vertex_groups(cls, obj: bpy.types.Object) -> None:
"""Save mesh-object item_ids as vertex groups in format 'ios_item_id_xxxx'.
Since ios_item_ids are item ids for original faces (triangulated),
this method should be used before `dissolve_triangulated_edges`."""
mesh = obj.data
assert isinstance(mesh, bpy.types.Mesh)
# I guess, they're already applied.
if "ios_item_ids" not in mesh:
return
# Just to be safe.
if "ios_edges" not in mesh:
raise Exception("Triangulated edges are already dissolved, cannot aply item ids.")
polygon_verts = np.empty(len(mesh.polygons) * 3, dtype="I")
mesh.polygons.foreach_get("vertices", polygon_verts)
polygon_verts = polygon_verts.reshape(-1, 3)
ios_item_ids: list[int] = mesh["ios_item_ids"]
vertices_by_item_ids = defaultdict(list[int])
for i, item_id in enumerate(ios_item_ids):
# .tolist() as VertexGroup.add() is not ready for uints.
vertices_by_item_ids[item_id].extend(polygon_verts[i].tolist())
for item_id, verts in vertices_by_item_ids.items():
vg = obj.vertex_groups.new(name=f"ios_item_id_{item_id}")
vg.add(verts, weight=1.0, type="ADD")
del mesh["ios_item_ids"]
@classmethod
def does_representation_id_exist(cls, representation_id: int) -> bool:
try:
+32
View File
@@ -727,3 +727,35 @@ class TestRemoveRepresentationItemFromShapeAspect(NewFile):
subject.remove_representation_items_from_shape_aspect([items[1]], shape_aspect1)
representation = shape_aspect1.ShapeRepresentations[0]
assert set(representation.Items) == {items[0]}
class TestApplyItemIdsAsVertexGroups(NewFile):
def test_run(self):
bpy.ops.mesh.primitive_cube_add(size=10, location=(0, 0, 4))
obj = bpy.data.objects["Cube"]
bpy.ops.object.editmode_toggle()
bpy.ops.mesh.quads_convert_to_tris()
bpy.ops.mesh.duplicate_move()
bpy.ops.object.editmode_toggle()
mesh = obj.data
assert isinstance(mesh, bpy.types.Mesh)
assert len(mesh.polygons) == 24
mesh["ios_edges"] = [] # Method requirement.
ios_item_ids = (95,) * 12 + (45,) * 12
mesh["ios_item_ids"] = ios_item_ids
unique = tuple(dict.fromkeys(ios_item_ids))
ios_item_ids_unique = [unique.index(i) for i in ios_item_ids]
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
vertex_group_names = [vg.name for vg in obj.vertex_groups]
assert vertex_group_names == [f"ios_item_id_{i}" for i in unique]
verts = mesh.vertices
for i, polygon in enumerate(mesh.polygons):
for vi in polygon.vertices:
vert = verts[vi]
groups = [g.group for g in vert.groups]
assert groups == [ios_item_ids_unique[i]]
@@ -275,6 +275,11 @@ def get_faces(geometry: ShapeType) -> npt.NDArray[np.int32]:
return np.array([[faces[i], faces[i + 1], faces[i + 2]] for i in range(0, len(faces), 3)])
def get_representation_item_ids(geometry: ShapeType) -> npt.NDArray[np.int32]:
"""Get representation item ids for the geometry faces."""
return np.frombuffer(geometry.item_ids_buffer, dtype="i")
def get_shape_vertices(shape: ShapeType, geometry: ShapeType) -> npt.NDArray[np.float64]:
"""Get the shape's vertices as a numpy array