1 # SPDX-License-Identifier: GPL-2.0-or-later
9 from mathutils
import geometry
10 from mathutils
import Vector
13 def generate_bmesh_repr(p1
, v1
, axis
, num_verts
):
15 p1: center of circle (local coordinates)
16 v1: first vertex of circle in (local coordinates)
17 axis: orientation matrix
20 props
= bpy
.context
.scene
.tinycad_props
21 rescale
= props
.rescale
23 # generate geometry up front
25 gamma
= 2 * math
.pi
/ num_verts
26 for i
in range(num_verts
+ 1):
28 mat_rot
= mathutils
.Matrix
.Rotation(theta
, 4, axis
)
29 local_point
= (mat_rot
@ ((v1
- p1
) * rescale
))
30 chain
.append(local_point
+ p1
)
32 obj
= bpy
.context
.edit_object
34 bm
= bmesh
.from_edit_mesh(me
)
40 v
.select
= False # this might be a default.. redundant?
43 # join verts, daisy chain
44 num_verts
= len(v_refs
)
45 for i
in range(num_verts
):
47 idx2
= (i
+ 1) % num_verts
48 bm
.edges
.new([v_refs
[idx1
], v_refs
[idx2
]])
50 bmesh
.update_edit_mesh(me
, loop_triangles
=True)
53 def generate_3PT(pts
, obj
, nv
, mode
=1):
59 v1
, v2
, v3
, v4
= V(pts
[0]), V(pts
[1]), V(pts
[1]), V(pts
[2])
60 edge1_mid
= v1
.lerp(v2
, 0.5)
61 edge2_mid
= v3
.lerp(v4
, 0.5)
62 axis
= geometry
.normal(v1
, v2
, v4
)
63 mat_rot
= mathutils
.Matrix
.Rotation(math
.radians(90.0), 4, axis
)
66 v1_
= ((v1
- edge1_mid
) @ mat_rot
) + edge1_mid
67 v2_
= ((v2
- edge1_mid
) @ mat_rot
) + edge1_mid
68 v3_
= ((v3
- edge2_mid
) @ mat_rot
) + edge2_mid
69 v4_
= ((v4
- edge2_mid
) @ mat_rot
) + edge2_mid
71 r
= geometry
.intersect_line_line(v1_
, v2_
, v3_
, v4_
)
75 bpy
.context
.scene
.cursor
.location
= cp
81 generate_bmesh_repr(p1
, v1
, axis
, nv
)
84 print('not on a circle')
87 def get_three_verts_from_selection(obj
):
89 bm
= bmesh
.from_edit_mesh(me
)
91 bm
.verts
.ensure_lookup_table()
92 bm
.edges
.ensure_lookup_table()
94 return [v
.co
[:] for v
in bm
.verts
if v
.select
]
97 def dispatch(context
, mode
=0):
99 obj
= context
.edit_object
100 pts
= get_three_verts_from_selection(obj
)
101 props
= context
.scene
.tinycad_props
102 generate_3PT(pts
, obj
, props
.num_verts
, mode
)
104 print('dispatch failed', mode
)
107 class TCCallBackCCEN(bpy
.types
.Operator
):
108 bl_idname
= 'tinycad.reset_circlescale'
109 bl_label
= 'CCEN circle reset'
110 bl_options
= {'REGISTER'}
112 def execute(self
, context
):
113 context
.scene
.tinycad_props
.rescale
= 1
117 class TCCircleCenter(bpy
.types
.Operator
):
118 '''Recreate a Circle from 3 selected verts, move 3dcursor to its center'''
120 bl_idname
= 'tinycad.circlecenter'
121 bl_label
= 'CCEN circle center from selected'
122 bl_options
= {'REGISTER', 'UNDO'}
124 def draw(self
, context
):
129 col
.prop(scn
.tinycad_props
, 'num_verts', text
='num verts')
130 row
= col
.row(align
=True)
131 row
.prop(scn
.tinycad_props
, 'rescale', text
='rescale')
132 row
.operator('tinycad.reset_circlescale', text
="", icon
="PIVOT_CURSOR")
135 def poll(cls
, context
):
136 obj
= context
.edit_object
137 return obj
is not None and obj
.type == 'MESH'
139 def execute(self
, context
):
140 dispatch(context
, mode
=1)