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 3ba7ecdb95
commit 52a8fcd86e
@@ -362,52 +362,100 @@ class Usecase:
results.append(self.file.createIfcSweptDiskSolid(curve, radius)) results.append(self.file.createIfcSweptDiskSolid(curve, radius))
return results 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): def create_curves(self, should_exclude_faces=False, is_2d=False):
if isinstance(self.settings["geometry"], bpy.types.Mesh): geom_data = self.settings["geometry"]
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
selected_objects = bpy.context.selected_objects if isinstance(geom_data, bpy.types.Mesh):
active_object = bpy.context.active_object 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 import blenderbim.tool as tool
# since now we do not really support splines curves natively
obj = self.settings["blender_object"] selected_objects = bpy.context.selected_objects
dummy = bpy.data.objects.new("Dummy", obj.data.copy()) active_object = bpy.context.active_object
bpy.context.scene.collection.objects.link(dummy)
tool.Blender.select_and_activate_single_object(bpy.context, dummy) # 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="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": if self.file.schema == "IFC2X3":
curves = self.create_curves_from_curve_ifc2x3(is_2d=is_2d, curve_object_data=dummy.data) curves = self.create_curves_from_curve_ifc2x3(is_2d=is_2d, curve_object_data=dummy.data)
else: else:
curves = self.create_curves_from_curve(is_2d=is_2d, curve_object_data=dummy.data) curves = self.create_curves_from_curve(is_2d=is_2d, curve_object_data=dummy.data)
# restore objects selection # restore objects selection
bpy.data.objects.remove(dummy) bpy.data.objects.remove(dummy)
tool.Blender.set_objects_selection(bpy.context, active_object, selected_objects) tool.Blender.set_objects_selection(bpy.context, active_object, selected_objects)
return curves return curves
def create_curves_from_mesh(self, should_exclude_faces=False, is_2d=False): 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 = [] 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 = [] edge_loops = []
previous_edge = None previous_edge = None
edge_loop = [] edge_loop = []
face_edges = set() face_edges = set()
if should_exclude_faces: 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] [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(self.settings["geometry"].edges): for i, edge in enumerate(geom_data.edges):
if should_exclude_faces and i in face_edges: if should_exclude_faces and i in face_edges:
continue 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: elif previous_edge is None:
edge_loop = [self.file.createIfcLineIndex((edge.vertices[0] + 1, edge.vertices[1] + 1))] edge_loop = [self.file.createIfcLineIndex((edge.vertices[0] + 1, edge.vertices[1] + 1))]
elif edge.vertices[0] == previous_edge.vertices[1]: elif edge.vertices[0] == previous_edge.vertices[1]:
@@ -421,11 +469,19 @@ class Usecase:
curves.append(self.file.createIfcIndexedPolyCurve(points, edge_loop)) curves.append(self.file.createIfcIndexedPolyCurve(points, edge_loop))
return curves 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): 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 = [] curves = []
points = [ points = [
self.create_cartesian_point(v.co.x, v.co.y, v.co.z if not is_2d else None) 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] coord_list = [p.Coordinates for p in points]
edge_loops = [] edge_loops = []
@@ -433,13 +489,10 @@ class Usecase:
edge_loop = [] edge_loop = []
face_edges = set() face_edges = set()
if should_exclude_faces: 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] [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(self.settings["geometry"].edges): for i, edge in enumerate(geom_data.edges):
if should_exclude_faces and i in face_edges: if should_exclude_faces and i in face_edges:
continue 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: elif previous_edge is None:
edge_loop = [edge.vertices] edge_loop = [edge.vertices]
elif edge.vertices[0] == previous_edge.vertices[1]: elif edge.vertices[0] == previous_edge.vertices[1]: