mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-06 07:51:47 +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
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user