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 # 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_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.add(num_vertices)
mesh.vertices.foreach_set("co", verts) mesh.vertices.foreach_set("co", verts)
@@ -1651,6 +1651,7 @@ class OverrideModeSetEdit(bpy.types.Operator):
should_sync_changes_first=False, should_sync_changes_first=False,
apply_openings=False, apply_openings=False,
) )
tool.Geometry.apply_item_ids_as_vertex_groups(obj)
tool.Geometry.dissolve_triangulated_edges(obj) tool.Geometry.dissolve_triangulated_edges(obj)
obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data) obj.data.BIMMeshProperties.mesh_checksum = tool.Geometry.get_mesh_checksum(obj.data)
else: else:
@@ -37,6 +37,7 @@ import blenderbim.core.system
import blenderbim.core.geometry import blenderbim.core.geometry
import blenderbim.tool as tool import blenderbim.tool as tool
import blenderbim.bim.import_ifc import blenderbim.bim.import_ifc
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 blenderbim.bim.ifc import IfcStore from blenderbim.bim.ifc import IfcStore
@@ -173,6 +174,39 @@ class Geometry(blenderbim.core.tool.Geometry):
bm.free() bm.free()
del obj.data["ios_edges"] 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 @classmethod
def does_representation_id_exist(cls, representation_id: int) -> bool: def does_representation_id_exist(cls, representation_id: int) -> bool:
try: try:
+32
View File
@@ -727,3 +727,35 @@ class TestRemoveRepresentationItemFromShapeAspect(NewFile):
subject.remove_representation_items_from_shape_aspect([items[1]], shape_aspect1) subject.remove_representation_items_from_shape_aspect([items[1]], shape_aspect1)
representation = shape_aspect1.ShapeRepresentations[0] representation = shape_aspect1.ShapeRepresentations[0]
assert set(representation.Items) == {items[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)]) 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]: def get_shape_vertices(shape: ShapeType, geometry: ShapeType) -> npt.NDArray[np.float64]:
"""Get the shape's vertices as a numpy array """Get the shape's vertices as a numpy array