Merge branch 'blender-v4.0-release'
[blender-addons.git] / precision_drawing_tools / pdt_design.py
blob64e0f16e051f42f1e6f1e1e781760013115d05e6
1 # SPDX-FileCopyrightText: 2019-2022 Alan Odom (Clockmender)
2 # SPDX-FileCopyrightText: 2019-2022 Rune Morling (ermo)
4 # SPDX-License-Identifier: GPL-2.0-or-later
6 from bpy.types import Operator
7 from .pdt_msg_strings import (
8 PDT_ERR_NON_VALID,
9 PDT_LAB_ABS,
10 PDT_LAB_DEL,
11 PDT_LAB_DIR,
12 PDT_LAB_INTERSECT,
13 PDT_LAB_PERCENT,
17 class PDT_OT_PlacementAbs(Operator):
18 """Use Absolute, or Global Placement"""
20 bl_idname = "pdt.absolute"
21 bl_label = "Absolute Mode"
22 bl_options = {"REGISTER", "UNDO"}
24 def execute(self, context):
25 """Manipulates Geometry, or Objects by Absolute (World) Coordinates.
27 Note:
28 - Reads pg.operate from Operation Mode Selector as 'operation'
29 - Reads pg.cartesian_coords scene variables to:
30 -- set position of Cursor (CU)
31 -- set position of Pivot Point (PP)
32 -- MoVe geometry/objects (MV)
33 -- Extrude Vertices (EV)
34 -- Split Edges (SE)
35 -- add a New Vertex (NV)
37 Invalid Options result in self.report Error.
39 Args:
40 context: Blender bpy.context instance.
42 Returns:
43 Status Set.
44 """
46 pg = context.scene.pdt_pg
47 operation = pg.operation
48 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
50 if operation == "CU":
51 # Cursor
52 pg.command = (
53 f"ca{str(round(pg.cartesian_coords.x, decimal_places))}"
54 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
55 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
57 elif operation == "PP":
58 # Pivot Point
59 pg.command = (
60 f"pa{str(round(pg.cartesian_coords.x, decimal_places))}"
61 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
62 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
64 elif operation == "MV":
65 # Move Entities
66 pg.command = (
67 f"ga{str(round(pg.cartesian_coords.x, decimal_places))}"
68 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
69 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
71 elif operation == "SE":
72 # Split Edges
73 pg.command = (
74 f"sa{str(round(pg.cartesian_coords.x, decimal_places))}"
75 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
76 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
78 elif operation == "NV":
79 # New Vertex
80 pg.command = (
81 f"na{str(round(pg.cartesian_coords.x, decimal_places))}"
82 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
83 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
85 elif operation == "EV":
86 # Extrude Vertices
87 pg.command = (
88 f"va{str(round(pg.cartesian_coords.x, decimal_places))}"
89 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
90 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
92 else:
93 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_ABS}"
94 self.report({"ERROR"}, error_message)
95 return {"FINISHED"}
98 class PDT_OT_PlacementDelta(Operator):
99 """Use Delta, or Incremental Placement"""
101 bl_idname = "pdt.delta"
102 bl_label = "Delta Mode"
103 bl_options = {"REGISTER", "UNDO"}
105 def execute(self, context):
106 """Manipulates Geometry, or Objects by Delta Offset (Increment).
108 Note:
109 - Reads pg.operation from Operation Mode Selector as 'operation'
110 - Reads pg.select, pg.plane, pg.cartesian_coords scene variables to:
111 -- set position of CUrsor (CU)
112 -- set position of Pivot Point (PP)
113 -- MoVe geometry/objects (MV)
114 -- Extrude Vertices (EV)
115 -- Split Edges (SE)
116 -- add a New Vertex (NV)
117 -- Duplicate Geometry (DG)
118 -- Extrude Geometry (EG)
120 Invalid Options result in self.report Error.
122 Args:
123 context: Blender bpy.context instance.
125 Returns:
126 Status Set.
129 pg = context.scene.pdt_pg
130 operation = pg.operation
131 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
133 if operation == "CU":
134 # Cursor
135 pg.command = (
136 f"cd{str(round(pg.cartesian_coords.x, decimal_places))}"
137 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
138 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
140 elif operation == "PP":
141 # Pivot Point
142 pg.command = (
143 f"pd{str(round(pg.cartesian_coords.x, decimal_places))}"
144 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
145 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
147 elif operation == "MV":
148 # Move Entities
149 pg.command = (
150 f"gd{str(round(pg.cartesian_coords.x, decimal_places))}"
151 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
152 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
154 elif operation == "SE":
155 # Split Edges
156 pg.command = (
157 f"sd{str(round(pg.cartesian_coords.x, decimal_places))}"
158 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
159 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
161 elif operation == "NV":
162 # New Vertex
163 pg.command = (
164 f"nd{str(round(pg.cartesian_coords.x, decimal_places))}"
165 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
166 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
168 elif operation == "EV":
169 # Extrude Vertices
170 pg.command = (
171 f"vd{str(round(pg.cartesian_coords.x, decimal_places))}"
172 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
173 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
175 elif operation == "DG":
176 # Duplicate Entities
177 pg.command = (
178 f"dd{str(round(pg.cartesian_coords.x, decimal_places))}"
179 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
180 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
182 elif operation == "EG":
183 # Extrude Geometry
184 pg.command = (
185 f"ed{str(round(pg.cartesian_coords.x, decimal_places))}"
186 f",{str(round(pg.cartesian_coords.y, decimal_places))}"
187 f",{str(round(pg.cartesian_coords.z, decimal_places))}"
189 else:
190 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}"
191 self.report({"ERROR"}, error_message)
192 return {"FINISHED"}
195 class PDT_OT_PlacementDis(Operator):
196 """Use Directional, or Distance @ Angle Placement"""
198 bl_idname = "pdt.distance"
199 bl_label = "Distance@Angle Mode"
200 bl_options = {"REGISTER", "UNDO"}
202 def execute(self, context):
203 """Manipulates Geometry, or Objects by Distance at Angle (Direction).
205 Note:
206 - Reads pg.operation from Operation Mode Selector as 'operation'
207 - Reads pg.select, pg.distance, pg.angle, pg.plane & pg.flip_angle scene variables to:
208 -- set position of CUrsor (CU)
209 -- set position of Pivot Point (PP)
210 -- MoVe geometry/objects (MV)
211 -- Extrude Vertices (EV)
212 -- Split Edges (SE)
213 -- add a New Vertex (NV)
214 -- Duplicate Geometry (DG)
215 -- Extrude Geometry (EG)
217 Invalid Options result in self.report Error.
219 Args:
220 context: Blender bpy.context instance.
222 Returns:
223 Status Set.
226 pg = context.scene.pdt_pg
227 operation = pg.operation
228 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
230 if operation == "CU":
231 # Cursor
232 pg.command = (
233 f"ci{str(round(pg.distance, decimal_places))}"
234 f",{str(round(pg.angle, decimal_places))}"
236 elif operation == "PP":
237 # Pivot Point
238 pg.command = (
239 f"pi{str(round(pg.distance, decimal_places))}"
240 f",{str(round(pg.angle, decimal_places))}"
242 elif operation == "MV":
243 # Move Entities
244 pg.command = (
245 f"gi{str(round(pg.distance, decimal_places))}"
246 f",{str(round(pg.angle, decimal_places))}"
248 elif operation == "SE":
249 # Split Edges
250 pg.command = (
251 f"si{str(round(pg.distance, decimal_places))}"
252 f",{str(round(pg.angle, decimal_places))}"
254 elif operation == "NV":
255 # New Vertex
256 pg.command = (
257 f"ni{str(round(pg.distance, decimal_places))}"
258 f",{str(round(pg.angle, decimal_places))}"
260 elif operation == "EV":
261 # Extrude Vertices
262 pg.command = (
263 f"vi{str(round(pg.distance, decimal_places))}"
264 f",{str(round(pg.angle, decimal_places))}"
266 elif operation == "DG":
267 # Duplicate Geometry
268 pg.command = (
269 f"di{str(round(pg.distance, decimal_places))}"
270 f",{str(round(pg.angle, decimal_places))}"
272 elif operation == "EG":
273 # Extrude Geometry
274 pg.command = (
275 f"ei{str(round(pg.distance, decimal_places))}"
276 f",{str(round(pg.angle, decimal_places))}"
278 else:
279 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DIR}"
280 self.report({"ERROR"}, error_message)
281 return {"FINISHED"}
284 class PDT_OT_PlacementView(Operator):
285 """Use Distance Input for View Normal Axis Operations"""
287 bl_idname = "pdt.view_axis"
288 bl_label = "View Normal Axis Mode"
289 bl_options = {"REGISTER", "UNDO"}
291 def execute(self, context):
292 """Manipulates Geometry, or Objects by View Normal Axis Offset (Increment).
294 Note:
295 - Reads pg.operation from Operation Mode Selector as 'operation'
296 - Reads pg.select, pg.plane, pg.cartesian_coords scene variables to:
297 -- set position of CUrsor (CU)
298 -- set position of Pivot Point (PP)
299 -- MoVe geometry/objects (MV)
300 -- Extrude Vertices (EV)
301 -- Split Edges (SE)
302 -- add a New Vertex (NV)
303 -- Duplicate Geometry (DG)
304 -- Extrude Geometry (EG)
306 Invalid Options result in self.report Error.
308 Args:
309 context: Blender bpy.context instance.
311 Returns:
312 Status Set.
315 pg = context.scene.pdt_pg
316 operation = pg.operation
317 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
319 if operation == "CU":
320 # Cursor
321 pg.command = (
322 f"cn{str(round(pg.distance, decimal_places))}"
324 elif operation == "PP":
325 # Pivot Point
326 pg.command = (
327 f"pn{str(round(pg.distance, decimal_places))}"
329 elif operation == "MV":
330 # Move Entities
331 pg.command = (
332 f"gn{str(round(pg.distance, decimal_places))}"
334 elif operation == "NV":
335 # New Vertex
336 pg.command = (
337 f"nn{str(round(pg.distance, decimal_places))}"
339 elif operation == "EV":
340 # Extrude Vertices
341 pg.command = (
342 f"vn{str(round(pg.distance, decimal_places))}"
344 elif operation == "DG":
345 # Duplicate Entities
346 pg.command = (
347 f"dn{str(round(pg.distance, decimal_places))}"
349 elif operation == "EG":
350 # Extrude Geometry
351 pg.command = (
352 f"en{str(round(pg.distance, decimal_places))}"
354 else:
355 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}"
356 self.report({"ERROR"}, error_message)
357 return {"FINISHED"}
360 class PDT_OT_PlacementPer(Operator):
361 """Use Percentage Placement"""
363 bl_idname = "pdt.percent"
364 bl_label = "Percentage Mode"
365 bl_options = {"REGISTER", "UNDO"}
367 def execute(self, context):
368 """Manipulates Geometry, or Objects by Percentage between 2 points.
370 Note:
371 - Reads pg.operation from Operation Mode Selector as 'operation'
372 - Reads pg.percent, pg.extend & pg.flip_percent scene variables to:
373 -- set position of CUrsor (CU)
374 -- set position of Pivot Point (PP)
375 -- MoVe geometry/objects (MV)
376 -- Extrude Vertices (EV)
377 -- Split Edges (SE)
378 -- add a New Vertex (NV)
380 Invalid Options result in self.report Error.
382 Args:
383 context: Blender bpy.context instance.
385 Returns:
386 Status Set.
389 pg = context.scene.pdt_pg
390 operation = pg.operation
391 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
393 if operation == "CU":
394 # Cursor
395 pg.command = f"cp{str(round(pg.percent, decimal_places))}"
396 elif operation == "PP":
397 # Pivot Point
398 pg.command = f"pp{str(round(pg.percent, decimal_places))}"
399 elif operation == "MV":
400 # Move Entities
401 pg.command = f"gp{str(round(pg.percent, decimal_places))}"
402 elif operation == "SE":
403 # Split Edges
404 pg.command = f"sp{str(round(pg.percent, decimal_places))}"
405 elif operation == "NV":
406 # New Vertex
407 pg.command = f"np{str(round(pg.percent, decimal_places))}"
408 elif operation == "EV":
409 # Extrude Vertices
410 pg.command = f"vp{str(round(pg.percent, decimal_places))}"
411 else:
412 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_PERCENT}"
413 self.report({"ERROR"}, error_message)
414 return {"FINISHED"}
417 class PDT_OT_PlacementNormal(Operator):
418 """Use Normal, or Perpendicular Placement"""
420 bl_idname = "pdt.normal"
421 bl_label = "Normal Mode"
422 bl_options = {"REGISTER", "UNDO"}
424 def execute(self, context):
425 """Manipulates Geometry, or Objects by Normal Intersection between 3 points.
427 Note:
428 - Reads pg.operation from Operation Mode Selector as 'operation'
429 - Reads pg.extend scene variable to:
430 -- set position of CUrsor (CU)
431 -- set position of Pivot Point (PP)
432 -- MoVe geometry/objects (MV)
433 -- Extrude Vertices (EV)
434 -- Split Edges (SE)
435 -- add a New Vertex (NV)
437 Invalid Options result in self.report Error.
439 Args:
440 context: Blender bpy.context instance.
442 Returns:
443 Status Set.
446 pg = context.scene.pdt_pg
447 operation = pg.operation
448 if operation == "CU":
449 pg.command = f"cnml"
450 elif operation == "PP":
451 pg.command = f"pnml"
452 elif operation == "MV":
453 pg.command = f"gnml"
454 elif operation == "EV":
455 pg.command = f"vnml"
456 elif operation == "SE":
457 pg.command = f"snml"
458 elif operation == "NV":
459 pg.command = f"nnml"
460 else:
461 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
462 self.report({"ERROR"}, error_message)
463 return {"FINISHED"}
466 class PDT_OT_PlacementCen(Operator):
467 """Use Placement at Arc Centre"""
469 bl_idname = "pdt.centre"
470 bl_label = "Centre Mode"
471 bl_options = {"REGISTER", "UNDO"}
473 def execute(self, context):
474 """Manipulates Geometry, or Objects to an Arc Centre defined by 3 points on an Imaginary Arc.
476 Note:
477 - Reads pg.operation from Operation Mode Selector as 'operation'
478 -- set position of CUrsor (CU)
479 -- set position of Pivot Point (PP)
480 -- MoVe geometry/objects (MV)
481 -- Extrude Vertices (EV)
482 -- add a New vertex (NV)
484 Invalid Options result in self.report Error.
486 Args:
487 context: Blender bpy.context instance.
489 Returns:
490 Status Set.
493 pg = context.scene.pdt_pg
494 operation = pg.operation
495 if operation == "CU":
496 pg.command = f"ccen"
497 elif operation == "PP":
498 pg.command = f"pcen"
499 elif operation == "MV":
500 pg.command = f"gcen"
501 elif operation == "EV":
502 pg.command = f"vcen"
503 elif operation == "NV":
504 pg.command = f"ncen"
505 else:
506 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
507 self.report({"ERROR"}, error_message)
508 return {"FINISHED"}
511 class PDT_OT_PlacementInt(Operator):
512 """Use Intersection, or Convergence Placement"""
514 bl_idname = "pdt.intersect"
515 bl_label = "Intersect Mode"
516 bl_options = {"REGISTER", "UNDO"}
518 def execute(self, context):
519 """Manipulates Geometry, or Objects by Convergence Intersection between 4 points, or 2 Edges.
521 Note:
522 - Reads pg.operation from Operation Mode Selector as 'operation'
523 - Reads pg.plane scene variable and operates in Working Plane to:
524 -- set position of CUrsor (CU)
525 -- set position of Pivot Point (PP)
526 -- MoVe geometry/objects (MV)
527 -- Extrude Vertices (EV)
528 -- add a New vertex (NV)
530 Invalid Options result in "self.report" Error.
532 Args:
533 context: Blender bpy.context instance.
535 Returns:
536 Status Set.
539 pg = context.scene.pdt_pg
540 operation = pg.operation
541 if operation == "CU":
542 pg.command = f"cint"
543 elif operation == "PP":
544 pg.command = f"pint"
545 elif operation == "MV":
546 pg.command = f"gint"
547 elif operation == "EV":
548 pg.command = f"vint"
549 elif operation == "NV":
550 pg.command = f"nint"
551 else:
552 error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
553 self.report({"ERROR"}, error_message)
554 return {"FINISHED"}
557 class PDT_OT_JoinVerts(Operator):
558 """Join 2 Free Vertices into an Edge"""
560 bl_idname = "pdt.join"
561 bl_label = "Join 2 Vertices"
562 bl_options = {"REGISTER", "UNDO"}
564 @classmethod
565 def poll(cls, context):
566 ob = context.object
567 if ob is None:
568 return False
569 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
571 def execute(self, context):
572 """Joins 2 Free Vertices that do not form part of a Face.
574 Note:
575 Joins two vertices that do not form part of a single face
576 It is designed to close open Edge Loops, where a face is not required
577 or to join two disconnected Edges.
579 Args:
580 context: Blender bpy.context instance.
582 Returns:
583 Status Set.
586 pg = context.scene.pdt_pg
587 pg.command = f"j2v"
588 return {"FINISHED"}
591 class PDT_OT_Fillet(Operator):
592 """Fillet Edges by Vertex, Set Use Verts to False for Extruded Structure"""
594 bl_idname = "pdt.fillet"
595 bl_label = "Fillet"
596 bl_options = {"REGISTER", "UNDO"}
598 @classmethod
599 def poll(cls, context):
600 ob = context.object
601 if ob is None:
602 return False
603 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
605 def execute(self, context):
606 """Create Fillets by Vertex or by Geometry.
608 Note:
609 Fillets connected edges, or connected faces
610 Uses:
611 - pg.fillet_radius ; Radius of fillet
612 - pg.fillet_segments ; Number of segments
613 - pg.fillet_profile ; Profile, values 0 to 1
614 - pg.fillet_vertices_only ; Vertices (True), or Face/Edges
615 - pg.fillet_intersect ; Intersect dges first (True), or not
617 Args:
618 context: Blender bpy.context instance.
620 Returns:
621 Status Set.
624 pg = context.scene.pdt_pg
625 decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round
626 if pg.fillet_intersect:
627 pg.command = (
628 f"fi{str(round(pg.fillet_radius, decimal_places))}"
629 f",{str(round(pg.fillet_segments, decimal_places))}"
630 f",{str(round(pg.fillet_profile, decimal_places))}"
632 elif pg.fillet_vertices_only:
633 pg.command = (
634 f"fv{str(round(pg.fillet_radius, decimal_places))}"
635 f",{str(round(pg.fillet_segments, decimal_places))}"
636 f",{str(round(pg.fillet_profile, decimal_places))}"
638 else:
639 pg.command = (
640 f"fe{str(round(pg.fillet_radius, decimal_places))}"
641 f",{str(round(pg.fillet_segments, decimal_places))}"
642 f",{str(round(pg.fillet_profile, decimal_places))}"
644 return {"FINISHED"}
647 class PDT_OT_Angle2(Operator):
648 """Measure Distance and Angle in Working Plane, Also sets Deltas"""
650 bl_idname = "pdt.angle2"
651 bl_label = "Measure 2D"
652 bl_options = {"REGISTER", "UNDO"}
654 def execute(self, context):
655 """Measures Angle and Offsets between 2 Points in View Plane.
657 Note:
658 Uses 2 Selected Vertices to set pg.angle and pg.distance scene variables
659 also sets delta offset from these 2 points using standard Numpy Routines
660 Works in Edit and Object Modes.
662 Args:
663 context: Blender bpy.context instance.
665 Returns:
666 Status Set.
669 pg = context.scene.pdt_pg
670 pg.command = f"ad2"
671 return {"FINISHED"}
674 class PDT_OT_Angle3(Operator):
675 """Measure Distance and Angle in 3D Space"""
677 bl_idname = "pdt.angle3"
678 bl_label = "Measure 3D"
679 bl_options = {"REGISTER", "UNDO"}
681 def execute(self, context):
682 """Measures Angle and Offsets between 3 Points in World Space, Also sets Deltas.
684 Note:
685 Uses 3 Selected Vertices to set pg.angle and pg.distance scene variables
686 also sets delta offset from these 3 points using standard Numpy Routines
687 Works in Edit and Object Modes.
689 Args:
690 context: Blender bpy.context instance.
692 Returns:
693 Status Set.
696 pg = context.scene.pdt_pg
697 pg.command = f"ad3"
698 return {"FINISHED"}
701 class PDT_OT_Origin(Operator):
702 """Move Object Origin to Cursor Location"""
704 bl_idname = "pdt.origin"
705 bl_label = "Move Origin"
706 bl_options = {"REGISTER", "UNDO"}
708 def execute(self, context):
709 """Sets Object Origin in Edit Mode to Cursor Location.
711 Note:
712 Keeps geometry static in World Space whilst moving Object Origin
713 Requires cursor location
714 Works in Edit and Object Modes.
716 Args:
717 context: Blender bpy.context instance.
719 Returns:
720 Status Set.
723 pg = context.scene.pdt_pg
724 pg.command = f"otc"
725 return {"FINISHED"}
728 class PDT_OT_Taper(Operator):
729 """Taper Vertices at Angle in Chosen Axis Mode"""
731 bl_idname = "pdt.taper"
732 bl_label = "Taper"
733 bl_options = {"REGISTER", "UNDO"}
735 @classmethod
736 def poll(cls, context):
737 ob = context.object
738 if ob is None:
739 return False
740 return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
742 def execute(self, context):
743 """Taper Geometry along World Axes.
745 Note:
746 Similar to Blender Shear command except that it shears by angle rather than displacement.
747 Rotates about World Axes and displaces along World Axes, angle must not exceed +-80 degrees.
748 Rotation axis is centred on Active Vertex.
749 Works only in Edit mode.
751 Args:
752 context: Blender bpy.context instance.
754 Note:
755 Uses pg.taper & pg.angle scene variables
757 Returns:
758 Status Set.
761 pg = context.scene.pdt_pg
762 pg.command = f"tap"
763 return {"FINISHED"}
765 #class PDT_Extrude_Modal(Operator):
766 # """Extrude Modal Plane Along Normal Axis"""
767 # bl_idname = "pdt.extrude_modal"
768 # bl_label = "Extrude Modal Normal"
769 # bl_options = {"REGISTER", "UNDO"}