add_representation - detect non consequtive curves and handle them

Basically if current mesh curve is non consequtive we handle it in similar way to b03c4da7b - convert it to curves using Blender and then save them to ifc.

The advantage is that it creates less curves in ifc because mesh method wasn't able to detect separate curves correctly if vertex order was messed up (usually it is) and it would create separate curve for each curve segment.

I've also replaced figuring vertex duplicates with `bmesh.ops.remove_doubles` manually with `bmesh.ops.remove_doubles` and it's now much faster. In some cases because of the manual calculations it was taking too long - in #3233 Ryan mentiones that it freezes blender, it wasn't really freezing Blender but to finish calculations it would take 10 minutes.
This commit is contained in:
Andrej730
2023-07-04 14:38:15 +05:00
parent d81e64f7a2
commit bd70f7bf7f
@@ -362,52 +362,100 @@ class Usecase:
results.append(self.file.createIfcSweptDiskSolid(curve, radius))
return results
def is_mesh_curve_consequtive(self, geom_data):
import blenderbim.tool as tool
bm = tool.Blender.get_bmesh_for_mesh(geom_data)
bm.verts.ensure_lookup_table()
start_vert = bm.verts[0]
n_verts = len(bm.verts)
cur_edges = bm.verts[0].link_edges
if len(cur_edges) > 2:
return False
elif cur_edges == 2:
edge0, edge1 = cur_edges
else:
edge0, edge1 = cur_edges[0], None
processed_verts = set()
processed_verts.add(start_vert)
def validate_edge(edge, start_vert, processed_verts):
cur_vert = edge.other_vert(start_vert)
while True:
if cur_vert == start_vert:
break
if cur_vert in processed_verts:
return
processed_verts.add(cur_vert)
edges = cur_vert.link_edges
if len(edges) > 2:
return
elif len(edges) == 1:
return True
edge = next(e for e in edges if e != edge)
cur_vert = edge.other_vert(cur_vert)
return True
if not validate_edge(edge0, start_vert, processed_verts):
return
if edge1 and not validate_edge(edge1, start_vert, processed_verts):
return
if len(processed_verts) != n_verts:
return False
return True
def create_curves(self, should_exclude_faces=False, is_2d=False):
if isinstance(self.settings["geometry"], bpy.types.Mesh):
if self.file.schema == "IFC2X3":
return self.create_curves_from_mesh_ifc2x3(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
else:
return self.create_curves_from_mesh(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
elif isinstance(self.settings["geometry"], bpy.types.Curve):
import blenderbim.tool as tool
geom_data = self.settings["geometry"]
selected_objects = bpy.context.selected_objects
active_object = bpy.context.active_object
if isinstance(geom_data, bpy.types.Mesh):
if self.is_mesh_curve_consequtive(geom_data):
if self.file.schema == "IFC2X3":
return self.create_curves_from_mesh_ifc2x3(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
else:
return self.create_curves_from_mesh(should_exclude_faces=should_exclude_faces, is_2d=is_2d)
# create dummy object that will have more detailed curves
# since now we do not really support splines curves natively
obj = self.settings["blender_object"]
dummy = bpy.data.objects.new("Dummy", obj.data.copy())
bpy.context.scene.collection.objects.link(dummy)
tool.Blender.select_and_activate_single_object(bpy.context, dummy)
import blenderbim.tool as tool
selected_objects = bpy.context.selected_objects
active_object = bpy.context.active_object
# create dummy object that will have more detailed curves
# since now we do not really support splines curves natively
obj = self.settings["blender_object"]
dummy = bpy.data.objects.new("Dummy", obj.data.copy())
bpy.context.scene.collection.objects.link(dummy)
tool.Blender.select_and_activate_single_object(bpy.context, dummy)
if not isinstance(geom_data, bpy.types.Mesh):
bpy.ops.object.convert(target="MESH")
bpy.ops.object.convert(target="CURVE")
self.remove_doubles_from_mesh(dummy.data)
bpy.ops.object.convert(target="CURVE")
if self.file.schema == "IFC2X3":
curves = self.create_curves_from_curve_ifc2x3(is_2d=is_2d, curve_object_data=dummy.data)
else:
curves = self.create_curves_from_curve(is_2d=is_2d, curve_object_data=dummy.data)
if self.file.schema == "IFC2X3":
curves = self.create_curves_from_curve_ifc2x3(is_2d=is_2d, curve_object_data=dummy.data)
else:
curves = self.create_curves_from_curve(is_2d=is_2d, curve_object_data=dummy.data)
# restore objects selection
bpy.data.objects.remove(dummy)
tool.Blender.set_objects_selection(bpy.context, active_object, selected_objects)
return curves
# restore objects selection
bpy.data.objects.remove(dummy)
tool.Blender.set_objects_selection(bpy.context, active_object, selected_objects)
return curves
def create_curves_from_mesh(self, should_exclude_faces=False, is_2d=False):
geom_data = self.settings["geometry"].copy()
self.remove_doubles_from_mesh(geom_data)
curves = []
points = self.create_cartesian_point_list_from_vertices(self.settings["geometry"].vertices, is_2d=is_2d)
points = self.create_cartesian_point_list_from_vertices(geom_data.vertices, is_2d=is_2d)
edge_loops = []
previous_edge = None
edge_loop = []
face_edges = set()
if should_exclude_faces:
[face_edges.union([self.settings["geometry"].edge_keys.index(ek) for ek in p.edge_keys]) for p in self.settings["geometry"].polygons]
for i, edge in enumerate(self.settings["geometry"].edges):
[face_edges.union([geom_data.edge_keys.index(ek) for ek in p.edge_keys]) for p in geom_data.polygons]
for i, edge in enumerate(geom_data.edges):
if should_exclude_faces and i in face_edges:
continue
if (Vector(points.CoordList[edge.vertices[0]]) - Vector(points.CoordList[edge.vertices[1]])).length < 0.001:
# Maybe we should warn the user to weld vertices in this scenario?
continue
elif previous_edge is None:
edge_loop = [self.file.createIfcLineIndex((edge.vertices[0] + 1, edge.vertices[1] + 1))]
elif edge.vertices[0] == previous_edge.vertices[1]:
@@ -421,11 +469,19 @@ class Usecase:
curves.append(self.file.createIfcIndexedPolyCurve(points, edge_loop))
return curves
def remove_doubles_from_mesh(self, mesh):
import blenderbim.tool as tool
bm = tool.Blender.get_bmesh_for_mesh(mesh)
bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
tool.Blender.apply_bmesh(mesh, bm)
def create_curves_from_mesh_ifc2x3(self, should_exclude_faces=False, is_2d=False):
geom_data = self.settings["geometry"].copy()
self.remove_doubles_from_mesh(geom_data)
curves = []
points = [
self.create_cartesian_point(v.co.x, v.co.y, v.co.z if not is_2d else None)
for v in self.settings["geometry"].vertices
for v in geom_data.vertices
]
coord_list = [p.Coordinates for p in points]
edge_loops = []
@@ -433,13 +489,10 @@ class Usecase:
edge_loop = []
face_edges = set()
if should_exclude_faces:
[face_edges.union([self.settings["geometry"].edge_keys.index(ek) for ek in p.edge_keys]) for p in self.settings["geometry"].polygons]
for i, edge in enumerate(self.settings["geometry"].edges):
[face_edges.union([geom_data.edge_keys.index(ek) for ek in p.edge_keys]) for p in geom_data.polygons]
for i, edge in enumerate(geom_data.edges):
if should_exclude_faces and i in face_edges:
continue
if (Vector(coord_list[edge.vertices[0]]) - Vector(coord_list[edge.vertices[1]])).length < 0.001:
# Maybe we should warn the user to weld vertices in this scenario?
continue
elif previous_edge is None:
edge_loop = [edge.vertices]
elif edge.vertices[0] == previous_edge.vertices[1]: