File headers: use SPDX license identifiers
[blender-addons.git] / precision_drawing_tools / pdt_design.py
blobddb7fb3886f6ae24231ba938dc888cbda875fac2
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # -----------------------------------------------------------------------
4 # Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
5 # -----------------------------------------------------------------------
7 from bpy.types import Operator
8 from .pdt_msg_strings import (
9 PDT_ERR_NON_VALID,
10 PDT_LAB_ABS,
11 PDT_LAB_DEL,
12 PDT_LAB_DIR,
13 PDT_LAB_INTERSECT,
14 PDT_LAB_PERCENT,
18 class PDT_OT_PlacementAbs(Operator):
19 """Use Absolute, or Global Placement"""
21 bl_idname = "pdt.absolute"
22 bl_label = "Absolute Mode"
23 bl_options = {"REGISTER", "UNDO"}
25 def execute(self, context):
26 """Manipulates Geometry, or Objects by Absolute (World) Coordinates.
28 Note:
29 - Reads pg.operate from Operation Mode Selector as 'operation'
30 - Reads pg.cartesian_coords scene variables to:
31 -- set position of Cursor (CU)
32 -- set position of Pivot Point (PP)
33 -- MoVe geometry/objects (MV)
34 -- Extrude Vertices (EV)
35 -- Split Edges (SE)
36 -- add a New Vertex (NV)
38 Invalid Options result in self.report Error.
40 Args:
41 context: Blender bpy.context instance.
43 Returns:
44 Status Set.
45 """
47 pg = context.scene.pdt_pg
48 operation = pg.operation
49 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
51 if operation == "CU":
52 # Cursor
53 pg.command = (
54 f"ca{str(round(pg.cartesian_coords.x, decimal_places))}"
55 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
56 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
58 elif operation == "PP":
59 # Pivot Point
60 pg.command = (
61 f"pa{str(round(pg.cartesian_coords.x, decimal_places))}"
62 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
63 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
65 elif operation == "MV":
66 # Move Entities
67 pg.command = (
68 f"ga{str(round(pg.cartesian_coords.x, decimal_places))}"
69 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
70 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
72 elif operation == "SE":
73 # Split Edges
74 pg.command = (
75 f"sa{str(round(pg.cartesian_coords.x, decimal_places))}"
76 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
77 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
79 elif operation == "NV":
80 # New Vertex
81 pg.command = (
82 f"na{str(round(pg.cartesian_coords.x, decimal_places))}"
83 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
84 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
86 elif operation == "EV":
87 # Extrude Vertices
88 pg.command = (
89 f"va{str(round(pg.cartesian_coords.x, decimal_places))}"
90 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
91 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
93 else:
94 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_ABS}"
95 self.report({"ERROR"}, error_message)
96 return {"FINISHED"}
99 class PDT_OT_PlacementDelta(Operator):
100 """Use Delta, or Incremental Placement"""
102 bl_idname = "pdt.delta"
103 bl_label = "Delta Mode"
104 bl_options = {"REGISTER", "UNDO"}
106 def execute(self, context):
107 """Manipulates Geometry, or Objects by Delta Offset (Increment).
109 Note:
110 - Reads pg.operation from Operation Mode Selector as 'operation'
111 - Reads pg.select, pg.plane, pg.cartesian_coords scene variables to:
112 -- set position of CUrsor (CU)
113 -- set position of Pivot Point (PP)
114 -- MoVe geometry/objects (MV)
115 -- Extrude Vertices (EV)
116 -- Split Edges (SE)
117 -- add a New Vertex (NV)
118 -- Duplicate Geometry (DG)
119 -- Extrude Geometry (EG)
121 Invalid Options result in self.report Error.
123 Args:
124 context: Blender bpy.context instance.
126 Returns:
127 Status Set.
130 pg = context.scene.pdt_pg
131 operation = pg.operation
132 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
134 if operation == "CU":
135 # Cursor
136 pg.command = (
137 f"cd{str(round(pg.cartesian_coords.x, decimal_places))}"
138 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
139 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
141 elif operation == "PP":
142 # Pivot Point
143 pg.command = (
144 f"pd{str(round(pg.cartesian_coords.x, decimal_places))}"
145 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
146 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
148 elif operation == "MV":
149 # Move Entities
150 pg.command = (
151 f"gd{str(round(pg.cartesian_coords.x, decimal_places))}"
152 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
153 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
155 elif operation == "SE":
156 # Split Edges
157 pg.command = (
158 f"sd{str(round(pg.cartesian_coords.x, decimal_places))}"
159 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
160 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
162 elif operation == "NV":
163 # New Vertex
164 pg.command = (
165 f"nd{str(round(pg.cartesian_coords.x, decimal_places))}"
166 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
167 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
169 elif operation == "EV":
170 # Extrude Vertices
171 pg.command = (
172 f"vd{str(round(pg.cartesian_coords.x, decimal_places))}"
173 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
174 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
176 elif operation == "DG":
177 # Duplicate Entities
178 pg.command = (
179 f"dd{str(round(pg.cartesian_coords.x, decimal_places))}"
180 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
181 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
183 elif operation == "EG":
184 # Extrude Geometry
185 pg.command = (
186 f"ed{str(round(pg.cartesian_coords.x, decimal_places))}"
187 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
188 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
190 else:
191 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}"
192 self.report({"ERROR"}, error_message)
193 return {"FINISHED"}
196 class PDT_OT_PlacementDis(Operator):
197 """Use Directional, or Distance @ Angle Placement"""
199 bl_idname = "pdt.distance"
200 bl_label = "Distance@Angle Mode"
201 bl_options = {"REGISTER", "UNDO"}
203 def execute(self, context):
204 """Manipulates Geometry, or Objects by Distance at Angle (Direction).
206 Note:
207 - Reads pg.operation from Operation Mode Selector as 'operation'
208 - Reads pg.select, pg.distance, pg.angle, pg.plane & pg.flip_angle scene variables to:
209 -- set position of CUrsor (CU)
210 -- set position of Pivot Point (PP)
211 -- MoVe geometry/objects (MV)
212 -- Extrude Vertices (EV)
213 -- Split Edges (SE)
214 -- add a New Vertex (NV)
215 -- Duplicate Geometry (DG)
216 -- Extrude Geometry (EG)
218 Invalid Options result in self.report Error.
220 Args:
221 context: Blender bpy.context instance.
223 Returns:
224 Status Set.
227 pg = context.scene.pdt_pg
228 operation = pg.operation
229 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
231 if operation == "CU":
232 # Cursor
233 pg.command = (
234 f"ci{str(round(pg.distance, decimal_places))}"
235 f",{str(round(pg.angle, decimal_places))}"
237 elif operation == "PP":
238 # Pivot Point
239 pg.command = (
240 f"pi{str(round(pg.distance, decimal_places))}"
241 f",{str(round(pg.angle, decimal_places))}"
243 elif operation == "MV":
244 # Move Entities
245 pg.command = (
246 f"gi{str(round(pg.distance, decimal_places))}"
247 f",{str(round(pg.angle, decimal_places))}"
249 elif operation == "SE":
250 # Split Edges
251 pg.command = (
252 f"si{str(round(pg.distance, decimal_places))}"
253 f",{str(round(pg.angle, decimal_places))}"
255 elif operation == "NV":
256 # New Vertex
257 pg.command = (
258 f"ni{str(round(pg.distance, decimal_places))}"
259 f",{str(round(pg.angle, decimal_places))}"
261 elif operation == "EV":
262 # Extrude Vertices
263 pg.command = (
264 f"vi{str(round(pg.distance, decimal_places))}"
265 f",{str(round(pg.angle, decimal_places))}"
267 elif operation == "DG":
268 # Duplicate Geometry
269 pg.command = (
270 f"di{str(round(pg.distance, decimal_places))}"
271 f",{str(round(pg.angle, decimal_places))}"
273 elif operation == "EG":
274 # Extrude Geometry
275 pg.command = (
276 f"ei{str(round(pg.distance, decimal_places))}"
277 f",{str(round(pg.angle, decimal_places))}"
279 else:
280 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DIR}"
281 self.report({"ERROR"}, error_message)
282 return {"FINISHED"}
285 class PDT_OT_PlacementView(Operator):
286 """Use Distance Input for View Normal Axis Operations"""
288 bl_idname = "pdt.view_axis"
289 bl_label = "View Normal Axis Mode"
290 bl_options = {"REGISTER", "UNDO"}
292 def execute(self, context):
293 """Manipulates Geometry, or Objects by View Normal Axis Offset (Increment).
295 Note:
296 - Reads pg.operation from Operation Mode Selector as 'operation'
297 - Reads pg.select, pg.plane, pg.cartesian_coords scene variables to:
298 -- set position of CUrsor (CU)
299 -- set position of Pivot Point (PP)
300 -- MoVe geometry/objects (MV)
301 -- Extrude Vertices (EV)
302 -- Split Edges (SE)
303 -- add a New Vertex (NV)
304 -- Duplicate Geometry (DG)
305 -- Extrude Geometry (EG)
307 Invalid Options result in self.report Error.
309 Args:
310 context: Blender bpy.context instance.
312 Returns:
313 Status Set.
316 pg = context.scene.pdt_pg
317 operation = pg.operation
318 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
320 if operation == "CU":
321 # Cursor
322 pg.command = (
323 f"cn{str(round(pg.distance, decimal_places))}"
325 elif operation == "PP":
326 # Pivot Point
327 pg.command = (
328 f"pn{str(round(pg.distance, decimal_places))}"
330 elif operation == "MV":
331 # Move Entities
332 pg.command = (
333 f"gn{str(round(pg.distance, decimal_places))}"
335 elif operation == "NV":
336 # New Vertex
337 pg.command = (
338 f"nn{str(round(pg.distance, decimal_places))}"
340 elif operation == "EV":
341 # Extrude Vertices
342 pg.command = (
343 f"vn{str(round(pg.distance, decimal_places))}"
345 elif operation == "DG":
346 # Duplicate Entities
347 pg.command = (
348 f"dn{str(round(pg.distance, decimal_places))}"
350 elif operation == "EG":
351 # Extrude Geometry
352 pg.command = (
353 f"en{str(round(pg.distance, decimal_places))}"
355 else:
356 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}"
357 self.report({"ERROR"}, error_message)
358 return {"FINISHED"}
361 class PDT_OT_PlacementPer(Operator):
362 """Use Percentage Placement"""
364 bl_idname = "pdt.percent"
365 bl_label = "Percentage Mode"
366 bl_options = {"REGISTER", "UNDO"}
368 def execute(self, context):
369 """Manipulates Geometry, or Objects by Percentage between 2 points.
371 Note:
372 - Reads pg.operation from Operation Mode Selector as 'operation'
373 - Reads pg.percent, pg.extend & pg.flip_percent scene variables to:
374 -- set position of CUrsor (CU)
375 -- set position of Pivot Point (PP)
376 -- MoVe geometry/objects (MV)
377 -- Extrude Vertices (EV)
378 -- Split Edges (SE)
379 -- add a New Vertex (NV)
381 Invalid Options result in self.report Error.
383 Args:
384 context: Blender bpy.context instance.
386 Returns:
387 Status Set.
390 pg = context.scene.pdt_pg
391 operation = pg.operation
392 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
394 if operation == "CU":
395 # Cursor
396 pg.command = f"cp{str(round(pg.percent, decimal_places))}"
397 elif operation == "PP":
398 # Pivot Point
399 pg.command = f"pp{str(round(pg.percent, decimal_places))}"
400 elif operation == "MV":
401 # Move Entities
402 pg.command = f"gp{str(round(pg.percent, decimal_places))}"
403 elif operation == "SE":
404 # Split Edges
405 pg.command = f"sp{str(round(pg.percent, decimal_places))}"
406 elif operation == "NV":
407 # New Vertex
408 pg.command = f"np{str(round(pg.percent, decimal_places))}"
409 elif operation == "EV":
410 # Extrude Vertices
411 pg.command = f"vp{str(round(pg.percent, decimal_places))}"
412 else:
413 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_PERCENT}"
414 self.report({"ERROR"}, error_message)
415 return {"FINISHED"}
418 class PDT_OT_PlacementNormal(Operator):
419 """Use Normal, or Perpendicular Placement"""
421 bl_idname = "pdt.normal"
422 bl_label = "Normal Mode"
423 bl_options = {"REGISTER", "UNDO"}
425 def execute(self, context):
426 """Manipulates Geometry, or Objects by Normal Intersection between 3 points.
428 Note:
429 - Reads pg.operation from Operation Mode Selector as 'operation'
430 - Reads pg.extend scene variable to:
431 -- set position of CUrsor (CU)
432 -- set position of Pivot Point (PP)
433 -- MoVe geometry/objects (MV)
434 -- Extrude Vertices (EV)
435 -- Split Edges (SE)
436 -- add a New Vertex (NV)
438 Invalid Options result in self.report Error.
440 Args:
441 context: Blender bpy.context instance.
443 Returns:
444 Status Set.
447 pg = context.scene.pdt_pg
448 operation = pg.operation
449 if operation == "CU":
450 pg.command = f"cnml"
451 elif operation == "PP":
452 pg.command = f"pnml"
453 elif operation == "MV":
454 pg.command = f"gnml"
455 elif operation == "EV":
456 pg.command = f"vnml"
457 elif operation == "SE":
458 pg.command = f"snml"
459 elif operation == "NV":
460 pg.command = f"nnml"
461 else:
462 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
463 self.report({"ERROR"}, error_message)
464 return {"FINISHED"}
467 class PDT_OT_PlacementCen(Operator):
468 """Use Placement at Arc Centre"""
470 bl_idname = "pdt.centre"
471 bl_label = "Centre Mode"
472 bl_options = {"REGISTER", "UNDO"}
474 def execute(self, context):
475 """Manipulates Geometry, or Objects to an Arc Centre defined by 3 points on an Imaginary Arc.
477 Note:
478 - Reads pg.operation from Operation Mode Selector as 'operation'
479 -- set position of CUrsor (CU)
480 -- set position of Pivot Point (PP)
481 -- MoVe geometry/objects (MV)
482 -- Extrude Vertices (EV)
483 -- add a New vertex (NV)
485 Invalid Options result in self.report Error.
487 Args:
488 context: Blender bpy.context instance.
490 Returns:
491 Status Set.
494 pg = context.scene.pdt_pg
495 operation = pg.operation
496 if operation == "CU":
497 pg.command = f"ccen"
498 elif operation == "PP":
499 pg.command = f"pcen"
500 elif operation == "MV":
501 pg.command = f"gcen"
502 elif operation == "EV":
503 pg.command = f"vcen"
504 elif operation == "NV":
505 pg.command = f"ncen"
506 else:
507 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
508 self.report({"ERROR"}, error_message)
509 return {"FINISHED"}
512 class PDT_OT_PlacementInt(Operator):
513 """Use Intersection, or Convergence Placement"""
515 bl_idname = "pdt.intersect"
516 bl_label = "Intersect Mode"
517 bl_options = {"REGISTER", "UNDO"}
519 def execute(self, context):
520 """Manipulates Geometry, or Objects by Convergence Intersection between 4 points, or 2 Edges.
522 Note:
523 - Reads pg.operation from Operation Mode Selector as 'operation'
524 - Reads pg.plane scene variable and operates in Working Plane to:
525 -- set position of CUrsor (CU)
526 -- set position of Pivot Point (PP)
527 -- MoVe geometry/objects (MV)
528 -- Extrude Vertices (EV)
529 -- add a New vertex (NV)
531 Invalid Options result in "self.report" Error.
533 Args:
534 context: Blender bpy.context instance.
536 Returns:
537 Status Set.
540 pg = context.scene.pdt_pg
541 operation = pg.operation
542 if operation == "CU":
543 pg.command = f"cint"
544 elif operation == "PP":
545 pg.command = f"pint"
546 elif operation == "MV":
547 pg.command = f"gint"
548 elif operation == "EV":
549 pg.command = f"vint"
550 elif operation == "NV":
551 pg.command = f"nint"
552 else:
553 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
554 self.report({"ERROR"}, error_message)
555 return {"FINISHED"}
558 class PDT_OT_JoinVerts(Operator):
559 """Join 2 Free Vertices into an Edge"""
561 bl_idname = "pdt.join"
562 bl_label = "Join 2 Vertices"
563 bl_options = {"REGISTER", "UNDO"}
565 @classmethod
566 def poll(cls, context):
567 ob = context.object
568 if ob is None:
569 return False
570 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
572 def execute(self, context):
573 """Joins 2 Free Vertices that do not form part of a Face.
575 Note:
576 Joins two vertices that do not form part of a single face
577 It is designed to close open Edge Loops, where a face is not required
578 or to join two disconnected Edges.
580 Args:
581 context: Blender bpy.context instance.
583 Returns:
584 Status Set.
587 pg = context.scene.pdt_pg
588 pg.command = f"j2v"
589 return {"FINISHED"}
592 class PDT_OT_Fillet(Operator):
593 """Fillet Edges by Vertex, Set Use Verts to False for Extruded Structure"""
595 bl_idname = "pdt.fillet"
596 bl_label = "Fillet"
597 bl_options = {"REGISTER", "UNDO"}
599 @classmethod
600 def poll(cls, context):
601 ob = context.object
602 if ob is None:
603 return False
604 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
606 def execute(self, context):
607 """Create Fillets by Vertex or by Geometry.
609 Note:
610 Fillets connected edges, or connected faces
611 Uses:
612 - pg.fillet_radius ; Radius of fillet
613 - pg.fillet_segments ; Number of segments
614 - pg.fillet_profile ; Profile, values 0 to 1
615 - pg.fillet_vertices_only ; Vertices (True), or Face/Edges
616 - pg.fillet_intersect ; Intersect dges first (True), or not
618 Args:
619 context: Blender bpy.context instance.
621 Returns:
622 Status Set.
625 pg = context.scene.pdt_pg
626 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
627 if pg.fillet_intersect:
628 pg.command = (
629 f"fi{str(round(pg.fillet_radius, decimal_places))}"
630 f",{str(round(pg.fillet_segments, decimal_places))}"
631 f",{str(round(pg.fillet_profile, decimal_places))}"
633 elif pg.fillet_vertices_only:
634 pg.command = (
635 f"fv{str(round(pg.fillet_radius, decimal_places))}"
636 f",{str(round(pg.fillet_segments, decimal_places))}"
637 f",{str(round(pg.fillet_profile, decimal_places))}"
639 else:
640 pg.command = (
641 f"fe{str(round(pg.fillet_radius, decimal_places))}"
642 f",{str(round(pg.fillet_segments, decimal_places))}"
643 f",{str(round(pg.fillet_profile, decimal_places))}"
645 return {"FINISHED"}
648 class PDT_OT_Angle2(Operator):
649 """Measure Distance and Angle in Working Plane, Also sets Deltas"""
651 bl_idname = "pdt.angle2"
652 bl_label = "Measure 2D"
653 bl_options = {"REGISTER", "UNDO"}
655 def execute(self, context):
656 """Measures Angle and Offsets between 2 Points in View Plane.
658 Note:
659 Uses 2 Selected Vertices to set pg.angle and pg.distance scene variables
660 also sets delta offset from these 2 points using standard Numpy Routines
661 Works in Edit and Oject Modes.
663 Args:
664 context: Blender bpy.context instance.
666 Returns:
667 Status Set.
670 pg = context.scene.pdt_pg
671 pg.command = f"ad2"
672 return {"FINISHED"}
675 class PDT_OT_Angle3(Operator):
676 """Measure Distance and Angle in 3D Space"""
678 bl_idname = "pdt.angle3"
679 bl_label = "Measure 3D"
680 bl_options = {"REGISTER", "UNDO"}
682 def execute(self, context):
683 """Measures Angle and Offsets between 3 Points in World Space, Also sets Deltas.
685 Note:
686 Uses 3 Selected Vertices to set pg.angle and pg.distance scene variables
687 also sets delta offset from these 3 points using standard Numpy Routines
688 Works in Edit and Oject Modes.
690 Args:
691 context: Blender bpy.context instance.
693 Returns:
694 Status Set.
697 pg = context.scene.pdt_pg
698 pg.command = f"ad3"
699 return {"FINISHED"}
702 class PDT_OT_Origin(Operator):
703 """Move Object Origin to Cursor Location"""
705 bl_idname = "pdt.origin"
706 bl_label = "Move Origin"
707 bl_options = {"REGISTER", "UNDO"}
709 def execute(self, context):
710 """Sets Object Origin in Edit Mode to Cursor Location.
712 Note:
713 Keeps geometry static in World Space whilst moving Object Origin
714 Requires cursor location
715 Works in Edit and Object Modes.
717 Args:
718 context: Blender bpy.context instance.
720 Returns:
721 Status Set.
724 pg = context.scene.pdt_pg
725 pg.command = f"otc"
726 return {"FINISHED"}
729 class PDT_OT_Taper(Operator):
730 """Taper Vertices at Angle in Chosen Axis Mode"""
732 bl_idname = "pdt.taper"
733 bl_label = "Taper"
734 bl_options = {"REGISTER", "UNDO"}
736 @classmethod
737 def poll(cls, context):
738 ob = context.object
739 if ob is None:
740 return False
741 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
743 def execute(self, context):
744 """Taper Geometry along World Axes.
746 Note:
747 Similar to Blender Shear command except that it shears by angle rather than displacement.
748 Rotates about World Axes and displaces along World Axes, angle must not exceed +-80 degrees.
749 Rotation axis is centred on Active Vertex.
750 Works only in Edit mode.
752 Args:
753 context: Blender bpy.context instance.
755 Note:
756 Uses pg.taper & pg.angle scene variables
758 Returns:
759 Status Set.
762 pg = context.scene.pdt_pg
763 pg.command = f"tap"
764 return {"FINISHED"}
766 #class PDT_Extrude_Modal(Operator):
767 # """Extrude Modal Plane Along Normal Axis"""
768 # bl_idname = "pdt.extrude_modal"
769 # bl_label = "Extrude Modal Normal"
770 # bl_options = {"REGISTER", "UNDO"}