Fix CAD arc bug where sometimes arc went the wrong way, and now auto-clean verts when creating arc

This commit is contained in:
Dion Moult
2022-09-28 11:02:17 +10:00
parent 2e5ce6b862
commit 1c0fa13f39
3 changed files with 83 additions and 96 deletions
@@ -299,51 +299,38 @@ class CadArcFrom3Points(bpy.types.Operator):
mesh = obj.data
bm = bmesh.from_edit_mesh(mesh)
selected_verts = [v for v in bm.verts if v.select]
if len(selected_verts) != 3:
arc = [v for v in bm.verts if v.select]
if len(arc) != 3:
return {"CANCELLED"}
pts = [v.co for v in selected_verts]
center = tool.Cad.get_center_of_arc(pts, obj)
if not center:
elif len([e for e in bm.edges if e.select]) != 2:
return {"CANCELLED"}
bpy.context.scene.cursor.location = center
if self.only_recalculate_center:
return {"FINISHED"}
sorted_arc = [None, None, None]
for v1 in arc:
connections = 0
for link_edge in v1.link_edges:
v2 = link_edge.other_vert(v1)
if v2 in arc:
connections += 1
if connections == 2: # Midpoint
sorted_arc[1] = v1
else:
sorted_arc[2 if sorted_arc[2] is None else 0] = v1
center = obj.matrix_world.inverted() @ center
points = [tuple(v.co) for v in sorted_arc]
verts, _ = tool.Cad.create_arc_segments(points, num_verts=(self.resolution * 4) + 1, make_edges=False)
def get_distance_to_other_points(vert):
other_verts = [v for v in selected_verts if v != vert]
total_vector = mathutils.Vector((0, 0, 0))
for other_vert in other_verts:
total_vector += other_vert.co - vert.co
return total_vector.length
bm.verts.index_update()
bm.edges.index_update()
# For three selected points that represent a start, end, and point on an
# arc, the start and end verts can be identified by being furthest away
# from the other 2 points.
arc_end_verts = sorted(selected_verts, key=get_distance_to_other_points)[-2:]
normal = mathutils.geometry.normal(pts)
index_offset = len(bm.verts)
new_verts = [bm.verts.new(v) for v in verts]
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
dir1 = (arc_end_verts[0].co - center).normalized()
dir2 = (arc_end_verts[1].co - center).normalized()
bm.verts.remove(sorted_arc[1])
bmesh.ops.remove_doubles(bm, verts=new_verts + [sorted_arc[0], sorted_arc[2]], dist=1e-5)
# Let's get the matrix that represents the coordinate system of the arc.
# This matrix allows us to get 2D vectors for calculating the signed arc angle.
z = normal
x = (arc_end_verts[0].co - center).normalized()
y = z.cross(x)
arc_matrix = mathutils.Matrix([x, y, z]).transposed().to_4x4()
dir1 = ((arc_matrix.inverted() @ arc_end_verts[0].co) - (arc_matrix.inverted() @ center)).normalized()
dir2 = ((arc_matrix.inverted() @ arc_end_verts[1].co) - (arc_matrix.inverted() @ center)).normalized()
angle = dir1.xy.angle_signed(dir2.xy)
v = arc_end_verts[0]
bmesh.ops.spin(bm, geom=[v], axis=normal, cent=center, steps=self.resolution * 4, angle=-angle)
bmesh.update_edit_mesh(mesh)
mesh.update()
return {"FINISHED"}
@@ -913,7 +913,7 @@ class DecorationsHandler:
centroid = tool.Cad.get_center_of_arc(points)
if centroid:
arc_centroids.append(tuple(centroid))
arc_segments.append(self.create_arc_segments(pts=points, num_verts=17, make_edges=True))
arc_segments.append(tool.Cad.create_arc_segments(pts=points, num_verts=17, make_edges=True))
batch = batch_for_shader(self.shader, "POINTS", {"pos": arc_centroids})
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
@@ -987,56 +987,3 @@ class DecorationsHandler:
listEdg.append((Vertices - 1, 0))
return points, listEdg
# https://github.com/nortikin/sverchok/blob/master/nodes/generator/basic_3pt_arc.py
# This function is taken from Sverchok's generate_3PT_mode_1 function, licensed under GPL v2-or-later.
# No functional modifications have been made.
def create_arc_segments(self, pts=None, num_verts=20, make_edges=False):
"""
Arc from [start - through - end]
- call this function only if you have 3 pts,
- do your error checking before passing to it.
"""
num_verts -= 1
verts, edges = [], []
V = Vector
# construction
v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
edge1_mid = v1.lerp(v2, 0.5)
edge2_mid = v3.lerp(v4, 0.5)
axis = mathutils.geometry.normal(v1, v2, v4)
mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis)
# triangle edges
v1_ = ((v1 - edge1_mid) @ mat_rot) + edge1_mid
v2_ = ((v2 - edge1_mid) @ mat_rot) + edge1_mid
v3_ = ((v3 - edge2_mid) @ mat_rot) + edge2_mid
v4_ = ((v4 - edge2_mid) @ mat_rot) + edge2_mid
r = mathutils.geometry.intersect_line_line(v1_, v2_, v3_, v4_)
if r:
# do arc
p1, _ = r
# find arc angle.
a = (v1 - p1).angle((v4 - p1), 0)
s = (2 * math.pi) - a
interior_angle = (v1 - v2).angle(v4 - v3, 0)
if interior_angle > 0.5 * math.pi:
s = math.pi + 2 * (0.5 * math.pi - interior_angle)
for i in range(num_verts + 1):
mat_rot = mathutils.Matrix.Rotation(((s / num_verts) * i), 4, axis)
vec = ((v4 - p1) @ mat_rot) + p1
verts.append(vec[:])
else:
# do straight line
step_size = 1 / num_verts
verts = [v1_.lerp(v4_, i * step_size)[:] for i in range(num_verts + 1)]
if make_edges:
edges = [(n, n + 1) for n in range(len(verts) - 1)]
return verts, edges
+59 -6
View File
@@ -33,9 +33,8 @@ import sys
import bpy
import math
import bmesh
import mathutils.geometry
from mathutils import Vector, Matrix, geometry
from mathutils.geometry import intersect_line_line as LineIntersect
from mathutils.geometry import intersect_point_line as PtLineIntersect
VTX_PRECISION = 1.0e-5
@@ -49,7 +48,7 @@ class Cad:
> edge: tuple of 2 vectors
< returns: True / False if a point happens to lie on an edge
"""
pt, _percent = PtLineIntersect(p, *edge)
pt, _percent = mathutils.geometry.intersect_point_line(p, *edge)
on_line = (pt - p).length < VTX_PRECISION
return on_line and (0.0 <= _percent <= 1.0)
@@ -60,7 +59,7 @@ class Cad:
> edge: tuple of 2 vectors
< returns: a vector of the closest point on that edge
"""
return PtLineIntersect(p, *edge)[0]
return mathutils.geometry.intersect_point_line(p, *edge)[0]
@classmethod
def edge_percent(cls, p, edge):
@@ -68,7 +67,7 @@ class Cad:
> takes a point, and a edge as a tuple of 2 vectors
< returns the percentage between 0 and 1 of where the point lies on the edge
"""
return PtLineIntersect(p, *edge)[1]
return mathutils.geometry.intersect_point_line(p, *edge)[1]
@classmethod
def angle_edges(cls, edge1, edge2, degrees=False, signed=False):
@@ -105,7 +104,7 @@ class Cad:
< returns output of intersect_line_line
"""
[p1, p2], [p3, p4] = edge1, edge2
return LineIntersect(p1, p2, p3, p4)
return mathutils.geometry.intersect_line_line(p1, p2, p3, p4)
@classmethod
def get_intersection(cls, edge1, edge2):
@@ -380,3 +379,57 @@ class Cad:
return cp
else:
print("not on a circle")
# https://github.com/nortikin/sverchok/blob/master/nodes/generator/basic_3pt_arc.py
# This function is taken from Sverchok's generate_3PT_mode_1 function, licensed under GPL v2-or-later.
# No functional modifications have been made.
@classmethod
def create_arc_segments(cls, pts=None, num_verts=20, make_edges=False):
"""
Arc from [start - through - end]
- call this function only if you have 3 pts,
- do your error checking before passing to it.
"""
num_verts -= 1
verts, edges = [], []
V = Vector
# construction
v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2])
edge1_mid = v1.lerp(v2, 0.5)
edge2_mid = v3.lerp(v4, 0.5)
axis = mathutils.geometry.normal(v1, v2, v4)
mat_rot = Matrix.Rotation(math.radians(90.0), 4, axis)
# triangle edges
v1_ = ((v1 - edge1_mid) @ mat_rot) + edge1_mid
v2_ = ((v2 - edge1_mid) @ mat_rot) + edge1_mid
v3_ = ((v3 - edge2_mid) @ mat_rot) + edge2_mid
v4_ = ((v4 - edge2_mid) @ mat_rot) + edge2_mid
r = mathutils.geometry.intersect_line_line(v1_, v2_, v3_, v4_)
if r:
# do arc
p1, _ = r
# find arc angle.
a = (v1 - p1).angle((v4 - p1), 0)
s = (2 * math.pi) - a
interior_angle = (v1 - v2).angle(v4 - v3, 0)
if interior_angle > 0.5 * math.pi:
s = math.pi + 2 * (0.5 * math.pi - interior_angle)
for i in range(num_verts + 1):
mat_rot = Matrix.Rotation(((s / num_verts) * i), 4, axis)
vec = ((v4 - p1) @ mat_rot) + p1
verts.append(vec[:])
else:
# do straight line
step_size = 1 / num_verts
verts = [v1_.lerp(v4_, i * step_size)[:] for i in range(num_verts + 1)]
if make_edges:
edges = [(n, n + 1) for n in range(len(verts) - 1)]
return verts, edges