mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-22 22:12:32 +00:00
Fix CAD arc bug where sometimes arc went the wrong way, and now auto-clean verts when creating arc
This commit is contained in:
@@ -299,51 +299,38 @@ class CadArcFrom3Points(bpy.types.Operator):
|
|||||||
mesh = obj.data
|
mesh = obj.data
|
||||||
bm = bmesh.from_edit_mesh(mesh)
|
bm = bmesh.from_edit_mesh(mesh)
|
||||||
|
|
||||||
selected_verts = [v for v in bm.verts if v.select]
|
arc = [v for v in bm.verts if v.select]
|
||||||
if len(selected_verts) != 3:
|
if len(arc) != 3:
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
pts = [v.co for v in selected_verts]
|
elif len([e for e in bm.edges if e.select]) != 2:
|
||||||
center = tool.Cad.get_center_of_arc(pts, obj)
|
|
||||||
if not center:
|
|
||||||
return {"CANCELLED"}
|
return {"CANCELLED"}
|
||||||
|
|
||||||
bpy.context.scene.cursor.location = center
|
sorted_arc = [None, None, None]
|
||||||
if self.only_recalculate_center:
|
for v1 in arc:
|
||||||
return {"FINISHED"}
|
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):
|
bm.verts.index_update()
|
||||||
other_verts = [v for v in selected_verts if v != vert]
|
bm.edges.index_update()
|
||||||
total_vector = mathutils.Vector((0, 0, 0))
|
|
||||||
for other_vert in other_verts:
|
|
||||||
total_vector += other_vert.co - vert.co
|
|
||||||
return total_vector.length
|
|
||||||
|
|
||||||
# For three selected points that represent a start, end, and point on an
|
index_offset = len(bm.verts)
|
||||||
# arc, the start and end verts can be identified by being furthest away
|
new_verts = [bm.verts.new(v) for v in verts]
|
||||||
# from the other 2 points.
|
new_edges = [bm.edges.new((new_verts[i], new_verts[i + 1])) for i in range(len(new_verts) - 1)]
|
||||||
arc_end_verts = sorted(selected_verts, key=get_distance_to_other_points)[-2:]
|
|
||||||
normal = mathutils.geometry.normal(pts)
|
|
||||||
|
|
||||||
dir1 = (arc_end_verts[0].co - center).normalized()
|
bm.verts.remove(sorted_arc[1])
|
||||||
dir2 = (arc_end_verts[1].co - center).normalized()
|
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)
|
bmesh.update_edit_mesh(mesh)
|
||||||
mesh.update()
|
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -913,7 +913,7 @@ class DecorationsHandler:
|
|||||||
centroid = tool.Cad.get_center_of_arc(points)
|
centroid = tool.Cad.get_center_of_arc(points)
|
||||||
if centroid:
|
if centroid:
|
||||||
arc_centroids.append(tuple(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})
|
batch = batch_for_shader(self.shader, "POINTS", {"pos": arc_centroids})
|
||||||
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
|
self.shader.uniform_float("color", (0.2, 0.2, 0.2, 1))
|
||||||
@@ -987,56 +987,3 @@ class DecorationsHandler:
|
|||||||
listEdg.append((Vertices - 1, 0))
|
listEdg.append((Vertices - 1, 0))
|
||||||
|
|
||||||
return points, listEdg
|
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
|
|
||||||
|
|||||||
@@ -33,9 +33,8 @@ import sys
|
|||||||
import bpy
|
import bpy
|
||||||
import math
|
import math
|
||||||
import bmesh
|
import bmesh
|
||||||
|
import mathutils.geometry
|
||||||
from mathutils import Vector, Matrix, 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
|
VTX_PRECISION = 1.0e-5
|
||||||
@@ -49,7 +48,7 @@ class Cad:
|
|||||||
> edge: tuple of 2 vectors
|
> edge: tuple of 2 vectors
|
||||||
< returns: True / False if a point happens to lie on an edge
|
< 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
|
on_line = (pt - p).length < VTX_PRECISION
|
||||||
return on_line and (0.0 <= _percent <= 1.0)
|
return on_line and (0.0 <= _percent <= 1.0)
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ class Cad:
|
|||||||
> edge: tuple of 2 vectors
|
> edge: tuple of 2 vectors
|
||||||
< returns: a vector of the closest point on that edge
|
< 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
|
@classmethod
|
||||||
def edge_percent(cls, p, edge):
|
def edge_percent(cls, p, edge):
|
||||||
@@ -68,7 +67,7 @@ class Cad:
|
|||||||
> takes a point, and a edge as a tuple of 2 vectors
|
> 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
|
< 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
|
@classmethod
|
||||||
def angle_edges(cls, edge1, edge2, degrees=False, signed=False):
|
def angle_edges(cls, edge1, edge2, degrees=False, signed=False):
|
||||||
@@ -105,7 +104,7 @@ class Cad:
|
|||||||
< returns output of intersect_line_line
|
< returns output of intersect_line_line
|
||||||
"""
|
"""
|
||||||
[p1, p2], [p3, p4] = edge1, edge2
|
[p1, p2], [p3, p4] = edge1, edge2
|
||||||
return LineIntersect(p1, p2, p3, p4)
|
return mathutils.geometry.intersect_line_line(p1, p2, p3, p4)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_intersection(cls, edge1, edge2):
|
def get_intersection(cls, edge1, edge2):
|
||||||
@@ -380,3 +379,57 @@ class Cad:
|
|||||||
return cp
|
return cp
|
||||||
else:
|
else:
|
||||||
print("not on a circle")
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user