# SPDX-License-Identifier: GPL-2.0-or-later # ----------------------------------------------------------------------- # Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019 # ----------------------------------------------------------------------- # from bpy.types import Operator from .pdt_msg_strings import ( PDT_ERR_NON_VALID, PDT_LAB_ABS, PDT_LAB_DEL, PDT_LAB_DIR, PDT_LAB_INTERSECT, PDT_LAB_PERCENT, ) class PDT_OT_PlacementAbs(Operator): """Use Absolute, or Global Placement""" bl_idname = "pdt.absolute" bl_label = "Absolute Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by Absolute (World) Coordinates. Note: - Reads pg.operate from Operation Mode Selector as 'operation' - Reads pg.cartesian_coords scene variables to: -- set position of Cursor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- Split Edges (SE) -- add a New Vertex (NV) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round if operation == "CU": # Cursor pg.command = ( f"ca{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "PP": # Pivot Point pg.command = ( f"pa{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "MV": # Move Entities pg.command = ( f"ga{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "SE": # Split Edges pg.command = ( f"sa{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "NV": # New Vertex pg.command = ( f"na{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "EV": # Extrude Vertices pg.command = ( f"va{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_ABS}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementDelta(Operator): """Use Delta, or Incremental Placement""" bl_idname = "pdt.delta" bl_label = "Delta Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by Delta Offset (Increment). Note: - Reads pg.operation from Operation Mode Selector as 'operation' - Reads pg.select, pg.plane, pg.cartesian_coords scene variables to: -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- Split Edges (SE) -- add a New Vertex (NV) -- Duplicate Geometry (DG) -- Extrude Geometry (EG) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round if operation == "CU": # Cursor pg.command = ( f"cd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "PP": # Pivot Point pg.command = ( f"pd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "MV": # Move Entities pg.command = ( f"gd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "SE": # Split Edges pg.command = ( f"sd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "NV": # New Vertex pg.command = ( f"nd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "EV": # Extrude Vertices pg.command = ( f"vd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "DG": # Duplicate Entities pg.command = ( f"dd{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) elif operation == "EG": # Extrude Geometry pg.command = ( f"ed{str(round(pg.cartesian_coords.x, decimal_places))}" f",{str(round(pg.cartesian_coords.y, decimal_places))}" f",{str(round(pg.cartesian_coords.z, decimal_places))}" ) else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementDis(Operator): """Use Directional, or Distance @ Angle Placement""" bl_idname = "pdt.distance" bl_label = "Distance@Angle Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by Distance at Angle (Direction). Note: - Reads pg.operation from Operation Mode Selector as 'operation' - Reads pg.select, pg.distance, pg.angle, pg.plane & pg.flip_angle scene variables to: -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- Split Edges (SE) -- add a New Vertex (NV) -- Duplicate Geometry (DG) -- Extrude Geometry (EG) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round if operation == "CU": # Cursor pg.command = ( f"ci{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "PP": # Pivot Point pg.command = ( f"pi{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "MV": # Move Entities pg.command = ( f"gi{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "SE": # Split Edges pg.command = ( f"si{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "NV": # New Vertex pg.command = ( f"ni{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "EV": # Extrude Vertices pg.command = ( f"vi{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "DG": # Duplicate Geometry pg.command = ( f"di{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) elif operation == "EG": # Extrude Geometry pg.command = ( f"ei{str(round(pg.distance, decimal_places))}" f",{str(round(pg.angle, decimal_places))}" ) else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DIR}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementView(Operator): """Use Distance Input for View Normal Axis Operations""" bl_idname = "pdt.view_axis" bl_label = "View Normal Axis Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by View Normal Axis Offset (Increment). Note: - Reads pg.operation from Operation Mode Selector as 'operation' - Reads pg.select, pg.plane, pg.cartesian_coords scene variables to: -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- Split Edges (SE) -- add a New Vertex (NV) -- Duplicate Geometry (DG) -- Extrude Geometry (EG) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round if operation == "CU": # Cursor pg.command = ( f"cn{str(round(pg.distance, decimal_places))}" ) elif operation == "PP": # Pivot Point pg.command = ( f"pn{str(round(pg.distance, decimal_places))}" ) elif operation == "MV": # Move Entities pg.command = ( f"gn{str(round(pg.distance, decimal_places))}" ) elif operation == "NV": # New Vertex pg.command = ( f"nn{str(round(pg.distance, decimal_places))}" ) elif operation == "EV": # Extrude Vertices pg.command = ( f"vn{str(round(pg.distance, decimal_places))}" ) elif operation == "DG": # Duplicate Entities pg.command = ( f"dn{str(round(pg.distance, decimal_places))}" ) elif operation == "EG": # Extrude Geometry pg.command = ( f"en{str(round(pg.distance, decimal_places))}" ) else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementPer(Operator): """Use Percentage Placement""" bl_idname = "pdt.percent" bl_label = "Percentage Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by Percentage between 2 points. Note: - Reads pg.operation from Operation Mode Selector as 'operation' - Reads pg.percent, pg.extend & pg.flip_percent scene variables to: -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- Split Edges (SE) -- add a New Vertex (NV) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round if operation == "CU": # Cursor pg.command = f"cp{str(round(pg.percent, decimal_places))}" elif operation == "PP": # Pivot Point pg.command = f"pp{str(round(pg.percent, decimal_places))}" elif operation == "MV": # Move Entities pg.command = f"gp{str(round(pg.percent, decimal_places))}" elif operation == "SE": # Split Edges pg.command = f"sp{str(round(pg.percent, decimal_places))}" elif operation == "NV": # New Vertex pg.command = f"np{str(round(pg.percent, decimal_places))}" elif operation == "EV": # Extrude Vertices pg.command = f"vp{str(round(pg.percent, decimal_places))}" else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_PERCENT}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementNormal(Operator): """Use Normal, or Perpendicular Placement""" bl_idname = "pdt.normal" bl_label = "Normal Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by Normal Intersection between 3 points. Note: - Reads pg.operation from Operation Mode Selector as 'operation' - Reads pg.extend scene variable to: -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- Split Edges (SE) -- add a New Vertex (NV) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation if operation == "CU": pg.command = f"cnml" elif operation == "PP": pg.command = f"pnml" elif operation == "MV": pg.command = f"gnml" elif operation == "EV": pg.command = f"vnml" elif operation == "SE": pg.command = f"snml" elif operation == "NV": pg.command = f"nnml" else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementCen(Operator): """Use Placement at Arc Centre""" bl_idname = "pdt.centre" bl_label = "Centre Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects to an Arc Centre defined by 3 points on an Imaginary Arc. Note: - Reads pg.operation from Operation Mode Selector as 'operation' -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- add a New vertex (NV) Invalid Options result in self.report Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation if operation == "CU": pg.command = f"ccen" elif operation == "PP": pg.command = f"pcen" elif operation == "MV": pg.command = f"gcen" elif operation == "EV": pg.command = f"vcen" elif operation == "NV": pg.command = f"ncen" else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_PlacementInt(Operator): """Use Intersection, or Convergence Placement""" bl_idname = "pdt.intersect" bl_label = "Intersect Mode" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Manipulates Geometry, or Objects by Convergence Intersection between 4 points, or 2 Edges. Note: - Reads pg.operation from Operation Mode Selector as 'operation' - Reads pg.plane scene variable and operates in Working Plane to: -- set position of CUrsor (CU) -- set position of Pivot Point (PP) -- MoVe geometry/objects (MV) -- Extrude Vertices (EV) -- add a New vertex (NV) Invalid Options result in "self.report" Error. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg operation = pg.operation if operation == "CU": pg.command = f"cint" elif operation == "PP": pg.command = f"pint" elif operation == "MV": pg.command = f"gint" elif operation == "EV": pg.command = f"vint" elif operation == "NV": pg.command = f"nint" else: error_message = f"{operation} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}" self.report({"ERROR"}, error_message) return {"FINISHED"} class PDT_OT_JoinVerts(Operator): """Join 2 Free Vertices into an Edge""" bl_idname = "pdt.join" bl_label = "Join 2 Vertices" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): ob = context.object if ob is None: return False return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"]) def execute(self, context): """Joins 2 Free Vertices that do not form part of a Face. Note: Joins two vertices that do not form part of a single face It is designed to close open Edge Loops, where a face is not required or to join two disconnected Edges. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg pg.command = f"j2v" return {"FINISHED"} class PDT_OT_Fillet(Operator): """Fillet Edges by Vertex, Set Use Verts to False for Extruded Structure""" bl_idname = "pdt.fillet" bl_label = "Fillet" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): ob = context.object if ob is None: return False return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"]) def execute(self, context): """Create Fillets by Vertex or by Geometry. Note: Fillets connected edges, or connected faces Uses: - pg.fillet_radius ; Radius of fillet - pg.fillet_segments ; Number of segments - pg.fillet_profile ; Profile, values 0 to 1 - pg.fillet_vertices_only ; Vertices (True), or Face/Edges - pg.fillet_intersect ; Intersect dges first (True), or not Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg decimal_places = context.preferences.addons[__package__].preferences.pdt_input_round if pg.fillet_intersect: pg.command = ( f"fi{str(round(pg.fillet_radius, decimal_places))}" f",{str(round(pg.fillet_segments, decimal_places))}" f",{str(round(pg.fillet_profile, decimal_places))}" ) elif pg.fillet_vertices_only: pg.command = ( f"fv{str(round(pg.fillet_radius, decimal_places))}" f",{str(round(pg.fillet_segments, decimal_places))}" f",{str(round(pg.fillet_profile, decimal_places))}" ) else: pg.command = ( f"fe{str(round(pg.fillet_radius, decimal_places))}" f",{str(round(pg.fillet_segments, decimal_places))}" f",{str(round(pg.fillet_profile, decimal_places))}" ) return {"FINISHED"} class PDT_OT_Angle2(Operator): """Measure Distance and Angle in Working Plane, Also sets Deltas""" bl_idname = "pdt.angle2" bl_label = "Measure 2D" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Measures Angle and Offsets between 2 Points in View Plane. Note: Uses 2 Selected Vertices to set pg.angle and pg.distance scene variables also sets delta offset from these 2 points using standard Numpy Routines Works in Edit and Object Modes. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg pg.command = f"ad2" return {"FINISHED"} class PDT_OT_Angle3(Operator): """Measure Distance and Angle in 3D Space""" bl_idname = "pdt.angle3" bl_label = "Measure 3D" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Measures Angle and Offsets between 3 Points in World Space, Also sets Deltas. Note: Uses 3 Selected Vertices to set pg.angle and pg.distance scene variables also sets delta offset from these 3 points using standard Numpy Routines Works in Edit and Object Modes. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg pg.command = f"ad3" return {"FINISHED"} class PDT_OT_Origin(Operator): """Move Object Origin to Cursor Location""" bl_idname = "pdt.origin" bl_label = "Move Origin" bl_options = {"REGISTER", "UNDO"} def execute(self, context): """Sets Object Origin in Edit Mode to Cursor Location. Note: Keeps geometry static in World Space whilst moving Object Origin Requires cursor location Works in Edit and Object Modes. Args: context: Blender bpy.context instance. Returns: Status Set. """ pg = context.scene.pdt_pg pg.command = f"otc" return {"FINISHED"} class PDT_OT_Taper(Operator): """Taper Vertices at Angle in Chosen Axis Mode""" bl_idname = "pdt.taper" bl_label = "Taper" bl_options = {"REGISTER", "UNDO"} @classmethod def poll(cls, context): ob = context.object if ob is None: return False return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"]) def execute(self, context): """Taper Geometry along World Axes. Note: Similar to Blender Shear command except that it shears by angle rather than displacement. Rotates about World Axes and displaces along World Axes, angle must not exceed +-80 degrees. Rotation axis is centred on Active Vertex. Works only in Edit mode. Args: context: Blender bpy.context instance. Note: Uses pg.taper & pg.angle scene variables Returns: Status Set. """ pg = context.scene.pdt_pg pg.command = f"tap" return {"FINISHED"} #class PDT_Extrude_Modal(Operator): # """Extrude Modal Plane Along Normal Axis""" # bl_idname = "pdt.extrude_modal" # bl_label = "Extrude Modal Normal" # bl_options = {"REGISTER", "UNDO"}