#1153 New arc from 3 points command for 2D CAD

This commit is contained in:
Dion Moult
2022-05-01 17:25:03 +10:00
parent d80e26e4ab
commit 02903c4929
4 changed files with 118 additions and 4 deletions
@@ -21,6 +21,7 @@ from . import operator, workspace
classes = (
operator.CadArcFrom2Points,
operator.CadArcFrom3Points,
operator.CadFillet,
operator.CadMitre,
operator.CadTrimExtend,
@@ -140,8 +140,9 @@ class CadFillet(bpy.types.Operator):
layout.prop(self, prop)
def execute(self, context):
if bpy.context.mode != "EDIT_MESH":
return {"CANCELLED"}
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
obj = bpy.context.active_object
cursor = obj.matrix_world.inverted() @ bpy.context.scene.cursor.location
mesh = obj.data
@@ -205,6 +206,7 @@ class CadFillet(bpy.types.Operator):
bmesh.update_edit_mesh(mesh)
mesh.update()
bm.free()
return {"FINISHED"}
@@ -212,7 +214,6 @@ class CadArcFrom2Points(bpy.types.Operator):
bl_idname = "bim.cad_arc_from_2_points"
bl_label = "CAD Arc from 2 Points"
bl_options = {"REGISTER", "UNDO"}
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
should_flip: bpy.props.BoolProperty(name="Flip", description="Flip arc", default=False)
@@ -254,4 +255,81 @@ class CadArcFrom2Points(bpy.types.Operator):
bmesh.ops.spin(bm, geom=[v], axis=axis, cent=cursor, steps=self.resolution * 4, angle=-angle)
bmesh.update_edit_mesh(mesh)
mesh.update()
bm.free()
return {"FINISHED"}
class CadArcFrom3Points(bpy.types.Operator):
bl_idname = "bim.cad_arc_from_3_points"
bl_label = "CAD Arc from 3 Points"
bl_options = {"REGISTER", "UNDO"}
resolution: bpy.props.IntProperty(name="Arc Resolution", min=1, default=1)
only_recalculate_center: bpy.props.BoolProperty(name="Only Recalculate Center", default=False)
@classmethod
def poll(cls, context):
obj = context.active_object
return obj and obj.type == "MESH" and context.mode == "EDIT_MESH"
def draw(self, context):
layout = self.layout
for prop in self.__class__.__annotations__.keys():
layout.prop(self, prop)
def execute(self, context):
bpy.ops.object.mode_set(mode="OBJECT")
bpy.ops.object.mode_set(mode="EDIT")
if bpy.context.mode != "EDIT_MESH":
return {"CANCELLED"}
obj = bpy.context.edit_object
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:
return {"CANCELLED"}
pts = [v.co for v in selected_verts]
center = tool.Cad.generate_3PT(pts, obj, self.resolution * 4)
if not center:
return {"CANCELLED"}
bpy.context.scene.cursor.location = center
if self.only_recalculate_center:
bm.free()
return {"FINISHED"}
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
# 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)
dir1 = (arc_end_verts[0].co - center).normalized()
dir2 = (arc_end_verts[1].co - center).normalized()
# 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()
bm.free()
return {"FINISHED"}
@@ -41,6 +41,7 @@ class CadTool(WorkSpaceTool):
# Enable Mesh Tools add-on to get this amazing tool
("mesh.offset_edges", {"type": "O", "value": "PRESS", "shift": True}, {"properties": []}),
("bim.cad_arc_from_2_points", {"type": "C", "value": "PRESS", "shift": True}, {"properties": []}),
("bim.cad_arc_from_3_points", {"type": "V", "value": "PRESS", "shift": True}, {"properties": []}),
)
def draw_settings(context, layout, tool):
@@ -64,6 +65,10 @@ class CadTool(WorkSpaceTool):
row.label(text="", icon="EVENT_SHIFT")
row.label(text="2-Point Arc", icon="EVENT_C")
row = layout.row(align=True)
row.label(text="", icon="EVENT_SHIFT")
row.label(text="3-Point Arc", icon="EVENT_V")
class CadHotkey(bpy.types.Operator):
bl_idname = "bim.cad_hotkey"
+31 -1
View File
@@ -25,12 +25,15 @@
#
# - Instead of AutoVTX, user explicitly chooses V, T, or X mode
# - Instead of adding edges, existing edges are extended
# - An arc is reconstructed from 3 points instead of a full circle
# - You can now derive the center from an arc without generating geometry
import sys
import bpy
import math
import bmesh
from mathutils import Vector, 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
@@ -298,3 +301,30 @@ class Cad:
elif mode == "V":
bm = cls.perform_v(bm, point, edges[0], edges[1], (p1, p2, p3, p4), vertex_indices)
return bm
@classmethod
def generate_3PT(cls, pts, obj, nv, mode=1):
mw = obj.matrix_world
V = Vector
nv = max(3, nv)
# 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 = 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 = geometry.intersect_line_line(v1_, v2_, v3_, v4_)
if r:
p1, _ = r
cp = mw @ p1
return cp
else:
print("not on a circle")