1 # SPDX-FileCopyrightText: 2016-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
11 from mathutils
import geometry
12 from mathutils
import Vector
15 def generate_bmesh_repr(p1
, v1
, axis
, num_verts
):
17 p1: center of circle (local coordinates)
18 v1: first vertex of circle in (local coordinates)
19 axis: orientation matrix
22 props
= bpy
.context
.scene
.tinycad_props
23 rescale
= props
.rescale
25 # generate geometry up front
27 gamma
= 2 * math
.pi
/ num_verts
28 for i
in range(num_verts
+ 1):
30 mat_rot
= mathutils
.Matrix
.Rotation(theta
, 4, axis
)
31 local_point
= (mat_rot
@ ((v1
- p1
) * rescale
))
32 chain
.append(local_point
+ p1
)
34 obj
= bpy
.context
.edit_object
36 bm
= bmesh
.from_edit_mesh(me
)
42 v
.select
= False # this might be a default.. redundant?
45 # join verts, daisy chain
46 num_verts
= len(v_refs
)
47 for i
in range(num_verts
):
49 idx2
= (i
+ 1) % num_verts
50 bm
.edges
.new([v_refs
[idx1
], v_refs
[idx2
]])
52 bmesh
.update_edit_mesh(me
, loop_triangles
=True)
55 def generate_3PT(pts
, obj
, nv
, mode
=1):
61 v1
, v2
, v3
, v4
= V(pts
[0]), V(pts
[1]), V(pts
[1]), V(pts
[2])
62 edge1_mid
= v1
.lerp(v2
, 0.5)
63 edge2_mid
= v3
.lerp(v4
, 0.5)
64 axis
= geometry
.normal(v1
, v2
, v4
)
65 mat_rot
= mathutils
.Matrix
.Rotation(math
.radians(90.0), 4, axis
)
68 v1_
= ((v1
- edge1_mid
) @ mat_rot
) + edge1_mid
69 v2_
= ((v2
- edge1_mid
) @ mat_rot
) + edge1_mid
70 v3_
= ((v3
- edge2_mid
) @ mat_rot
) + edge2_mid
71 v4_
= ((v4
- edge2_mid
) @ mat_rot
) + edge2_mid
73 r
= geometry
.intersect_line_line(v1_
, v2_
, v3_
, v4_
)
77 bpy
.context
.scene
.cursor
.location
= cp
83 generate_bmesh_repr(p1
, v1
, axis
, nv
)
86 print('not on a circle')
89 def get_three_verts_from_selection(obj
):
91 bm
= bmesh
.from_edit_mesh(me
)
93 bm
.verts
.ensure_lookup_table()
94 bm
.edges
.ensure_lookup_table()
96 return [v
.co
[:] for v
in bm
.verts
if v
.select
]
99 def dispatch(context
, mode
=0):
101 obj
= context
.edit_object
102 pts
= get_three_verts_from_selection(obj
)
103 props
= context
.scene
.tinycad_props
104 generate_3PT(pts
, obj
, props
.num_verts
, mode
)
106 print('dispatch failed', mode
)
109 class TCCallBackCCEN(bpy
.types
.Operator
):
110 bl_idname
= 'tinycad.reset_circlescale'
111 bl_label
= 'CCEN circle reset'
112 bl_options
= {'REGISTER'}
114 def execute(self
, context
):
115 context
.scene
.tinycad_props
.rescale
= 1
119 class TCCircleCenter(bpy
.types
.Operator
):
120 '''Recreate a Circle from 3 selected verts, move 3dcursor to its center'''
122 bl_idname
= 'tinycad.circlecenter'
123 bl_label
= 'CCEN circle center from selected'
124 bl_options
= {'REGISTER', 'UNDO'}
126 def draw(self
, context
):
131 col
.prop(scn
.tinycad_props
, 'num_verts', text
='num verts')
132 row
= col
.row(align
=True)
133 row
.prop(scn
.tinycad_props
, 'rescale', text
='rescale')
134 row
.operator('tinycad.reset_circlescale', text
="", icon
="PIVOT_CURSOR")
137 def poll(cls
, context
):
138 obj
= context
.edit_object
139 return obj
is not None and obj
.type == 'MESH'
141 def execute(self
, context
):
142 dispatch(context
, mode
=1)