Cleanup: strip trailing space, remove BOM
[blender-addons.git] / precision_drawing_tools / pdt_tangent.py
blob22b34c7a2a3efddf11766543f7c29c55773e6bd7
1 # ***** BEGIN GPL LICENSE BLOCK *****
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ***** END GPL LICENCE BLOCK *****
20 # -----------------------------------------------------------------------
21 # Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
22 # -----------------------------------------------------------------------
24 import bpy
25 import bmesh
26 from math import sqrt, floor, asin, sin, cos, pi
27 from mathutils import Vector
28 from bpy.types import Operator
30 from .pdt_functions import (
31 oops,
32 arc_centre,
33 set_mode,
34 view_coords,
35 view_coords_i,
38 from .pdt_msg_strings import (
39 PDT_OBJ_MODE_ERROR,
40 PDT_ERR_NO_ACT_OBJ,
41 PDT_ERR_SEL_3_VERTS,
42 PDT_ERR_SEL_1_VERT,
43 PDT_ERR_BADDISTANCE,
44 PDT_ERR_MATHSERROR,
45 PDT_ERR_SAMERADII,
46 PDT_ERR_VERT_MODE,
49 from . import pdt_exception
51 PDT_ObjectModeError = pdt_exception.ObjectModeError
52 PDT_SelectionError = pdt_exception.SelectionError
55 def get_tangent_intersect_outer(hloc_0, vloc_0, hloc_1, vloc_1, radius_0, radius_1):
56 """Return Location in 2 Dimensions of the Intersect Point for Outer Tangents.
58 Args:
59 hloc_0: Horizontal Coordinate of Centre of First Arc
60 vloc_0: Vertical Coordinate of Centre of First Arc
61 hloc_1: Horizontal Coordinate of Centre of Second Arc
62 vloc_1: Vertical Coordinate of Centre of Second Arc
63 radius_0: Radius of First Arc
64 radius_1: Radius of Second Arc
66 Returns:
67 hloc_p: Horizontal Coordinate of Centre of Intersection
68 vloc_p: Vertical Coordinate of Centre of Intersection.
69 """
71 hloc_p = ((hloc_1 * radius_0) - (hloc_0 * radius_1)) / (radius_0 - radius_1)
72 vloc_p = ((vloc_1 * radius_0) - (vloc_0 * radius_1)) / (radius_0 - radius_1)
74 return hloc_p, vloc_p
77 def get_tangent_intersect_inner(hloc_0, vloc_0, hloc_1, vloc_1, radius_0, radius_1):
78 """Return Location in 2 Dimensions of the Intersect Point for Inner Tangents.
80 Args:
81 hloc_0: Horizontal Coordinate of Centre of First Arc
82 vloc_0: Vertical Coordinate of Centre of First Arc
83 hloc_1: Horizontal Coordinate of Centre of Second Arc
84 vloc_1: Vertical Coordinate of Centre of Second Arc
85 radius_0: Radius of First Arc
86 radius_1: Radius of Second Arc
88 Returns:
89 hloc_p: Horizontal Coordinate of Centre of Intersection
90 vloc_p: Vertical Coordinate of Centre of Intersection.
91 """
93 hloc_p = ((hloc_1 * radius_0) + (hloc_0 * radius_1)) / (radius_0 + radius_1)
94 vloc_p = ((vloc_1 * radius_0) + (vloc_0 * radius_1)) / (radius_0 + radius_1)
96 return hloc_p, vloc_p
99 def get_tangent_points(context, hloc_0, vloc_0, radius_0, hloc_p, vloc_p):
100 """Return Location in 2 Dimensions of the Tangent Points.
102 Args:
103 context: Blender bpy.context instance
104 hloc_0: Horizontal Coordinate of Centre of First Arc
105 vloc_0: Vertical Coordinate of Centre of First Arc
106 radius_0: Radius of First Arc
107 hloc_p: Horizontal Coordinate of Intersection
108 vloc_p: Vertical Coordinate of Intersection
110 Returns:
111 hloc_t1: Horizontal Location of First Tangent Point
112 hloc_t2: Horizontal Location of Second Tangent Point
113 vloc_t1: Vertical Location of First Tangent Point
114 vloc_t2: Vertical Location of Second Tangent Point
117 # Uses basic Pythagorus' theorem to compute locations
119 numerator = (radius_0 ** 2 * (hloc_p - hloc_0)) + (
120 radius_0
121 * (vloc_p - vloc_0)
122 * sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
124 denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
125 hloc_t1 = round((numerator / denominator) + hloc_0, 5)
127 numerator = (radius_0 ** 2 * (hloc_p - hloc_0)) - (
128 radius_0
129 * (vloc_p - vloc_0)
130 * sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
132 denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
133 hloc_t2 = round((numerator / denominator) + hloc_0, 5)
135 # Get Y values
136 numerator = (radius_0 ** 2 * (vloc_p - vloc_0)) - (
137 radius_0
138 * (hloc_p - hloc_0)
139 * sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
141 denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
142 vloc_t1 = round((numerator / denominator) + vloc_0, 5)
144 numerator = (radius_0 ** 2 * (vloc_p - vloc_0)) + (
145 radius_0
146 * (hloc_p - hloc_0)
147 * sqrt((hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2 - radius_0 ** 2)
149 denominator = (hloc_p - hloc_0) ** 2 + (vloc_p - vloc_0) ** 2
150 vloc_t2 = round((numerator / denominator) + vloc_0, 5)
152 return hloc_t1, hloc_t2, vloc_t1, vloc_t2
155 def make_vectors(coords, a1, a2, a3, pg):
156 """Return Vectors of the Tangent Points.
158 Args:
159 coords: A List of Coordinates in 2D space of the tangent points
160 & a third dimension for the vectors
161 a1: Index of horizontal axis
162 a2: Index of vertical axis
163 a3: Index of depth axis
164 pg: PDT Parameters Group - our variables
166 Returns:
167 tangent_vector_o1: Location of First Tangent Point
168 tangent_vector_o2: Location of Second Tangent Point
169 tangent_vector_o3: Location of First Tangent Point
170 tangent_vector_o4: Location of Second Tangent Point
173 tangent_vector_o1 = Vector((0, 0, 0))
174 tangent_vector_o1[a1] = coords[0]
175 tangent_vector_o1[a2] = coords[1]
176 tangent_vector_o1[a3] = coords[8]
177 tangent_vector_o2 = Vector((0, 0, 0))
178 tangent_vector_o2[a1] = coords[2]
179 tangent_vector_o2[a2] = coords[3]
180 tangent_vector_o2[a3] = coords[8]
181 tangent_vector_o3 = Vector((0, 0, 0))
182 tangent_vector_o3[a1] = coords[4]
183 tangent_vector_o3[a2] = coords[5]
184 tangent_vector_o3[a3] = coords[8]
185 tangent_vector_o4 = Vector((0, 0, 0))
186 tangent_vector_o4[a1] = coords[6]
187 tangent_vector_o4[a2] = coords[7]
188 tangent_vector_o4[a3] = coords[8]
190 if pg.plane == "LO":
191 # Reset coordinates from view local (Horiz, Vert, depth) to World XYZ.
193 tangent_vector_o1 = view_coords(
194 tangent_vector_o1[a1], tangent_vector_o1[a2], tangent_vector_o1[a3]
196 tangent_vector_o2 = view_coords(
197 tangent_vector_o2[a1], tangent_vector_o2[a2], tangent_vector_o2[a3]
199 tangent_vector_o3 = view_coords(
200 tangent_vector_o3[a1], tangent_vector_o3[a2], tangent_vector_o3[a3]
202 tangent_vector_o4 = view_coords(
203 tangent_vector_o4[a1], tangent_vector_o4[a2], tangent_vector_o4[a3]
206 return (tangent_vector_o1, tangent_vector_o2, tangent_vector_o3, tangent_vector_o4)
209 def tangent_setup(context, pg, plane, obj_data, centre_0, centre_1, centre_2, radius_0, radius_1):
210 """This section sets up all the variables required for the tangent functions.
212 Args:
213 context: Blender bpy.context instance
214 pg: PDT Parameter Group of variables
215 plane: Working plane
216 obj_data: All the data of the chosen object
217 centre_0: Centre coordinates of the first arc
218 centre_1: Centre coordinates of the second arc
219 centre_2: Coordinates fo the point
220 radius_0: Radius if the first Arc
221 radius_1: Radius of the second Arc
223 Returns:
224 Status Set.
227 a1, a2, a3 = set_mode(plane)
228 mode = pg.tangent_mode
229 if plane == "LO":
230 # Translate world cordinates into view local (horiz, vert, depth)
232 centre_0 = view_coords_i(centre_0[a1], centre_0[a2], centre_0[a3])
233 centre_1 = view_coords_i(centre_1[a1], centre_1[a2], centre_1[a3])
234 centre_2 = view_coords_i(centre_2[a1], centre_2[a2], centre_2[a3])
235 if pg.tangent_mode == "point":
236 vector_difference = centre_2 - centre_0
237 distance = sqrt(vector_difference[a1] ** 2 + vector_difference[a2] ** 2)
238 else:
239 vector_difference = centre_1 - centre_0
240 distance = sqrt(vector_difference[a1] ** 2 + vector_difference[a2] ** 2)
242 if (
243 (distance <= radius_0 and mode in {"point"}) or
244 (distance <= (radius_0 + radius_1) and mode in {"inner", "both"}) or
245 (distance <= radius_0 or distance <= radius_1 and mode in {"outer", "both"})
247 # Cannot execute, centres are too close.
249 pg.error = f"{PDT_ERR_BADDISTANCE}"
250 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
251 return {"FINISHED"}
253 """This next section will draw Point based Tangents.
255 These are drawn from a point to an Arc
258 if mode == "point":
259 if (
260 (centre_2[a1] - centre_0[a1]) ** 2 + (centre_2[a2] - centre_0[a2]) ** 2 - radius_0 ** 2
261 ) > 0:
262 hloc_to1, hloc_to2, vloc_to1, vloc_to2 = get_tangent_points(
263 context, centre_0[a1], centre_0[a2], radius_0, centre_2[a1], centre_2[a2]
265 else:
266 pg.error = PDT_ERR_MATHSERROR
267 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
268 return {"FINISHED"}
269 # Point Tangents
271 tangent_vector_o1 = Vector((0, 0, 0))
272 tangent_vector_o1[a1] = hloc_to1
273 tangent_vector_o1[a2] = vloc_to1
274 tangent_vector_o1[a3] = centre_2[a3]
275 tangent_vector_o2 = Vector((0, 0, 0))
276 tangent_vector_o2[a1] = hloc_to2
277 tangent_vector_o2[a2] = vloc_to2
278 tangent_vector_o2[a3] = centre_2[a3]
279 if pg.plane == "LO":
280 # Translate view local coordinates (horiz, vert, depth) into World XYZ
282 centre_2 = view_coords(centre_2[a1], centre_2[a2], centre_2[a3])
283 tangent_vector_o1 = view_coords(
284 tangent_vector_o1[a1], tangent_vector_o1[a2], tangent_vector_o1[a3]
286 tangent_vector_o2 = view_coords(
287 tangent_vector_o2[a1], tangent_vector_o2[a2], tangent_vector_o2[a3]
289 tangent_vectors = (centre_2, tangent_vector_o1, tangent_vector_o2)
290 draw_tangents(tangent_vectors, obj_data)
292 return {"FINISHED"}
294 """This next section will draw Arc based Outer Tangents.
296 These are drawn from an Arc to another Arc
299 if mode in {"outer", "both"}:
300 # Uses basic trigonometry and Pythagorus' theorem to compute locations
302 if radius_0 == radius_1:
303 # No intersection point for outer tangents
305 sin_angle = (centre_1[a2] - centre_0[a2]) / distance
306 cos_angle = (centre_1[a1] - centre_0[a1]) / distance
307 hloc_to1 = centre_0[a1] + (radius_0 * sin_angle)
308 hloc_to2 = centre_0[a1] - (radius_0 * sin_angle)
309 hloc_to3 = centre_1[a1] + (radius_0 * sin_angle)
310 hloc_to4 = centre_1[a1] - (radius_0 * sin_angle)
311 vloc_to1 = centre_0[a2] - (radius_0 * cos_angle)
312 vloc_to2 = centre_0[a2] + (radius_0 * cos_angle)
313 vloc_to3 = centre_1[a2] - (radius_0 * cos_angle)
314 vloc_to4 = centre_1[a2] + (radius_0 * cos_angle)
315 else:
316 hloc_po, vloc_po = get_tangent_intersect_outer(
317 centre_0[a1], centre_0[a2], centre_1[a1], centre_1[a2], radius_0, radius_1
320 if ((hloc_po - centre_0[a1]) ** 2 + (vloc_po - centre_0[a2]) ** 2 - radius_0 ** 2) > 0:
321 hloc_to1, hloc_to2, vloc_to1, vloc_to2 = get_tangent_points(
322 context, centre_0[a1], centre_0[a2], radius_0, hloc_po, vloc_po
324 else:
325 pg.error = PDT_ERR_MATHSERROR
326 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
327 return {"FINISHED"}
328 if ((hloc_po - centre_0[a1]) ** 2 + (vloc_po - centre_0[a2]) ** 2 - radius_1 ** 2) > 0:
329 hloc_to3, hloc_to4, vloc_to3, vloc_to4 = get_tangent_points(
330 context, centre_1[a1], centre_1[a2], radius_1, hloc_po, vloc_po
332 else:
333 pg.error = PDT_ERR_MATHSERROR
334 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
335 return {"FINISHED"}
337 dloc_p = centre_0[a3]
338 coords_in = (
339 hloc_to1,
340 vloc_to1,
341 hloc_to2,
342 vloc_to2,
343 hloc_to3,
344 vloc_to3,
345 hloc_to4,
346 vloc_to4,
347 dloc_p,
349 tangent_vectors = make_vectors(coords_in, a1, a2, a3, pg)
350 draw_tangents(tangent_vectors, obj_data)
352 """This next section will draw Arc based Inner Tangents.
354 These are drawn from an Arc to another Arc
357 if mode in {"inner", "both"}:
358 # Uses basic trigonometry and Pythagorus' theorem to compute locations
360 hloc_pi, vloc_pi = get_tangent_intersect_inner(
361 centre_0[a1], centre_0[a2], centre_1[a1], centre_1[a2], radius_0, radius_1
363 if ((hloc_pi - centre_0[a1]) ** 2 + (vloc_pi - centre_0[a2]) ** 2 - radius_0 ** 2) > 0:
364 hloc_to1, hloc_to2, vloc_to1, vloc_to2 = get_tangent_points(
365 context, centre_0[a1], centre_0[a2], radius_0, hloc_pi, vloc_pi
367 else:
368 pg.error = PDT_ERR_MATHSERROR
369 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
370 return {"FINISHED"}
371 if ((hloc_pi - centre_0[a1]) ** 2 + (vloc_pi - centre_0[a2]) ** 2 - radius_0 ** 2) > 0:
372 hloc_to3, hloc_to4, vloc_to3, vloc_to4 = get_tangent_points(
373 context, centre_1[a1], centre_1[a2], radius_1, hloc_pi, vloc_pi
375 else:
376 pg.error = PDT_ERR_MATHSERROR
377 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
378 return {"FINISHED"}
380 dloc_p = centre_0[a3]
381 coords_in = (
382 hloc_to1,
383 vloc_to1,
384 hloc_to2,
385 vloc_to2,
386 hloc_to3,
387 vloc_to3,
388 hloc_to4,
389 vloc_to4,
390 dloc_p,
392 tangent_vectors = make_vectors(coords_in, a1, a2, a3, pg)
393 draw_tangents(tangent_vectors, obj_data)
395 return {"FINISHED"}
398 def draw_tangents(tangent_vectors, obj_data):
399 """Add Edges Representing the Tangents.
401 Note:
402 The length of the tanget_vectors determins whcih tangents will be
403 drawn, 3 gives Point Tangents, 4 gives Inner/Outer tangents
405 Args:
406 tangent_vectors: A list of vectores representing the tangents
407 obj_data: A list giving Object, Object Location and Object Bmesh
409 Returns:
410 Nothing.
412 obj = obj_data[0]
413 obj_loc = obj_data[1]
414 bm = obj_data[2]
415 if len(tangent_vectors) == 3:
416 point_vertex_outer = bm.verts.new(tangent_vectors[0] - obj_loc)
417 tangent_vertex_o1 = bm.verts.new(tangent_vectors[1] - obj_loc)
418 tangent_vertex_o2 = bm.verts.new(tangent_vectors[2] - obj_loc)
419 bm.edges.new([tangent_vertex_o1, point_vertex_outer])
420 bm.edges.new([tangent_vertex_o2, point_vertex_outer])
421 else:
422 tangent_vertex_o1 = bm.verts.new(tangent_vectors[0] - obj_loc)
423 tangent_vertex_o2 = bm.verts.new(tangent_vectors[2] - obj_loc)
424 tangent_vertex_o3 = bm.verts.new(tangent_vectors[1] - obj_loc)
425 tangent_vertex_o4 = bm.verts.new(tangent_vectors[3] - obj_loc)
426 bm.edges.new([tangent_vertex_o1, tangent_vertex_o2])
427 bm.edges.new([tangent_vertex_o3, tangent_vertex_o4])
428 bmesh.update_edit_mesh(obj.data)
431 def analyse_arc(context, pg):
432 """Analyses an Arc inferred from Selected Vertices.
434 Note:
435 Will work if more than 3 vertices are selected, taking the
436 first, the nearest to the middle and the last.
438 Args:
439 context: Blender bpy.context instance
440 pg: PDT Parameters Group - our variables
442 Returns:
443 vector_delta: Location of Arc Centre
444 radius: Radius of Arc.
446 obj = context.view_layer.objects.active
447 if obj is None:
448 pg.error = PDT_ERR_NO_ACT_OBJ
449 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
450 raise PDT_ObjectModeError
451 if obj.mode == "EDIT":
452 obj_loc = obj.matrix_world.decompose()[0]
453 bm = bmesh.from_edit_mesh(obj.data)
454 verts = [v for v in bm.verts if v.select]
455 if len(verts) < 3:
456 pg.error = f"{PDT_ERR_SEL_3_VERTS} {len(verts)})"
457 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
458 raise PDT_SelectionError
459 vector_a = verts[0].co
460 # Get the nearest to middle vertex of the arc
462 vector_b = verts[int(floor(len(verts) / 2))].co
463 vector_c = verts[-1].co
464 vector_delta, radius = arc_centre(vector_a, vector_b, vector_c)
466 return vector_delta, radius
469 class PDT_OT_TangentOperate(Operator):
470 """Calculate Tangents from Inputs."""
472 bl_idname = "pdt.tangentoperate"
473 bl_label = "Calculate Tangents"
474 bl_options = {"REGISTER", "UNDO"}
475 bl_description = "Calculate Tangents to Arcs from Points or Other Arcs"
477 @classmethod
478 def poll(cls, context):
479 ob = context.object
480 if ob is None:
481 return False
482 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
484 def execute(self, context):
485 """Calculate Tangents from Inputs.
487 Note:
488 Uses pg.plane, pg.tangent_point0, pg.tangent_radius0, pg.tangent_point1
489 pg.tangent_radius1, pg.tangent_point2 to place tangents.
491 Analyses distance between arc centres, or arc centre and tangent point
492 to determine which mode is possible (Inner, Outer, or Point). If centres are
493 both contianed within 1 inferred circle, Inner tangents are not possible.
495 Arcs of same radius will have no intersection for outer tangents so these
496 are calculated differently.
498 Args:
499 context: Blender bpy.context instance.
501 Returns:
502 Nothing.
505 scene = context.scene
506 pg = scene.pdt_pg
507 plane = pg.plane
508 # Get Object
509 obj = context.view_layer.objects.active
510 if obj is not None:
511 if obj.mode not in {"EDIT"} or obj.type != "MESH":
512 pg.error = PDT_OBJ_MODE_ERROR
513 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
514 return {"FINISHED"}
515 else:
516 pg.error = PDT_ERR_NO_ACT_OBJ
517 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
518 return {"FINISHED"}
519 bm = bmesh.from_edit_mesh(obj.data)
520 obj_loc = obj.matrix_world.decompose()[0]
521 obj_data = (obj, obj_loc, bm)
523 radius_0 = pg.tangent_radius0
524 radius_1 = pg.tangent_radius1
525 centre_0 = pg.tangent_point0
526 centre_1 = pg.tangent_point1
527 centre_2 = pg.tangent_point2
529 tangent_setup(
530 context, pg, plane, obj_data, centre_0, centre_1, centre_2, radius_0, radius_1
533 return {"FINISHED"}
536 class PDT_OT_TangentOperateSel(Operator):
537 """Calculate Tangents from Selection."""
539 bl_idname = "pdt.tangentoperatesel"
540 bl_label = "Calculate Tangents"
541 bl_options = {"REGISTER", "UNDO"}
542 bl_description = "Calculate Tangents to Arcs from 2 Selected Vertices, or 1 & Point in Menu"
544 @classmethod
545 def poll(cls, context):
546 ob = context.object
547 if ob is None:
548 return False
549 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
551 def execute(self, context):
552 """Calculate Tangents from Selection.
554 Note:
555 Uses pg.plane & 2 or more selected Vertices to place tangents.
556 One vertex must be on each arc.
558 Analyses distance between arc centres, or arc centre and tangent point
559 to determine which mode is possible (Inner, Outer, or Point). If centres are
560 both contianed within 1 inferred circle, Inner tangents are not possible.
562 Arcs of same radius will have no intersection for outer tangents so these
563 are calculated differently.
565 Args:
566 context: Blender bpy.context instance.
568 Returns:
569 Nothing.
572 scene = context.scene
573 pg = scene.pdt_pg
574 plane = pg.plane
575 # Get Object
576 obj = context.view_layer.objects.active
577 if obj is not None:
578 if obj.mode not in {"EDIT"} or obj.type != "MESH":
579 pg.error = PDT_OBJ_MODE_ERROR
580 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
581 return {"FINISHED"}
582 else:
583 pg.error = PDT_ERR_NO_ACT_OBJ
584 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
585 return {"FINISHED"}
586 bm = bmesh.from_edit_mesh(obj.data)
587 obj_loc = obj.matrix_world.decompose()[0]
588 obj_data = (obj, obj_loc, bm)
590 # Get All Values from Selected Vertices
591 verts = [v for v in bm.verts if v.select]
592 if len(verts) <= 0:
593 pg.error = f"{PDT_ERR_SEL_1_VERT} 0"
594 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
595 return {"FINISHED"}
596 v1 = verts[0]
597 vn = verts[-1]
598 for v in bm.verts:
599 v.select_set(False)
600 for e in bm.edges:
601 e.select_set(False)
602 v1.select_set(True)
603 bpy.ops.mesh.select_linked()
604 verts1 = [v for v in bm.verts if v.select].copy()
605 if len(verts1) < 3:
606 pg.error = f"{PDT_ERR_VERT_MODE} or Less than 3 vertices in your Arc(s)"
607 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
608 return {"FINISHED"}
609 for v in bm.verts:
610 v.select_set(False)
611 for e in bm.edges:
612 e.select_set(False)
613 vn.select_set(True)
614 bpy.ops.mesh.select_linked()
615 vertsn = [v for v in bm.verts if v.select].copy()
616 for v in bm.verts:
617 v.select_set(False)
618 for e in bm.edges:
619 e.select_set(False)
620 bmesh.update_edit_mesh(obj.data)
621 bm.select_history.clear()
622 # Select the nearest to middle vertex in the arc
624 verts1 = [verts1[0].co, verts1[int(floor(len(verts1) / 2))].co, verts1[-1].co]
625 vertsn = [vertsn[0].co, vertsn[int(floor(len(vertsn) / 2))].co, vertsn[-1].co]
626 centre_0, radius_0 = arc_centre(verts1[0], verts1[1], verts1[2])
627 centre_1, radius_1 = arc_centre(vertsn[0], vertsn[1], vertsn[2])
628 centre_2 = pg.tangent_point2
630 tangent_setup(
631 context, pg, plane, obj_data, centre_0, centre_1, centre_2, radius_0, radius_1
634 return {"FINISHED"}
637 class PDT_OT_TangentSet1(Operator):
638 """Calculates Centres & Radii from 3 Vectors."""
640 bl_idname = "pdt.tangentset1"
641 bl_label = "Calculate Centres & Radii"
642 bl_options = {"REGISTER", "UNDO"}
643 bl_description = "Calculate Centres & Radii from Selected Vertices"
645 @classmethod
646 def poll(cls, context):
647 ob = context.object
648 if ob is None:
649 return False
650 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
652 def execute(self, context):
653 """Sets Input Tangent Point 1 to analysis of Arc.
655 Args:
656 context: Blender bpy.context instance.
658 Returns:
659 Nothing.
661 scene = context.scene
662 pg = scene.pdt_pg
663 vector_delta, radius = analyse_arc(context, pg)
664 pg.tangent_point0 = vector_delta
665 pg.tangent_radius0 = radius
666 return {"FINISHED"}
669 class PDT_OT_TangentSet2(Operator):
670 """Calculates Centres & Radii from 3 Vectors."""
672 bl_idname = "pdt.tangentset2"
673 bl_label = "Calculate Centres & Radii"
674 bl_options = {"REGISTER", "UNDO"}
675 bl_description = "Calculate Centres & Radii from Selected Vertices"
677 @classmethod
678 def poll(cls, context):
679 obj = context.object
680 if obj is None:
681 return False
682 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
684 def execute(self, context):
685 """Sets Input Tangent Point 2 to analysis of Arc.
687 Args:
688 context: Blender bpy.context instance.
690 Returns:
691 Nothing.
693 scene = context.scene
694 pg = scene.pdt_pg
695 vector_delta, radius = analyse_arc(context, pg)
696 pg.tangent_point1 = vector_delta
697 pg.tangent_radius1 = radius
698 return {"FINISHED"}
701 class PDT_OT_TangentSet3(Operator):
702 """Set Tangent Origin Point from Cursor."""
704 bl_idname = "pdt.tangentset3"
705 bl_label = "Set Tangent Origin Point from Cursor"
706 bl_options = {"REGISTER", "UNDO"}
707 bl_description = "Set Tangent Origin Point from Cursor"
709 @classmethod
710 def poll(cls, context):
711 obj = context.object
712 if obj is None:
713 return False
714 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
716 def execute(self, context):
717 """Sets Input Tangent Point 3 to analysis of Arc.
719 Args:
720 context: Blender bpy.context instance.
722 Returns:
723 Nothing.
725 scene = context.scene
726 pg = scene.pdt_pg
727 pg.tangent_point2 = scene.cursor.location
728 return {"FINISHED"}
731 class PDT_OT_TangentSet4(Operator):
732 """Set Tangent Origin Point from Cursor."""
734 bl_idname = "pdt.tangentset4"
735 bl_label = "Set Tangent Origin Point from Vertex"
736 bl_options = {"REGISTER", "UNDO"}
737 bl_description = "Set Tangent Origin Point from Vertex"
739 @classmethod
740 def poll(cls, context):
741 obj = context.object
742 if obj is None:
743 return False
744 return all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"])
746 def execute(self, context):
747 """Sets Input Tangent Point 2 to selected Vertex.
749 Args:
750 context: Blender bpy.context instance.
752 Returns:
753 Nothing.
755 scene = context.scene
756 pg = scene.pdt_pg
757 obj = context.object
758 bm = bmesh.from_edit_mesh(obj.data)
759 verts = [v for v in bm.verts if v.select]
760 if len(verts) != 1:
761 pg.error = f"{PDT_ERR_SEL_1_VERT} {len(verts)})"
762 context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
763 raise PDT_SelectionError
764 pg.tangent_point2 = verts[0].co
765 return {"FINISHED"}
768 class PDT_OT_TangentExpandMenu(Operator):
769 """Expand/Collapse Tangent Menu."""
771 bl_idname = "pdt.tangentexpandmenu"
772 bl_label = "Expand/Collapse Tangent Menu"
773 bl_options = {"REGISTER", "UNDO"}
774 bl_description = "Expand/Collapse Tangent Menu to Show/Hide Input Options"
776 def execute(self, context):
777 """Expand/Collapse Tangent Menu.
779 Note:
780 This is used to add further options to the menu.
782 Args:
783 context: Blender bpy.context instance.
785 Returns:
786 Nothing.
788 scene = context.scene
789 pg = scene.pdt_pg
790 if pg.menu_expand:
791 pg.menu_expand = False
792 else:
793 pg.menu_expand = True
794 return {"FINISHED"}