diff --git a/src/blenderbim/blenderbim/bim/module/cad/operator.py b/src/blenderbim/blenderbim/bim/module/cad/operator.py index 53a8ae60d9..f57114ba75 100644 --- a/src/blenderbim/blenderbim/bim/module/cad/operator.py +++ b/src/blenderbim/blenderbim/bim/module/cad/operator.py @@ -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"} diff --git a/src/blenderbim/blenderbim/bim/module/model/slab.py b/src/blenderbim/blenderbim/bim/module/model/slab.py index 127a736ea1..205d0ac717 100644 --- a/src/blenderbim/blenderbim/bim/module/model/slab.py +++ b/src/blenderbim/blenderbim/bim/module/model/slab.py @@ -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 diff --git a/src/blenderbim/blenderbim/tool/cad.py b/src/blenderbim/blenderbim/tool/cad.py index 7b19d32f7f..682bf174d0 100644 --- a/src/blenderbim/blenderbim/tool/cad.py +++ b/src/blenderbim/blenderbim/tool/cad.py @@ -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