diff --git a/precision_drawing_tools/__init__.py b/precision_drawing_tools/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..b3c3024dfcf6cc8d3cb060fb0987016992f28f15
--- /dev/null
+++ b/precision_drawing_tools/__init__.py
@@ -0,0 +1,539 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+#
+# <pep8 compliant>
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+# ----------------------------------------------
+# Define Addon info
+# ----------------------------------------------
+#
+bl_info = {
+    "name": "Precision Drawing Tools (PDT)",
+    "author": "Alan Odom (Clockmender), Rune Morling (ermo)",
+    "version": (1, 1, 5),
+    "blender": (2, 80, 0),
+    "location": "View3D > UI > PDT",
+    "description": "Precision Drawing Tools for Acccurate Modelling",
+    "warning": "",
+    "wiki_url": "https://github.com/Clockmender/Precision-Drawing-Tools/wiki",
+    "category": "3D View",
+}
+
+
+# ----------------------------------------------
+# Import modules
+# ----------------------------------------------
+if "bpy" in locals():
+    import importlib
+
+    importlib.reload(pdt_design)
+    importlib.reload(pdt_pivot_point)
+    importlib.reload(pdt_menus)
+    importlib.reload(pdt_library)
+    importlib.reload(pdt_view)
+    importlib.reload(pdt_xall)
+    importlib.reload(pdt_bix)
+    importlib.reload(pdt_etof)
+else:
+    from . import pdt_design
+    from . import pdt_pivot_point
+    from . import pdt_menus
+    from . import pdt_library
+    from . import pdt_view
+    from . import pdt_xall
+    from . import pdt_bix
+    from . import pdt_etof
+
+import bpy
+import os
+from pathlib import Path
+from bpy.types import AddonPreferences, PropertyGroup, Scene, WindowManager
+from bpy.props import (
+    BoolProperty,
+    CollectionProperty,
+    EnumProperty,
+    FloatProperty,
+    FloatVectorProperty,
+    IntProperty,
+    PointerProperty,
+    StringProperty,
+)
+from .pdt_msg_strings import (
+    PDT_DES_COORDS,
+    PDT_DES_FILLETPROF,
+    PDT_DES_FILLETRAD,
+    PDT_DES_FILLETSEG,
+    PDT_DES_FILLETVERTS,
+    PDT_DES_FLIPANG,
+    PDT_DES_FLIPPER,
+    PDT_DES_LIBCOLS,
+    PDT_DES_LIBMATS,
+    PDT_DES_LIBMODE,
+    PDT_DES_LIBOBS,
+    PDT_DES_LIBSER,
+    PDT_DES_MOVESEL,
+    PDT_DES_OBORDER,
+    PDT_DES_OFFANG,
+    PDT_DES_OFFDIS,
+    PDT_DES_OFFPER,
+    PDT_DES_OPMODE,
+    PDT_DES_PIVOTDIS,
+    PDT_DES_PPLOC,
+    PDT_DES_PPSCALEFAC,
+    PDT_DES_PPSIZE,
+    PDT_DES_PPTRANS,
+    PDT_DES_PPWIDTH,
+    PDT_DES_ROTMOVAX,
+    PDT_DES_TRIM,
+    PDT_DES_VALIDLET,
+    PDT_DES_WORPLANE
+)
+from .pdt_command import command_run
+from .pdt_functions import scale_set
+
+
+# Declare enum items variables
+#
+_pdt_obj_items = []
+_pdt_col_items = []
+_pdt_mat_items = []
+
+
+def enumlist_objects(self, context):
+    """Populate Objects List from Parts Library.
+
+    Creates list of objects that optionally have search string contained in them
+    to populate variable pdt_lib_objects enumerator.
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Returns:
+        list of Object Names.
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    file_path = context.preferences.addons[__package__].preferences.pdt_library_path
+    path = Path(file_path)
+    _pdt_obj_items.clear()
+
+    if path.is_file() and ".blend" in str(path):
+        with bpy.data.libraries.load(str(path)) as (data_from, _):
+            if len(pg.object_search_string) == 0:
+                object_names = [ob for ob in data_from.objects]
+            else:
+                object_names = [ob for ob in data_from.objects if pg.object_search_string in ob]
+        for object_name in object_names:
+            _pdt_obj_items.append((object_name, object_name, ""))
+    else:
+        _pdt_obj_items.append(("MISSING", "Library is Missing", ""))
+    return _pdt_obj_items
+
+
+def enumlist_collections(self, context):
+    """Populate Collections List from Parts Library.
+
+    Creates list of collections that optionally have search string contained in them
+    to populate variable pg.lib_collections enumerator
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Returns:
+        list of Collections Names.
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    file_path = context.preferences.addons[__package__].preferences.pdt_library_path
+    path = Path(file_path)
+    _pdt_col_items.clear()
+
+    if path.is_file() and ".blend" in str(path):
+        with bpy.data.libraries.load(str(path)) as (data_from, _):
+            if len(pg.collection_search_string) == 0:
+                object_names = [ob for ob in data_from.collections]
+            else:
+                object_names = [ob for ob in data_from.collections if pg.collection_search_string in ob]
+        for object_name in object_names:
+            _pdt_col_items.append((object_name, object_name, ""))
+    else:
+        _pdt_col_items.append(("MISSING", "Library is Missing", ""))
+    return _pdt_col_items
+
+
+def enumlist_materials(self, context):
+    """Populate Materials List from Parts Library.
+
+    Creates list of materials that optionally have search string contained in them
+    to populate variable pg.lib_materials enumerator.
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Returns:
+        list of Object Names.
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    file_path = context.preferences.addons[__package__].preferences.pdt_library_path
+    path = Path(file_path)
+    _pdt_mat_items.clear()
+
+    if path.is_file() and ".blend" in str(path):
+        with bpy.data.libraries.load(str(path)) as (data_from, _):
+            if len(pg.material_search_string) == 0:
+                object_names = [ob for ob in data_from.materials]
+            else:
+                object_names = [ob for ob in data_from.materials if pg.material_search_string in ob]
+        for object_name in object_names:
+            _pdt_mat_items.append((object_name, object_name, ""))
+    else:
+        _pdt_mat_items.append(("MISSING", "Library is Missing", ""))
+    return _pdt_mat_items
+
+
+class PDTSceneProperties(PropertyGroup):
+    """Contains all PDT related properties."""
+
+    object_search_string : StringProperty(
+        name="Search", default="", description=PDT_DES_LIBSER
+    )
+    collection_search_string : StringProperty(
+        name="Search", default="", description=PDT_DES_LIBSER
+    )
+    material_search_string : StringProperty(
+        name="Search", default="", description=PDT_DES_LIBSER
+    )
+
+    cartesian_coords : FloatVectorProperty(
+        name="Coords",
+        default=(0.0, 0.0, 0.0),
+        subtype="XYZ",
+        description=PDT_DES_COORDS
+    )
+    distance : FloatProperty(
+        name="Distance", default=0.0, precision=5, description=PDT_DES_OFFDIS, unit="LENGTH"
+    )
+    angle : FloatProperty(
+        name="Angle", min=-180, max=180, default=0.0, precision=5, description=PDT_DES_OFFANG
+    )
+    percent : FloatProperty(
+        name="Percent", default=0.0, precision=5, description=PDT_DES_OFFPER
+    )
+    plane : EnumProperty(
+        items=(
+            ("XZ", "Front(X-Z)", "Use X-Z Plane"),
+            ("XY", "Top(X-Y)", "Use X-Y Plane"),
+            ("YZ", "Right(Y-Z)", "Use Y-Z Plane"),
+            ("LO", "View", "Use View Plane"),
+        ),
+        name="Working Plane",
+        default="XZ",
+        description=PDT_DES_WORPLANE,
+    )
+    select : EnumProperty(
+        items=(
+            ("REL", "Current", "Moved Relative to Current Position"),
+            (
+                "SEL",
+                "Selected",
+                "Moved Relative to Selected Object, or Vertex, Cursor & Pivot Only",
+            ),
+        ),
+        name="Move Mode",
+        default="SEL",
+        description=PDT_DES_MOVESEL,
+    )
+    operation : EnumProperty(
+        items=(
+            ("CU", "Cursor", "This Function will Move the Cursor"),
+            ("PP", "Pivot", "This Function will Move the Pivot Point"),
+            ("MV", "Move", "This function will Move Vertices, or Objects"),
+            ("NV", "New Vertex", "This function will Add a New Vertex"),
+            ("EV", "Extrude Vertices", "This function will Extrude Vertices Only in EDIT Mode"),
+            ("SE", "Split Edges", "This function will Split Edges Only in EDIT Mode"),
+            (
+                "DG",
+                "Duplicate Geometry",
+                "This function will Duplicate Geometry in EDIT Mode (Delta & Direction Only)",
+            ),
+            (
+                "EG",
+                "Extrude Geometry",
+                "This function will Extrude Geometry in EDIT Mode (Delta & Direction Only)",
+            ),
+        ),
+        name="Operation",
+        default="CU",
+        description=PDT_DES_OPMODE,
+    )
+    taper : EnumProperty(
+        items=(
+            ("RX-MY", "RotX-MovY", "Rotate X - Move Y"),
+            ("RX-MZ", "RotX-MovZ", "Rotate X - Move Z"),
+            ("RY-MX", "RotY-MovX", "Rotate Y - Move X"),
+            ("RY-MZ", "RotY-MovZ", "Rotate Y - Move Z"),
+            ("RZ-MX", "RotZ-MovX", "Rotate Z - Move X"),
+            ("RZ-MY", "RotZ-MovY", "Rotate Z - Move Y"),
+        ),
+        name="Axes",
+        default="RX-MY",
+        description=PDT_DES_ROTMOVAX,
+    )
+
+    flip_angle : BoolProperty(
+        name="Flip Angle", default=False, description=PDT_DES_FLIPANG
+    )
+    flip_percent : BoolProperty(
+        name="Flip %", default=False, description=PDT_DES_FLIPPER
+    )
+
+    extend : BoolProperty(
+        name="Trim/Extend All", default=False, description=PDT_DES_TRIM
+    )
+
+    lib_objects : EnumProperty(
+        items=enumlist_objects, name="Objects", description=PDT_DES_LIBOBS
+    )
+    lib_collections : EnumProperty(
+        items=enumlist_collections, name="Collections", description=PDT_DES_LIBCOLS
+    )
+    lib_materials : EnumProperty(
+        items=enumlist_materials, name="Materials", description=PDT_DES_LIBMATS
+    )
+    lib_mode : EnumProperty(
+        items=(
+            ("OBJECTS", "Objects", "Use Objects"),
+            ("COLLECTIONS", "Collections", "Use Collections"),
+            ("MATERIALS", "Materials", "Use Materials"),
+        ),
+        name="Mode",
+        default="OBJECTS",
+        description=PDT_DES_LIBMODE,
+    )
+
+    rotation_coords : FloatVectorProperty(
+        name="Rotation",
+        default=(0.0, 0.0, 0.0),
+        subtype="XYZ",
+        description="Rotation Coordinates"
+    )
+
+    object_order : EnumProperty(
+        items=(
+            ("1,2,3,4", "1,2,3,4", "Objects 1 & 2 are First Line"),
+            ("1,3,2,4", "1,3,2,4", "Objects 1 & 3 are First Line"),
+            ("1,4,2,3", "1,4,2,3", "Objects 1 & 4 are First Line"),
+        ),
+        name="Order",
+        default="1,2,3,4",
+        description=PDT_DES_OBORDER,
+    )
+    vrotangle : FloatProperty(name="View Rotate Angle", default=10, max=180, min=-180)
+    command : StringProperty(
+        name="Command",
+        default="CA0,0,0",
+        update=command_run,
+        description=PDT_DES_VALIDLET,
+    )
+    error : StringProperty(name="Error", default="")
+
+    # Was pivot* -- is now pivot_*
+    pivot_loc : FloatVectorProperty(
+        name="Pivot Location",
+        default=(0.0, 0.0, 0.0),
+        subtype="XYZ",
+        description=PDT_DES_PPLOC,
+    )
+    pivot_scale : FloatVectorProperty(
+        name="Pivot Scale", default=(1.0, 1.0, 1.0), subtype="XYZ", description=PDT_DES_PPSCALEFAC
+    )
+    pivot_size : FloatProperty(
+        name="Pivot Factor", min=0.4, max=10, default=1, precision=1, description=PDT_DES_PPSIZE
+    )
+    pivot_width : IntProperty(
+        name="Width", min=1, max=5, default=2, description=PDT_DES_PPWIDTH
+    )
+    # FIXME: might as well become pivot_angle
+    pivot_ang : FloatProperty(name="Pivot Angle", min=-180, max=180, default=0.0)
+    # FIXME: pivot_dist for consistency?
+    pivot_dis : FloatProperty(name="Pivot Dist",
+        default=0.0,
+        min = 0,
+        update=scale_set,
+        description=PDT_DES_PIVOTDIS,
+    )
+    pivot_alpha : FloatProperty(
+        name="Alpha",
+        min=0.2,
+        max=1,
+        default=0.6,
+        precision=1,
+        description=PDT_DES_PPTRANS,
+    )
+    pivot_show : BoolProperty()
+
+    # Was filletrad
+    fillet_radius : FloatProperty(
+        name="Fillet Radius", min=0.0, default=1.0, description=PDT_DES_FILLETRAD
+    )
+    # Was filletnum
+    fillet_segments : IntProperty(
+        name="Fillet Segments", min=1, default=4, description=PDT_DES_FILLETSEG
+    )
+    # Was filletpro
+    fillet_profile : FloatProperty(
+        name="Fillet Profile", min=0.0, max=1.0, default=0.5, description=PDT_DES_FILLETPROF
+    )
+    # Was filletbool
+    fillet_vertices_only : BoolProperty(
+        name="Fillet Vertices Only",
+        default=True,
+        description=PDT_DES_FILLETVERTS,
+    )
+
+
+class PDTPreferences(AddonPreferences):
+    # This must match the addon name, use '__package__' when defining this in a submodule of a python package.
+
+    bl_idname = __name__
+
+    pdt_library_path : StringProperty(
+        name="Parts Library", default="", description="Parts Library File",
+        maxlen=1024, subtype='FILE_PATH'
+    )
+
+    debug : BoolProperty(
+        name="Enable console debug output from PDT scripts", default=False,
+        description="NOTE: Does not enable debugging globally in Blender (only in PDT scripts)"
+    )
+
+    def draw(self, context):
+        layout = self.layout
+
+        box = layout.box()
+        row1 = box.row()
+        row2 = box.row()
+        row1.prop(self, "debug")
+        row2.prop(self, "pdt_library_path")
+
+
+# List of All Classes in the Add-on to register
+#
+# Due to the way PropertyGroups work, this needs to be listed/loaded first
+# (and unloaded last)
+#
+classes = (
+    PDTSceneProperties,
+    PDTPreferences,
+    pdt_bix.PDT_OT_LineOnBisection,
+    pdt_design.PDT_OT_PlacementAbs,
+    pdt_design.PDT_OT_PlacementDelta,
+    pdt_design.PDT_OT_PlacementDis,
+    pdt_design.PDT_OT_PlacementCen,
+    pdt_design.PDT_OT_PlacementPer,
+    pdt_design.PDT_OT_PlacementNormal,
+    pdt_design.PDT_OT_PlacementInt,
+    pdt_design.PDT_OT_JoinVerts,
+    pdt_design.PDT_OT_Angle2,
+    pdt_design.PDT_OT_Angle3,
+    pdt_design.PDT_OT_Origin,
+    pdt_design.PDT_OT_Taper,
+    pdt_design.PDT_OT_Fillet,
+    pdt_etof.PDT_OT_EdgeToFace,
+    pdt_library.PDT_OT_Append,
+    pdt_library.PDT_OT_Link,
+    pdt_library.PDT_OT_LibShow,
+    pdt_menus.PDT_PT_PanelDesign,
+    pdt_menus.PDT_PT_PanelCommandLine,
+    pdt_menus.PDT_PT_PanelViewControl,
+    pdt_menus.PDT_PT_PanelPivotPoint,
+    pdt_menus.PDT_PT_PanelPartsLibrary,
+    pdt_pivot_point.PDT_OT_ModalDrawOperator,
+    pdt_pivot_point.PDT_OT_ViewPlaneRotate,
+    pdt_pivot_point.PDT_OT_ViewPlaneScale,
+    pdt_pivot_point.PDT_OT_PivotToCursor,
+    pdt_pivot_point.PDT_OT_CursorToPivot,
+    pdt_pivot_point.PDT_OT_PivotSelected,
+    pdt_pivot_point.PDT_OT_PivotOrigin,
+    pdt_pivot_point.PDT_OT_PivotWrite,
+    pdt_pivot_point.PDT_OT_PivotRead,
+    pdt_view.PDT_OT_ViewRot,
+    pdt_view.PDT_OT_vRotL,
+    pdt_view.PDT_OT_vRotR,
+    pdt_view.PDT_OT_vRotU,
+    pdt_view.PDT_OT_vRotD,
+    pdt_view.PDT_OT_vRoll,
+    pdt_view.PDT_OT_viso,
+    pdt_xall.PDT_OT_IntersectAllEdges,
+)
+
+
+def register():
+    """Register Classes and Create Scene Variables.
+
+    Operates on the classes list defined above.
+    """
+
+    from bpy.utils import register_class
+
+    for cls in classes:
+        register_class(cls)
+
+    # OpenGL flag
+    #
+    wm = WindowManager
+    # Register Internal OpenGL Property
+    #
+    wm.pdt_run_opengl = BoolProperty(default=False)
+
+    Scene.pdt_pg = PointerProperty(type=PDTSceneProperties)
+
+
+def unregister():
+    """Unregister Classes and Delete Scene Variables.
+
+    Operates on the classes list defined above.
+    """
+
+    from bpy.utils import unregister_class
+
+    # remove OpenGL data
+    pdt_pivot_point.PDT_OT_ModalDrawOperator.handle_remove(
+        pdt_pivot_point.PDT_OT_ModalDrawOperator, bpy.context
+    )
+    wm = bpy.context.window_manager
+    p = "pdt_run_opengl"
+    if p in wm:
+        del wm[p]
+
+    for cls in reversed(classes):
+        unregister_class(cls)
+
+    del Scene.pdt_pg
+
+
+if __name__ == "__main__":
+    register()
diff --git a/precision_drawing_tools/pdt_bix.py b/precision_drawing_tools/pdt_bix.py
new file mode 100644
index 0000000000000000000000000000000000000000..e59c1e08cd515686cefe7286fb9e9ade08c89595
--- /dev/null
+++ b/precision_drawing_tools/pdt_bix.py
@@ -0,0 +1,118 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+#
+# <pep8 compliant>
+#
+# ----------------------------------------------------------
+# Author: Zeffii
+# Modified by: Alan Odom (Clockmender) & Rune Morling (ermo)
+# ----------------------------------------------------------
+#
+#
+import bpy
+import bmesh
+from . import pdt_cad_module as cm
+from .pdt_msg_strings import PDT_ERR_2CPNPE, PDT_ERR_NCEDGES
+from .pdt_functions import debug
+
+def add_line_to_bisection(self, context):
+    """Computes Bisector of 2 Co-Planar Edges.
+
+    Args:
+        context: Blender bpy.context instance
+
+    Returns:
+        Nothing.
+    """
+
+    obj = context.object
+    me = obj.data
+    bm = bmesh.from_edit_mesh(me)
+
+    bm.verts.ensure_lookup_table()
+    bm.edges.ensure_lookup_table()
+
+    edges = [e for e in bm.edges if e.select and not e.hide]
+
+    if not len(edges) == 2:
+        msg = PDT_ERR_2CPNPE
+        self.report({"ERROR"}, msg)
+        return
+
+    [[v1, v2], [v3, v4]] = [[v.co for v in e.verts] for e in edges]
+    debug(f"vectors found:\n {v1}\n {v2}\n {v3}\n {v4}")
+
+    dist1 = (v1 - v2).length
+    dist2 = (v3 - v4).length
+    bdist = min([dist1, dist2])
+    edge1 = (v1, v2)
+    edge2 = (v3, v4)
+
+    if not cm.test_coplanar(edge1, edge2):
+        msg = PDT_ERR_NCEDGES
+        self.report({"ERROR"}, msg)
+        return
+
+    # get pt and pick farthest vertex from (projected) intersections
+    pt = cm.get_intersection(edge1, edge2)
+    far1 = v2 if (v1 - pt).length < (v2 - pt).length else v1
+    far2 = v4 if (v3 - pt).length < (v4 - pt).length else v3
+
+    dex1 = far1 - pt
+    dex2 = far2 - pt
+    dex1 = dex1 * (bdist / dex1.length)
+    dex2 = dex2 * (bdist / dex2.length)
+    pt2 = pt + (dex1).lerp(dex2, 0.5)
+    pt3 = pt2.lerp(pt, 2.0)
+
+    vec1 = bm.verts.new(pt2)
+    vec2 = bm.verts.new(pt)
+    vec3 = bm.verts.new(pt3)
+    bm.edges.new((vec1, vec2))
+    bm.edges.new((vec2, vec3))
+    bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
+    bmesh.update_edit_mesh(me)
+
+
+class PDT_OT_LineOnBisection(bpy.types.Operator):
+    """Create Bisector between 2 Selected Edges."""
+
+    bl_idname = "pdt.linetobisect"
+    bl_label = "Add Edges Bisector"
+    bl_options = {"REGISTER", "UNDO"}
+
+    @classmethod
+    def poll(cls, context):
+        """Only allow operation on a mesh object in EDIT mode."""
+        ob = context.active_object
+        if ob is None:
+            return False
+        return all([ob is not None, ob.type == "MESH", ob.mode == "EDIT"])
+
+    def execute(self, context):
+        """Computes Bisector of 2 Co-Planar Edges.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        add_line_to_bisection(self, context)
+        return {"FINISHED"}
diff --git a/precision_drawing_tools/pdt_cad_module.py b/precision_drawing_tools/pdt_cad_module.py
new file mode 100644
index 0000000000000000000000000000000000000000..21a5c6c700b99b63f153a675c14b6ecca446dfd9
--- /dev/null
+++ b/precision_drawing_tools/pdt_cad_module.py
@@ -0,0 +1,210 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+#
+# <pep8 compliant>
+#
+# ----------------------------------------------------------
+# Author: Zeffii
+# Modified by: Alan Odom (Clockmender) & Rune Morling (ermo)
+# ----------------------------------------------------------
+#
+import bmesh
+from mathutils import Vector
+from mathutils.geometry import intersect_line_line, intersect_point_line
+from .pdt_functions import debug
+
+def point_on_edge(p, edge):
+    """Find Point on Edge.
+
+    Args:
+        p:        vector
+        edge:     tuple containing 2 vectors.
+
+    Returns:
+        True if point p happens to lie on the edge, False otherwise.
+    """
+
+    pt, _percent = intersect_point_line(p, *edge)
+    on_line = (pt - p).length < 1.0e-5
+    return on_line and (0.0 <= _percent <= 1.0)
+
+
+def line_from_edge_intersect(edge1, edge2):
+    """Get New Line from Intersections.
+
+    Prepares input for sending to intersect_line_line
+
+    Args:
+        edge1, edge2: tuples containing 2 vectors.
+
+    Returns:
+        Output of intersect_line_line.
+    """
+
+    [p1, p2], [p3, p4] = edge1, edge2
+    return intersect_line_line(p1, p2, p3, p4)
+
+
+def get_intersection(edge1, edge2):
+    """Get Intersections of 2 Edges.
+
+    Args:
+        edge1, edge2: tuples containing 2 vectors.
+
+    Returns:
+        The point halfway on line. See intersect_line_line.
+    """
+
+    line = line_from_edge_intersect(edge1, edge2)
+    if line:
+        return (line[0] + line[1]) / 2
+
+
+def test_coplanar(edge1, edge2):
+    """Test 2 Edges are Co-planar.
+
+    The line that describes the shortest line between the two edges would be short if the
+    lines intersect mathematically. If this line is longer than 1.0e-5 then they are either
+    coplanar or parallel
+
+    Args:
+        edge1, edge2: tuples containing 2 vectors.
+
+    Returns:
+        True if edge1 and edge2 or coplanar, False otherwise.
+    """
+
+    line = line_from_edge_intersect(edge1, edge2)
+    if line:
+        return (line[0] - line[1]).length < 1.0e-5
+
+
+def closest_idx(pt, e):
+    """Get Closest Vertex to input point.
+
+    If both points in e are equally far from pt, then v1 is returned.
+
+    Args:
+        pt:       vector
+        e:        bmesh edge
+
+    Returns:
+        Index of vertex closest to pt.
+    """
+
+    if isinstance(e, bmesh.types.BMEdge):
+        ev = e.verts
+        v1 = ev[0].co
+        v2 = ev[1].co
+        distance_test = (v1 - pt).length <= (v2 - pt).length
+        return ev[0].index if distance_test else ev[1].index
+
+    debug(f"Received {e}, check expected input in docstring ")
+
+
+def closest_vector(pt, e):
+    """Return Closest Vector to input Point.
+
+    If both points in e are equally far from pt, then v1 is returned.
+
+    Args:
+        pt:       vector
+        e:        tuple containing 2 vectors
+
+    Returns:
+        Vector closest to pt.
+    """
+
+    if isinstance(e, tuple) and all([isinstance(co, Vector) for co in e]):
+        v1, v2 = e
+        distance_test = (v1 - pt).length <= (v2 - pt).length
+        return v1 if distance_test else v2
+
+    debug(f"Received {e}, check expected input in docstring ")
+
+
+def coords_tuple_from_edge_idx(bm, idx):
+    """Return Tuple from Vertex."""
+    return tuple(v.co for v in bm.edges[idx].verts)
+
+
+def vectors_from_indices(bm, raw_vert_indices):
+    """Return List of vectors from input indices."""
+    return [bm.verts[i].co for i in raw_vert_indices]
+
+
+def vertex_indices_from_edges_tuple(bm, edge_tuple):
+    """Return List of vertices.
+
+    Args:
+        bm:           is a bmesh representation
+        edge_tuple:   contains 2 edge indices.
+
+    Returns:
+        The vertex indices of edge_tuple.
+    """
+
+    def k(v, w):
+        return bm.edges[edge_tuple[v]].verts[w].index
+
+    return [k(i >> 1, i % 2) for i in range(4)]
+
+
+def get_vert_indices_from_bmedges(edges):
+    """Return List of Edges for evaluation.
+
+    Args:
+        bmedges:      a list of 2 bm edges
+
+    Returns:
+        The vertex indices of edge_tuple as a flat list.
+    """
+    temp_edges = []
+    debug(edges)
+    for e in edges:
+        for v in e.verts:
+            temp_edges.append(v.index)
+    return temp_edges
+
+
+def num_edges_point_lies_on(pt, edges):
+    """Returns the number of edges that a point lies on."""
+    res = [point_on_edge(pt, edge) for edge in [edges[:2], edges[2:]]]
+    return len([i for i in res if i])
+
+
+def find_intersecting_edges(bm, pt, idx1, idx2):
+    """Find Intercecting Edges.
+
+    Args:
+        pt:           Vector
+        idx1, ix2:    edge indices
+
+    Returns:
+        The list of edge indices where pt is on those edges.
+    """
+    if not pt:
+        return []
+    idxs = [idx1, idx2]
+    edges = [coords_tuple_from_edge_idx(bm, idx) for idx in idxs]
+    return [idx for edge, idx in zip(edges, idxs) if point_on_edge(pt, edge)]
+
+
+def vert_idxs_from_edge_idx(bm, idx):
+    edge = bm.edges[idx]
+    return edge.verts[0].index, edge.verts[1].index
diff --git a/precision_drawing_tools/pdt_command.py b/precision_drawing_tools/pdt_command.py
new file mode 100644
index 0000000000000000000000000000000000000000..423bc83c2a2bc58f45023fc3bb9d1c3773d94ea8
--- /dev/null
+++ b/precision_drawing_tools/pdt_command.py
@@ -0,0 +1,880 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+import bpy
+import bmesh
+from mathutils import Vector
+import math
+from .pdt_functions import (
+    debug,
+    disAng,
+    getPercent,
+    objCheck,
+    oops,
+    updateSel,
+)
+from .pdt_msg_strings import (
+    PDT_ERR_ADDVEDIT,
+    PDT_ERR_BAD1VALS,
+    PDT_ERR_BAD2VALS,
+    PDT_ERR_BAD3VALS,
+    PDT_ERR_BADCOORDL,
+    PDT_ERR_BADFLETTER,
+    PDT_ERR_BADMATHS,
+    PDT_ERR_BADSLETTER,
+    PDT_ERR_CHARS_NUM,
+    PDT_ERR_DUPEDIT,
+    PDT_ERR_EXTEDIT,
+    PDT_ERR_FACE_SEL,
+    PDT_ERR_FILEDIT,
+    PDT_ERR_NOCOMMAS,
+    PDT_ERR_NON_VALID,
+    PDT_ERR_NO_ACT_OBJ,
+    PDT_ERR_NO_SEL_GEOM,
+    PDT_ERR_SEL_1_EDGE,
+    PDT_ERR_SEL_1_EDGEM,
+    PDT_ERR_SEL_1_VERT,
+    PDT_ERR_SPLITEDIT
+)
+
+
+def pdt_help(self, context):
+    """Display PDT Command Line help in a pop-up."""
+    label = self.layout.label
+    label(text="Primary Letters (Available Secondary Letters):")
+    label(text="")
+    label(text="C: Cursor (a, d, i, p)")
+    label(text="D: Duplicate Geometry (d, i)")
+    label(text="E: Extrude Geometry (d, i)")
+    label(text="F: Fillet (v, e)")
+    label(text="G: Grab (Move) (a, d, i, p)")
+    label(text="N: New Vertex (a, d, i, p)")
+    label(text="M: Maths Functions (x, y, z, d, a, p)")
+    label(text="P: Pivot Point (a, d, i, p)")
+    label(text="V: Extrude Vertice Only (a, d, i, p)")
+    label(text="S: Split Edges (a, d, i, p)")
+    label(text="?: Quick Help")
+    label(text="")
+    label(text="Secondary Letters:")
+    label(text="")
+    label(text="- General Options:")
+    label(text="a: Absolute (Global) Coordinates e.g. 1,3,2")
+    label(text="d: Delta (Relative) Coordinates, e.g. 0.5,0,1.2")
+    label(text="i: Directional (Polar) Coordinates e.g. 2.6,45")
+    label(text="p: Percent e.g. 67.5")
+    label(text="- Fillet Options:")
+    label(text="v: Fillet Vertices")
+    label(text="e: Fillet Edges")
+    label(text="- Math Options:")
+    label(text="x, y, z: Send result to X, Y and Z input fields in PDT Design")
+    label(text="d, a, p: Send result to Distance, Angle or Percent input field in PDT Design")
+    label(text="")
+    label(text="Note that commands are case-insensitive: ED = Ed = eD = ed")
+    label(text="")
+    label(text="Examples:")
+    label(text="")
+    label(text="ed0.5,,0.6")
+    label(text="'- Extrude Geometry Delta 0.5 in X, 0 in Y, 0.6 in Z")
+    label(text="")
+    label(text="fe0.1,4,0.5")
+    label(text="'- Fillet Edges")
+    label(text="'- Radius: 0.1 (float) -- the radius (or offset) of the bevel/fillet")
+    label(text="'- Segments: 4 (int) -- choosing an even amount of segments gives better geometry")
+    label(text="'- Profile: 0.5 (float[0.0;1.0]) -- 0.5 (default) yields a circular, convex shape")
+
+def command_run(self, context):
+    """Run Command String as input into Command Line.
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Note:
+        Uses pg.command, pg.error & many other 'pg.' variables to set PDT menu items,
+        or alter functions
+
+        Command Format; Operation(single letter) Mode(single letter) Values(up to 3 values
+        separated by commas)
+
+        Example; CD0.4,0.6,1.1 - Moves Cursor Delta XYZ = 0.4,0.6,1.1 from Current Position/Active
+        Vertex/Object Origin
+
+        Example; SP35 - Splits active Edge at 35% of separation between edge's vertices
+
+        Valid First Letters (as 'oper' - pg.command[0])
+            C = Cursor, G = Grab(move), N = New Vertex, V = Extrude Vertices Only,
+            E = Extrude geometry, P = Move Pivot Point, D = Duplicate geometry, S = Split Edges
+
+            Capitals and lower case letters are both allowed
+
+        Valid Second Letters (as 'mode' - pg.command[1])
+
+            A = Absolute XYZ, D = Delta XYZ, I = Distance at Angle, P = Percent
+            X = X Delta, Y = Y, Delta Z, = Z Delta (Maths Operation only)
+            V = Vertex Bevel, E = Edge Bevel
+
+            Capitals and lower case letters are both allowed
+
+        Valid Values (pdt_command[2:])
+            Only Integers and Floats, missing values are set to 0, appropriate length checks are
+            performed as Values is split by commas.
+
+            Example; CA,,3 - Cursor to Absolute, is re-interpreted as CA0,0,3
+
+            Exception for Maths Operation, Values section is evaluated as Maths command
+
+            Example; madegrees(atan(3/4)) - sets PDT Angle to smallest angle of 3,4,5 Triangle;
+            (36.8699 degrees)
+
+            This is why all Math functions are imported
+
+    Returns:
+        Nothing.
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    cmd = pg.command
+
+    if cmd.strip() == "?" or cmd.lower().strip() == "help":
+        # fmt: off
+        context.window_manager.popup_menu(pdt_help, title="PDT Command Line Help", icon="INFO")
+        # fmt: on
+        return
+    if len(cmd) < 3:
+        pg.error = PDT_ERR_CHARS_NUM
+        context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+        return
+    oper = cmd[0].upper()
+    if oper not in {"C", "D", "E", "F", "G", "N", "M", "P", "V", "S"}:
+        pg.error = PDT_ERR_BADFLETTER
+        context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+        return
+    mode = cmd[1].lower()
+    if mode not in {"a", "d", "e", "g", "i", "p", "v", "x", "y", "z"}:
+        pg.error = PDT_ERR_BADSLETTER
+        context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+        return
+
+    # --------------
+    # Math Operation
+    if oper == "M":
+        if mode not in {"x", "y", "z", "d", "a", "p"}:
+            pg.error = f"{mode} {PDT_ERR_NON_VALID} Maths)"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        exp = cmd[2:]
+        namespace = {}
+        namespace.update(vars(math))
+        if "," in exp:
+            pg.error = PDT_ERR_NOCOMMAS
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+        try:
+            num = eval(exp, namespace, namespace)
+        except ValueError:
+            pg.error = PDT_ERR_BADMATHS
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if mode == "x":
+            pg.cartesian_coords.x = num
+        elif mode == "y":
+            pg.cartesian_coords.y = num
+        elif mode == "z":
+            pg.cartesian_coords.z = num
+        elif mode == "d":
+            pg.distance = num
+        elif mode == "a":
+            pg.angle = num
+        elif mode == "p":
+            pg.percent = num
+        return
+    # "x"/"y"/"z" modes are only legal for Math Operation
+    else:
+        if mode in {"x", "y", "z"}:
+            pg.error = PDT_ERR_BADCOORDL
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+
+    # -----------------------------------------------------
+    # Not a Math Operation, so let's parse the command line
+    vals = cmd[2:].split(",")
+    ind = 0
+    for r in vals:
+        try:
+            _ = float(r)
+            good = True
+        except ValueError:
+            vals[ind] = "0"
+        ind = ind + 1
+    mode_s = pg.select
+    flip_a = pg.flip_angle
+    flip_p = pg.flip_percent
+    ext_a = pg.extend
+    plane = pg.plane
+    obj = context.view_layer.objects.active
+    #FIXME: What does call this imply in terms of invariants?
+    #       There's a lot of places in the code where we rely on bm not being None...
+    bm, good = objCheck(obj, scene, oper)
+    obj_loc = None
+    if good:
+        obj_loc = obj.matrix_world.decompose()[0]
+    debug(f"cmd: {cmd}")
+    debug(f"obj: {obj}, bm: {bm}, obj_loc: {obj_loc}")
+
+    # static set variable for use in recurring comparisons
+    adip = {"a", "d", "i", "p"}
+
+    # ---------------------
+    # Cursor or Pivot Point
+    if oper in {"C", "P"}:
+        if mode not in adip:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Absolute/Global Coordinates
+        if mode == "a":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            if oper == "C":
+                scene.cursor.location = vector_delta
+            else:
+                pg.pivot_loc = vector_delta
+        # Delta/Relative Coordinates
+        elif mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            if mode_s == "REL":
+                if oper == "C":
+                    scene.cursor.location = scene.cursor.location + vector_delta
+                else:
+                    pg.pivot_loc = pg.pivot_loc + vector_delta
+            elif mode_s == "SEL":
+                if obj.mode == "EDIT":
+                    if oper == "C":
+                        scene.cursor.location = (
+                            bm.select_history[-1].co + obj_loc + vector_delta
+                        )
+                    else:
+                        pg.pivot_loc = bm.select_history[-1].co + obj_loc + vector_delta
+                elif obj.mode == "OBJECT":
+                    if oper == "C":
+                        scene.cursor.location = obj_loc + vector_delta
+                    else:
+                        pg.pivot_loc = obj_loc + vector_delta
+        # Direction/Polar Coordinates
+        elif mode == "i":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            if mode_s == "REL":
+                if oper == "C":
+                    scene.cursor.location = scene.cursor.location + vector_delta
+                else:
+                    pg.pivot_loc = pg.pivot_loc + vector_delta
+            elif mode_s == "SEL":
+                if obj.mode == "EDIT":
+                    if oper == "C":
+                        scene.cursor.location = (
+                            bm.select_history[-1].co + obj_loc + vector_delta
+                        )
+                    else:
+                        pg.pivot_loc = bm.select_history[-1].co + obj_loc + vector_delta
+                elif obj.mode == "OBJECT":
+                    if oper == "C":
+                        scene.cursor.location = obj_loc + vector_delta
+                    else:
+                        pg.pivot_loc = obj_loc + vector_delta
+        # Percent Options
+        elif mode == "p":
+            if len(vals) != 1:
+                pg.error = PDT_ERR_BAD1VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = getPercent(obj, flip_p, float(vals[0]), oper, scene)
+            if vector_delta is None:
+                return
+            if obj.mode == "EDIT":
+                if oper == "C":
+                    scene.cursor.location = obj_loc + vector_delta
+                else:
+                    pg.pivot_loc = obj_loc + vector_delta
+            elif obj.mode == "OBJECT":
+                if oper == "C":
+                    scene.cursor.location = vector_delta
+                else:
+                    pg.pivot_loc = vector_delta
+        return
+
+    # ------------------------
+    # Move Vertices or Objects
+    elif oper == "G":
+        if mode not in adip:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Absolute/Global Coordinates
+        if mode == "a":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            if obj.mode == "EDIT":
+                verts = [v for v in bm.verts if v.select]
+                if len(verts) == 0:
+                    pg.error = PDT_ERR_NO_SEL_GEOM
+                    context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                    return
+                for v in verts:
+                    v.co = vector_delta - obj_loc
+                bmesh.ops.remove_doubles(
+                    bm, verts=[v for v in bm.verts if v.select], dist=0.0001
+                )
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif obj.mode == "OBJECT":
+                for ob in context.view_layer.objects.selected:
+                    ob.location = vector_delta
+        # Delta/Relative Coordinates
+        elif mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            if obj.mode == "EDIT":
+                # FIXME: Show error popup if nothing is selected?
+                bmesh.ops.translate(
+                    bm, verts=[v for v in bm.verts if v.select], vec=vector_delta
+                )
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif obj.mode == "OBJECT":
+                for ob in context.view_layer.objects.selected:
+                    ob.location = obj_loc + vector_delta
+        # Direction/Polar Coordinates
+        elif mode == "i":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            if obj.mode == "EDIT":
+                verts = [v for v in bm.verts if v.select]
+                if len(verts) == 0:
+                    pg.error = PDT_ERR_NO_SEL_GEOM
+                    context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                    return
+                bmesh.ops.translate(bm, verts=verts, vec=vector_delta)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif obj.mode == "OBJECT":
+                for ob in context.view_layer.objects.selected:
+                    ob.location = ob.location + vector_delta
+        # Percent Options
+        elif mode == "p":
+            if obj.mode == "OBJECT":
+                if len(vals) != 1:
+                    pg.error = PDT_ERR_BAD1VALS
+                    context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                    return
+                vector_delta = getPercent(obj, flip_p, float(vals[0]), oper, scene)
+                if vector_delta is None:
+                    return
+                ob.location = vector_delta
+        return
+
+    # --------------
+    # Add New Vertex
+    elif oper == "N":
+        if mode not in adip:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if not obj.mode == "EDIT":
+            pg.error = PDT_ERR_ADDVEDIT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Absolute/Global Coordinates
+        if mode == "a":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            vNew = vector_delta - obj_loc
+            nVert = bm.verts.new(vNew)
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Delta/Relative Coordinates
+        elif mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            vNew = bm.select_history[-1].co + vector_delta
+            nVert = bm.verts.new(vNew)
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Direction/Polar Coordinates
+        elif mode == "d":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            vNew = bm.select_history[-1].co + vector_delta
+            nVert = bm.verts.new(vNew)
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Percent Options
+        elif mode == "p":
+            if len(vals) != 1:
+                pg.error = PDT_ERR_BAD1VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = getPercent(obj, flip_p, float(vals[0]), oper, scene)
+            vNew = vector_delta
+            nVert = bm.verts.new(vNew)
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        return
+
+    # -----------
+    # Split Edges
+    elif oper == "S":
+        if mode not in adip:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if not obj.mode == "EDIT":
+            pg.error = PDT_ERR_SPLITEDIT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Absolute/Global Coordinates
+        if mode == "a":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            edges = [e for e in bm.edges if e.select]
+            if len(edges) != 1:
+                pg.error = f"{PDT_ERR_SEL_1_EDGE} {len(edges)})"
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+            new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+            nVert = new_verts[0]
+            nVert.co = vector_delta - obj_loc
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Delta/Relative Coordinates
+        elif mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            edges = [e for e in bm.edges if e.select]
+            faces = [f for f in bm.faces if f.select]
+            if len(faces) != 0:
+                pg.error = PDT_ERR_FACE_SEL
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            if len(edges) < 1:
+                pg.error = f"{PDT_ERR_SEL_1_EDGEM} {len(edges)})"
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+            new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            for v in new_verts:
+                v.select_set(False)
+            bmesh.ops.translate(bm, verts=new_verts, vec=vector_delta)
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            for v in new_verts:
+                v.select_set(False)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Directional/Polar Coordinates
+        elif mode == "i":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            edges = [e for e in bm.edges if e.select]
+            faces = [f for f in bm.faces if f.select]
+            if len(faces) != 0:
+                pg.error = PDT_ERR_FACE_SEL
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            if len(edges) < 1:
+                pg.error = f"{PDT_ERR_SEL_1_EDGEM} {len(edges)})"
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+            new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+            bmesh.ops.translate(bm, verts=new_verts, vec=vector_delta)
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            for v in new_verts:
+                v.select_set(False)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Percent Options
+        elif mode == "p":
+            if len(vals) != 1:
+                pg.error = PDT_ERR_BAD1VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = getPercent(obj, flip_p, float(vals[0]), oper, scene)
+            if vector_delta is None:
+                return
+            edges = [e for e in bm.edges if e.select]
+            faces = [f for f in bm.faces if f.select]
+            if len(faces) != 0:
+                pg.error = PDT_ERR_FACE_SEL
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            if len(edges) < 1:
+                pg.error = f"{PDT_ERR_SEL_1_EDGEM} {len(edges)})"
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+            new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+            nVert = new_verts[0]
+            nVert.co = vector_delta
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        return
+
+    # ----------------
+    # Extrude Vertices
+    elif oper == "V":
+        if mode not in adip:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if not obj.mode == "EDIT":
+            pg.error = PDT_ERR_EXTEDIT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Absolute/Global Coordinates
+        if mode == "a":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            vNew = vector_delta - obj_loc
+            nVert = bm.verts.new(vNew)
+            for v in [v for v in bm.verts if v.select]:
+                bm.edges.new([v, nVert])
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.ops.remove_doubles(
+                bm, verts=[v for v in bm.verts if v.select], dist=0.0001
+            )
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Delta/Relative Coordinates
+        elif mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            for v in verts:
+                nVert = bm.verts.new(v.co)
+                nVert.co = nVert.co + vector_delta
+                bm.edges.new([v, nVert])
+                v.select_set(False)
+                nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Direction/Polar Coordinates
+        elif mode == "i":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            for v in verts:
+                nVert = bm.verts.new(v.co)
+                nVert.co = nVert.co + vector_delta
+                bm.edges.new([v, nVert])
+                v.select_set(False)
+                nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Percent Options
+        elif mode == "p":
+            vector_delta = getPercent(obj, flip_p, float(vals[0]), oper, scene)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            nVert = bm.verts.new(vector_delta)
+            if ext_a:
+                for v in [v for v in bm.verts if v.select]:
+                    bm.edges.new([v, nVert])
+                    v.select_set(False)
+            else:
+                bm.edges.new([bm.select_history[-1], nVert])
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        return
+
+    # ----------------
+    # Extrude Geometry
+    elif oper == "E":
+        if mode not in {"d", "i"}:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if not obj.mode == "EDIT":
+            pg.error = PDT_ERR_EXTEDIT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Delta/Relative Coordinates
+        if mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            ret = bmesh.ops.extrude_face_region(
+                bm,
+                geom=(
+                    [f for f in bm.faces if f.select]
+                    + [e for e in bm.edges if e.select]
+                    + [v for v in bm.verts if v.select]
+                ),
+                use_select_history=True,
+            )
+            geom_extr = ret["geom"]
+            verts_extr = [v for v in geom_extr if isinstance(v, bmesh.types.BMVert)]
+            edges_extr = [e for e in geom_extr if isinstance(e, bmesh.types.BMEdge)]
+            faces_extr = [f for f in geom_extr if isinstance(f, bmesh.types.BMFace)]
+            del ret
+            bmesh.ops.translate(bm, verts=verts_extr, vec=vector_delta)
+            updateSel(bm, verts_extr, edges_extr, faces_extr)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Direction/Polar Coordinates
+        elif mode == "i":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            ret = bmesh.ops.extrude_face_region(
+                bm,
+                geom=(
+                    [f for f in bm.faces if f.select]
+                    + [e for e in bm.edges if e.select]
+                    + [v for v in bm.verts if v.select]
+                ),
+                use_select_history=True,
+            )
+            geom_extr = ret["geom"]
+            verts_extr = [v for v in geom_extr if isinstance(v, bmesh.types.BMVert)]
+            edges_extr = [e for e in geom_extr if isinstance(e, bmesh.types.BMEdge)]
+            faces_extr = [f for f in geom_extr if isinstance(f, bmesh.types.BMFace)]
+            del ret
+            bmesh.ops.translate(bm, verts=verts_extr, vec=vector_delta)
+            updateSel(bm, verts_extr, edges_extr, faces_extr)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        return
+
+    # ------------------
+    # Duplicate Geometry
+    elif oper == "D":
+        if mode not in {"d", "i"}:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if not obj.mode == "EDIT":
+            pg.error = PDT_ERR_DUPEDIT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Delta/Relative Coordinates
+        if mode == "d":
+            if len(vals) != 3:
+                pg.error = PDT_ERR_BAD3VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = Vector((float(vals[0]), float(vals[1]), float(vals[2])))
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            ret = bmesh.ops.duplicate(
+                bm,
+                geom=(
+                    [f for f in bm.faces if f.select]
+                    + [e for e in bm.edges if e.select]
+                    + [v for v in bm.verts if v.select]
+                ),
+                use_select_history=True,
+            )
+            geom_dupe = ret["geom"]
+            verts_dupe = [v for v in geom_dupe if isinstance(v, bmesh.types.BMVert)]
+            edges_dupe = [e for e in geom_dupe if isinstance(e, bmesh.types.BMEdge)]
+            faces_dupe = [f for f in geom_dupe if isinstance(f, bmesh.types.BMFace)]
+            del ret
+            bmesh.ops.translate(bm, verts=verts_dupe, vec=vector_delta)
+            updateSel(bm, verts_dupe, edges_dupe, faces_dupe)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        # Direction/Polar Coordinates
+        elif mode == "i":
+            if len(vals) != 2:
+                pg.error = PDT_ERR_BAD2VALS
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            vector_delta = disAng(vals, flip_a, plane, scene)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 0:
+                pg.error = PDT_ERR_NO_SEL_GEOM
+                context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return
+            ret = bmesh.ops.duplicate(
+                bm,
+                geom=(
+                    [f for f in bm.faces if f.select]
+                    + [e for e in bm.edges if e.select]
+                    + [v for v in bm.verts if v.select]
+                ),
+                use_select_history=True,
+            )
+            geom_dupe = ret["geom"]
+            verts_dupe = [v for v in geom_dupe if isinstance(v, bmesh.types.BMVert)]
+            edges_dupe = [e for e in geom_dupe if isinstance(e, bmesh.types.BMEdge)]
+            faces_dupe = [f for f in geom_dupe if isinstance(f, bmesh.types.BMFace)]
+            del ret
+            bmesh.ops.translate(bm, verts=verts_dupe, vec=vector_delta)
+            updateSel(bm, verts_dupe, edges_dupe, faces_dupe)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        return
+
+    # ---------------
+    # Fillet Geometry
+    elif oper == "F":
+        if mode not in {"v", "e"}:
+            pg.error = f"'{mode}' {PDT_ERR_NON_VALID} '{oper}'"
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if obj is None:
+            pg.error = PDT_ERR_NO_ACT_OBJ
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if not obj.mode == "EDIT":
+            pg.error = PDT_ERR_FILEDIT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if len(vals) != 3:
+            pg.error = PDT_ERR_BAD3VALS
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        if mode == "v":
+            vert_bool = True
+        elif mode == "e":
+            vert_bool = False
+        verts = [v for v in bm.verts if v.select]
+        if len(verts) == 0:
+            pg.error = PDT_ERR_SEL_1_VERT
+            context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return
+        # Note that passing an empty parameter results in that parameter being seen as "0"
+        # _offset <= 0 is ignored since a bevel/fillet radius must be > 0 to make sense
+        _offset = float(vals[0])
+        _segments = int(vals[1])
+        if _segments < 1:
+            _segments = 1   # This is a single, flat segment (ignores profile)
+        _profile = float(vals[2])
+        if _profile < 0.0 or _profile > 1.0:
+            _profile = 0.5  # This is a circular profile
+        bpy.ops.mesh.bevel(
+            offset_type="OFFSET",
+            offset=_offset,
+            segments=_segments,
+            profile=_profile,
+            vertex_only=vert_bool
+        )
+        return
diff --git a/precision_drawing_tools/pdt_design.py b/precision_drawing_tools/pdt_design.py
new file mode 100644
index 0000000000000000000000000000000000000000..ddde12310495d791d306db3bc688f91d958b70db
--- /dev/null
+++ b/precision_drawing_tools/pdt_design.py
@@ -0,0 +1,1478 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+import bmesh
+import bpy
+import numpy as np
+from bpy.types import Operator
+from mathutils import Vector
+from mathutils.geometry import intersect_point_line
+from math import sin, cos, tan, pi, sqrt
+from .pdt_functions import (
+    setMode,
+    checkSelection,
+    setAxis,
+    updateSel,
+    viewCoords,
+    viewCoordsI,
+    viewDir,
+    arcCentre,
+    intersection,
+    getPercent,
+)
+from .pdt_msg_strings import (
+    PDT_ERR_CONNECTED,
+    PDT_ERR_EDIT_MODE,
+    PDT_ERR_EDOB_MODE,
+    PDT_ERR_FACE_SEL,
+    PDT_ERR_INT_LINES,
+    PDT_ERR_INT_NO_ALL,
+    PDT_ERR_NON_VALID,
+    PDT_ERR_NO_ACT_OBJ,
+    PDT_ERR_NO_ACT_VERTS,
+    PDT_ERR_SEL_1_EDGE,
+    PDT_ERR_SEL_1_VERT,
+    PDT_ERR_SEL_1_VERTI,
+    PDT_ERR_SEL_2_OBJS,
+    PDT_ERR_SEL_2_VERTIO,
+    PDT_ERR_SEL_2_VERTS,
+    PDT_ERR_SEL_3_OBJS,
+    PDT_ERR_SEL_3_VERTIO,
+    PDT_ERR_SEL_3_VERTS,
+    PDT_ERR_SEL_4_OBJS,
+    PDT_ERR_SEL_4_VERTS,
+    PDT_ERR_STRIGHT_LINE,
+    PDT_ERR_TAPER_ANG,
+    PDT_ERR_TAPER_SEL,
+    PDT_ERR_VERT_MODE,
+    PDT_INF_OBJ_MOVED,
+    PDT_LAB_ABS,
+    PDT_LAB_ARCCENTRE,
+    PDT_LAB_DEL,
+    PDT_LAB_DIR,
+    PDT_LAB_INTERSECT,
+    PDT_LAB_NOR,
+    PDT_LAB_PERCENT,
+    PDT_LAB_PLANE
+)
+
+
+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.
+
+        - Reads pg.operate from Operation Mode Selector as 'data'
+        - Reads pg.cartesian_coords scene variables to:
+        -- set position of CUrsor      (CU)
+        -- set postion 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.
+
+        Local vector variable 'vector_delta' is used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        oper = pg.operation
+
+        vector_delta = pg.cartesian_coords
+        if oper not in {"CU", "PP", "NV"}:
+            obj = context.view_layer.objects.active
+            if obj is None:
+                errmsg = PDT_ERR_NO_ACT_OBJ
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            obj_loc = obj.matrix_world.decompose()[0]
+            if obj.mode == "EDIT":
+                bm = bmesh.from_edit_mesh(obj.data)
+                verts = [v for v in bm.verts if v.select]
+                if len(verts) == 0:
+                    errmsg = PDT_ERR_NO_ACT_VERTS
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+        if oper == "CU":
+            scene.cursor.location = vector_delta
+            scene.cursor.rotation_euler = (0, 0, 0)
+        elif oper == "PP":
+            pg.pivot_loc = vector_delta
+        elif oper == "MV":
+            if obj.mode == "EDIT":
+                for v in verts:
+                    v.co = vector_delta - obj_loc
+                bm.select_history.clear()
+                bmesh.ops.remove_doubles(bm, verts=[v for v in bm.verts if v.select], dist=0.0001)
+                bmesh.update_edit_mesh(obj.data)
+            elif obj.mode == "OBJECT":
+                for ob in context.view_layer.objects.selected:
+                    ob.location = vector_delta
+        elif oper == "SE" and obj.mode == "EDIT":
+            edges = [e for e in bm.edges if e.select]
+            if len(edges) != 1:
+                errmsg = f"{PDT_ERR_SEL_1_EDGE} {len(edges)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+            new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+            nVert = new_verts[0]
+            nVert.co = vector_delta - obj_loc
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        elif oper == "NV":
+            obj = context.view_layer.objects.active
+            if obj is None:
+                errmsg = PDT_ERR_NO_ACT_OBJ
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            if obj.mode == "EDIT":
+                bm = bmesh.from_edit_mesh(obj.data)
+                vNew = vector_delta - obj.location
+                nVert = bm.verts.new(vNew)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+                nVert.select_set(True)
+            else:
+                errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        elif oper == "EV" and obj.mode == "EDIT":
+            vNew = vector_delta - obj_loc
+            nVert = bm.verts.new(vNew)
+            for v in [v for v in bm.verts if v.select]:
+                bm.edges.new([v, nVert])
+                v.select_set(False)
+            nVert.select_set(True)
+            bm.select_history.clear()
+            bmesh.ops.remove_doubles(bm, verts=[v for v in bm.verts if v.select], dist=0.0001)
+            bmesh.update_edit_mesh(obj.data)
+        else:
+            errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_ABS}"
+            self.report({"ERROR"}, errmsg)
+        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).
+
+        - Reads pg.operation from Operation Mode Selector as 'oper'
+        - 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.
+
+        Local vector variable 'vector_delta' used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        x_loc = pg.cartesian_coords.x
+        y_loc = pg.cartesian_coords.y
+        z_loc = pg.cartesian_coords.z
+        mode_s = pg.select
+        oper = pg.operation
+
+        if pg.plane == "LO":
+            vector_delta = viewCoords(x_loc, y_loc, z_loc)
+        else:
+            vector_delta = Vector((x_loc, y_loc, z_loc))
+        if mode_s == "REL" and oper == "CU":
+            scene.cursor.location = scene.cursor.location + vector_delta
+        elif mode_s == "REL" and oper == "PP":
+            pg.pivot_loc = pg.pivot_loc + vector_delta
+        else:
+            obj = context.view_layer.objects.active
+            if obj is None:
+                errmsg = PDT_ERR_NO_ACT_OBJ
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            obj_loc = obj.matrix_world.decompose()[0]
+            if obj.mode == "EDIT":
+                bm = bmesh.from_edit_mesh(obj.data)
+                if oper not in {"MV", "SE", "EV", "DG", "EG"}:
+                    if len(bm.select_history) >= 1:
+                        actV = checkSelection(1, bm, obj)
+                        if actV is None:
+                            errmsg = PDT_ERR_VERT_MODE
+                            self.report({"ERROR"}, errmsg)
+                            return {"FINISHED"}
+                    else:
+                        errmsg = f"{PDT_ERR_SEL_1_VERTI} {len(bm.select_history)})"
+                        self.report({"ERROR"}, errmsg)
+                        return {"FINISHED"}
+            if oper not in {"CU", "PP", "NV"} and obj.mode == "EDIT":
+                verts = [v for v in bm.verts if v.select]
+                if len(verts) == 0:
+                    errmsg = PDT_ERR_NO_ACT_VERTS
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            if oper == "CU":
+                if obj.mode == "EDIT":
+                    scene.cursor.location = obj_loc + actV + vector_delta
+                elif obj.mode == "OBJECT":
+                    scene.cursor.location = obj_loc + vector_delta
+            elif oper == "PP":
+                if obj.mode == "EDIT":
+                    pg.pivot_loc = obj_loc + actV + vector_delta
+                elif obj.mode == "OBJECT":
+                    pg.pivot_loc = obj_loc + vector_delta
+            elif oper == "MV":
+                if obj.mode == "EDIT":
+                    bmesh.ops.translate(bm, verts=verts, vec=vector_delta)
+                    bmesh.update_edit_mesh(obj.data)
+                    bm.select_history.clear()
+                elif obj.mode == "OBJECT":
+                    for ob in context.view_layer.objects.selected:
+                        ob.location = obj_loc + vector_delta
+            elif oper == "SE" and obj.mode == "EDIT":
+                edges = [e for e in bm.edges if e.select]
+                faces = [f for f in bm.faces if f.select]
+                if len(faces) != 0:
+                    errmsg = PDT_ERR_FACE_SEL
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+                if len(edges) < 1:
+                    errmsg = f"{PDT_ERR_SEL_1_EDGE} {len(edges)})"
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+                geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+                new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+                bmesh.ops.translate(bm, verts=new_verts, vec=vector_delta)
+                for v in [v for v in bm.verts if v.select]:
+                    v.select_set(False)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper == "NV":
+                if obj.mode == "EDIT":
+                    vNew = actV + vector_delta
+                    nVert = bm.verts.new(vNew)
+                    bmesh.update_edit_mesh(obj.data)
+                    bm.select_history.clear()
+                    for v in [v for v in bm.verts if v.select]:
+                        v.select_set(False)
+                    nVert.select_set(True)
+                else:
+                    errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            elif oper == "EV" and obj.mode == "EDIT":
+                for v in [v for v in bm.verts if v.select]:
+                    nVert = bm.verts.new(v.co)
+                    nVert.co = nVert.co + vector_delta
+                    bm.edges.new([v, nVert])
+                    v.select_set(False)
+                    nVert.select_set(True)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper == "DG" and obj.mode == "EDIT":
+                ret = bmesh.ops.duplicate(
+                    bm,
+                    geom=(
+                        [f for f in bm.faces if f.select]
+                        + [e for e in bm.edges if e.select]
+                        + [v for v in bm.verts if v.select]
+                    ),
+                    use_select_history=True,
+                )
+                geom_dupe = ret["geom"]
+                verts_dupe = [v for v in geom_dupe if isinstance(v, bmesh.types.BMVert)]
+                edges_dupe = [e for e in geom_dupe if isinstance(e, bmesh.types.BMEdge)]
+                faces_dupe = [f for f in geom_dupe if isinstance(f, bmesh.types.BMFace)]
+                del ret
+                bmesh.ops.translate(bm, verts=verts_dupe, vec=vector_delta)
+                updateSel(bm, verts_dupe, edges_dupe, faces_dupe)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper == "EG" and obj.mode == "EDIT":
+                ret = bmesh.ops.extrude_face_region(
+                    bm,
+                    geom=(
+                        [f for f in bm.faces if f.select]
+                        + [e for e in bm.edges if e.select]
+                        + [v for v in bm.verts if v.select]
+                    ),
+                    use_select_history=True,
+                )
+                geom_extr = ret["geom"]
+                verts_extr = [v for v in geom_extr if isinstance(v, bmesh.types.BMVert)]
+                edges_extr = [e for e in geom_extr if isinstance(e, bmesh.types.BMEdge)]
+                faces_extr = [f for f in geom_extr if isinstance(f, bmesh.types.BMFace)]
+                del ret
+                bmesh.ops.translate(bm, verts=verts_extr, vec=vector_delta)
+                updateSel(bm, verts_extr, edges_extr, faces_extr)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            else:
+                errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_DEL}"
+                self.report({"ERROR"}, errmsg)
+        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).
+
+        - Reads pg.operation from Operation Mode Selector as 'oper'
+        - 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.
+
+        Local vector variable 'vector_delta' used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        dis_v = pg.distance
+        ang_v = pg.angle
+        plane = pg.plane
+        mode_s = pg.select
+        oper = pg.operation
+        flip_a = pg.flip_angle
+        if flip_a:
+            if ang_v > 0:
+                ang_v = ang_v - 180
+            else:
+                ang_v = ang_v + 180
+            pg.angle = ang_v
+        if plane == "LO":
+            vector_delta = viewDir(dis_v, ang_v)
+        else:
+            a1, a2, _ = setMode(plane)
+            vector_delta = Vector((0, 0, 0))
+            vector_delta[a1] = vector_delta[a1] + (dis_v * cos(ang_v * pi / 180))
+            vector_delta[a2] = vector_delta[a2] + (dis_v * sin(ang_v * pi / 180))
+        if mode_s == "REL" and oper == "CU":
+            scene.cursor.location = scene.cursor.location + vector_delta
+        elif mode_s == "REL" and oper == "PP":
+            pg.pivot_loc = pg.pivot_loc + vector_delta
+        else:
+            obj = context.view_layer.objects.active
+            if obj is None:
+                errmsg = PDT_ERR_NO_ACT_OBJ
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            obj_loc = obj.matrix_world.decompose()[0]
+            if obj.mode == "EDIT":
+                bm = bmesh.from_edit_mesh(obj.data)
+                if oper not in {"MV", "SE", "EV", "DG", "EG"}:
+                    if len(bm.select_history) >= 1:
+                        actV = checkSelection(1, bm, obj)
+                        if actV is None:
+                            errmsg = PDT_ERR_VERT_MODE
+                            self.report({"ERROR"}, errmsg)
+                            return {"FINISHED"}
+                    else:
+                        errmsg = f"{PDT_ERR_SEL_1_VERTI} {len(bm.select_history)})"
+                        self.report({"ERROR"}, errmsg)
+                        return {"FINISHED"}
+            if oper not in {"CU", "PP", "NV"} and obj.mode == "EDIT":
+                verts = [v for v in bm.verts if v.select]
+                if len(verts) == 0:
+                    errmsg = PDT_ERR_NO_ACT_VERTS
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            if oper == "CU":
+                if obj.mode == "EDIT":
+                    scene.cursor.location = obj_loc + actV + vector_delta
+                elif obj.mode == "OBJECT":
+                    scene.cursor.location = obj_loc + vector_delta
+            elif oper == "PP":
+                if obj.mode == "EDIT":
+                    pg.pivot_loc = obj_loc + actV + vector_delta
+                elif obj.mode == "OBJECT":
+                    pg.pivot_loc = obj_loc + vector_delta
+            elif oper == "MV":
+                if obj.mode == "EDIT":
+                    bmesh.ops.translate(bm, verts=verts, vec=vector_delta)
+                    bmesh.update_edit_mesh(obj.data)
+                    bm.select_history.clear()
+                elif obj.mode == "OBJECT":
+                    for ob in context.view_layer.objects.selected:
+                        ob.location = ob.location + vector_delta
+            elif oper == "SE" and obj.mode == "EDIT":
+                edges = [e for e in bm.edges if e.select]
+                faces = [f for f in bm.faces if f.select]
+                if len(faces) != 0:
+                    errmsg = PDT_ERR_FACE_SEL
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+                if len(edges) < 1:
+                    errmsg = f"{PDT_ERR_SEL_1_EDGE} {len(edges)})"
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+                geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+                new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+                bmesh.ops.translate(bm, verts=new_verts, vec=vector_delta)
+                for v in [v for v in bm.verts if v.select]:
+                    v.select_set(False)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper == "NV":
+                if obj.mode == "EDIT":
+                    vNew = actV + vector_delta
+                    nVert = bm.verts.new(vNew)
+                    bmesh.update_edit_mesh(obj.data)
+                    bm.select_history.clear()
+                    for v in [v for v in bm.verts if v.select]:
+                        v.select_set(False)
+                    nVert.select_set(True)
+                else:
+                    errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            elif oper == "EV" and obj.mode == "EDIT":
+                for v in [v for v in bm.verts if v.select]:
+                    nVert = bm.verts.new(v.co)
+                    nVert.co = nVert.co + vector_delta
+                    bm.edges.new([v, nVert])
+                    v.select_set(False)
+                    nVert.select_set(True)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper == "DG" and obj.mode == "EDIT":
+                ret = bmesh.ops.duplicate(
+                    bm,
+                    geom=(
+                        [f for f in bm.faces if f.select]
+                        + [e for e in bm.edges if e.select]
+                        + [v for v in bm.verts if v.select]
+                    ),
+                    use_select_history=True,
+                )
+                geom_dupe = ret["geom"]
+                verts_dupe = [v for v in geom_dupe if isinstance(v, bmesh.types.BMVert)]
+                edges_dupe = [e for e in geom_dupe if isinstance(e, bmesh.types.BMEdge)]
+                faces_dupe = [f for f in geom_dupe if isinstance(f, bmesh.types.BMFace)]
+                del ret
+                bmesh.ops.translate(bm, verts=verts_dupe, vec=vector_delta)
+                updateSel(bm, verts_dupe, edges_dupe, faces_dupe)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper == "EG" and obj.mode == "EDIT":
+                ret = bmesh.ops.extrude_face_region(
+                    bm,
+                    geom=(
+                        [f for f in bm.faces if f.select]
+                        + [e for e in bm.edges if e.select]
+                        + [v for v in bm.verts if v.select]
+                    ),
+                    use_select_history=True,
+                )
+                geom_extr = ret["geom"]
+                verts_extr = [v for v in geom_extr if isinstance(v, bmesh.types.BMVert)]
+                edges_extr = [e for e in geom_extr if isinstance(e, bmesh.types.BMEdge)]
+                faces_extr = [f for f in geom_extr if isinstance(f, bmesh.types.BMFace)]
+                del ret
+                bmesh.ops.translate(bm, verts=verts_extr, vec=vector_delta)
+                updateSel(bm, verts_extr, edges_extr, faces_extr)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            else:
+                errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_DIR}"
+                self.report({"ERROR"}, errmsg)
+        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.
+
+        - Reads pg.operation from Operation Mode Selector as 'oper'
+        - 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.
+
+        Local vector variable 'vector_delta' used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        per_v = pg.percent
+        oper = pg.operation
+        ext_a = pg.extend
+        flip_p = pg.flip_percent
+        obj = context.view_layer.objects.active
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        if obj.mode == "EDIT":
+            bm = bmesh.from_edit_mesh(obj.data)
+        obj_loc = obj.matrix_world.decompose()[0]
+        vector_delta = getPercent(obj, flip_p, per_v, oper, scene)
+        if vector_delta is None:
+            return {"FINISHED"}
+
+        if oper == "CU":
+            if obj.mode == "EDIT":
+                scene.cursor.location = obj_loc + vector_delta
+            elif obj.mode == "OBJECT":
+                scene.cursor.location = vector_delta
+        elif oper == "PP":
+            if obj.mode == "EDIT":
+                pg.pivot_loc = obj_loc + vector_delta
+            elif obj.mode == "OBJECT":
+                pg.pivot_loc = vector_delta
+        elif oper == "MV":
+            if obj.mode == "EDIT":
+                bm.select_history[-1].co = vector_delta
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif obj.mode == "OBJECT":
+                obj.location = vector_delta
+        elif oper == "SE" and obj.mode == "EDIT":
+            edges = [e for e in bm.edges if e.select]
+            if len(edges) != 1:
+                errmsg = f"{PDT_ERR_SEL_1_EDGE} {len(edges)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            geom = bmesh.ops.subdivide_edges(bm, edges=edges, cuts=1)
+            new_verts = [v for v in geom["geom_split"] if isinstance(v, bmesh.types.BMVert)]
+            nVert = new_verts[0]
+            nVert.co = vector_delta
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        elif oper == "NV":
+            if obj.mode == "EDIT":
+                nVert = bm.verts.new(vector_delta)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+                for v in [v for v in bm.verts if v.select]:
+                    v.select_set(False)
+                nVert.select_set(True)
+            else:
+                errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        elif oper == "EV" and obj.mode == "EDIT":
+            nVert = bm.verts.new(vector_delta)
+            if ext_a:
+                for v in [v for v in bm.verts if v.select]:
+                    bm.edges.new([v, nVert])
+                    v.select_set(False)
+            else:
+                bm.edges.new([bm.select_history[-1], nVert])
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        else:
+            errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_PERCENT}"
+            self.report({"ERROR"}, errmsg)
+        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.
+
+        - Reads pg.operation from Operation Mode Selector as 'oper'
+        - 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.
+
+        Local vector variable 'vector_delta' used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        oper = pg.operation
+        ext_a = pg.extend
+        obj = context.view_layer.objects.active
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        obj_loc = obj.matrix_world.decompose()[0]
+        if obj.mode == "EDIT":
+            bm = bmesh.from_edit_mesh(obj.data)
+            if len(bm.select_history) == 3:
+                actV, othV, lstV = checkSelection(3, bm, obj)
+                if actV is None:
+                    errmsg = PDT_ERR_VERT_MODE
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            else:
+                errmsg = f"{PDT_ERR_SEL_3_VERTS} {len(bm.select_history)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        elif obj.mode == "OBJECT":
+            objs = context.view_layer.objects.selected
+            if len(objs) != 3:
+                errmsg = f"{PDT_ERR_SEL_3_OBJS} {len(objs)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            else:
+                objs_s = [ob for ob in objs if ob.name != obj.name]
+                actV = obj.matrix_world.decompose()[0]
+                othV = objs_s[-1].matrix_world.decompose()[0]
+                lstV = objs_s[-2].matrix_world.decompose()[0]
+        vector_delta = intersect_point_line(actV, othV, lstV)[0]
+        if oper == "CU":
+            if obj.mode == "EDIT":
+                scene.cursor.location = obj_loc + vector_delta
+            elif obj.mode == "OBJECT":
+                scene.cursor.location = vector_delta
+        elif oper == "PP":
+            if obj.mode == "EDIT":
+                pg.pivot_loc = obj_loc + vector_delta
+            elif obj.mode == "OBJECT":
+                pg.pivot_loc = vector_delta
+        elif oper == "MV":
+            if obj.mode == "EDIT":
+                if ext_a:
+                    for v in [v for v in bm.verts if v.select]:
+                        v.co = vector_delta
+                    bm.select_history.clear()
+                    bmesh.ops.remove_doubles(
+                        bm, verts=[v for v in bm.verts if v.select], dist=0.0001
+                    )
+                else:
+                    bm.select_history[-1].co = vector_delta
+                    bm.select_history.clear()
+                bmesh.update_edit_mesh(obj.data)
+            elif obj.mode == "OBJECT":
+                context.view_layer.objects.active.location = vector_delta
+        elif oper == "NV":
+            if obj.mode == "EDIT":
+                nVert = bm.verts.new(vector_delta)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+                for v in [v for v in bm.verts if v.select]:
+                    v.select_set(False)
+                nVert.select_set(True)
+            else:
+                errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        elif oper == "EV" and obj.mode == "EDIT":
+            vNew = vector_delta
+            nVert = bm.verts.new(vNew)
+            if ext_a:
+                for v in [v for v in bm.verts if v.select]:
+                    bm.edges.new([v, nVert])
+            else:
+                bm.edges.new([bm.select_history[-1], nVert])
+            for v in [v for v in bm.verts if v.select]:
+                v.select_set(False)
+            nVert.select_set(True)
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        else:
+            errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_NOR}"
+            self.report({"ERROR"}, errmsg)
+        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 Convergance Intersection between 4 points, or 2 Edges.
+
+        - Reads pg.operation from Operation Mode Selector as 'oper'
+        - 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.
+
+        Local vector variable 'vector_delta' used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        oper = pg.operation
+        plane = pg.plane
+        obj = context.view_layer.objects.active
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        if obj.mode == "EDIT":
+            obj_loc = obj.matrix_world.decompose()[0]
+            bm = bmesh.from_edit_mesh(obj.data)
+            edges = [e for e in bm.edges if e.select]
+            if len(edges) == 2:
+                ext_a = True
+                va = edges[0].verts[0]
+                actV = va.co
+                vo = edges[0].verts[1]
+                othV = vo.co
+                vl = edges[1].verts[0]
+                lstV = vl.co
+                vf = edges[1].verts[1]
+                fstV = vf.co
+            elif len(bm.select_history) == 4:
+                ext_a = pg.extend
+                va = bm.select_history[-1]
+                vo = bm.select_history[-2]
+                vl = bm.select_history[-3]
+                vf = bm.select_history[-4]
+                actV, othV, lstV, fstV = checkSelection(4, bm, obj)
+                if actV is None:
+                    errmsg = PDT_ERR_VERT_MODE
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            else:
+                errmsg = (
+                    PDT_ERR_SEL_4_VERTS
+                    + str(len(bm.select_history))
+                    + " Vertices/"
+                    + str(len(edges))
+                    + " Edges)"
+                )
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            vector_delta, done = intersection(actV, othV, lstV, fstV, plane)
+            if not done:
+                errmsg = f"{PDT_ERR_INT_LINES} {plane}  {PDT_LAB_PLANE}"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+
+            if oper == "CU":
+                scene.cursor.location = obj_loc + vector_delta
+            elif oper == "PP":
+                pg.pivot_loc = obj_loc + vector_delta
+            elif oper == "NV":
+                vNew = vector_delta
+                nVert = bm.verts.new(vNew)
+                for v in [v for v in bm.verts if v.select]:
+                    v.select_set(False)
+                for f in bm.faces:
+                    f.select_set(False)
+                for e in bm.edges:
+                    e.select_set(False)
+                nVert.select_set(True)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+            elif oper in {"MV", "EV"}:
+                nVert = None
+                proc = False
+
+                if (actV - vector_delta).length < (othV - vector_delta).length:
+                    if oper == "MV":
+                        va.co = vector_delta
+                        proc = True
+                    elif oper == "EV":
+                        nVert = bm.verts.new(vector_delta)
+                        bm.edges.new([va, nVert])
+                        proc = True
+                else:
+                    if oper == "MV" and ext_a:
+                        vo.co = vector_delta
+                    elif oper == "EV" and ext_a:
+                        nVert = bm.verts.new(vector_delta)
+                        bm.edges.new([vo, nVert])
+
+                if (lstV - vector_delta).length < (fstV - vector_delta).length:
+                    if oper == "MV" and ext_a:
+                        vl.co = vector_delta
+                    elif oper == "EV" and ext_a:
+                        bm.edges.new([vl, nVert])
+                else:
+                    if oper == "MV" and ext_a:
+                        vf.co = vector_delta
+                    elif oper == "EV" and ext_a:
+                        bm.edges.new([vf, nVert])
+                bm.select_history.clear()
+                bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
+
+                if not proc and not ext_a:
+                    errmsg = PDT_ERR_INT_NO_ALL
+                    self.report({"ERROR"}, errmsg)
+                    bmesh.update_edit_mesh(obj.data)
+                    return {"FINISHED"}
+                else:
+                    for v in bm.verts:
+                        v.select_set(False)
+                    for f in bm.faces:
+                        f.select_set(False)
+                    for e in bm.edges:
+                        e.select_set(False)
+
+                    if nVert is not None:
+                        nVert.select_set(True)
+                    for v in bm.select_history:
+                        if v is not None:
+                            v.select_set(True)
+                    bmesh.update_edit_mesh(obj.data)
+            else:
+                errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
+                self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        elif obj.mode == "OBJECT":
+            if len(context.view_layer.objects.selected) != 4:
+                errmsg = f"{PDT_ERR_SEL_4_OBJS} {len(context.view_layer.objects.selected)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            else:
+                order = pg.object_order.split(",")
+                objs = sorted(
+                    [ob for ob in context.view_layer.objects.selected], key=lambda x: x.name
+                )
+                message = (
+                    "Original Object Order was: "
+                    + objs[0].name
+                    + ", "
+                    + objs[1].name
+                    + ", "
+                    + objs[2].name
+                    + ", "
+                    + objs[3].name
+                )
+                self.report({"INFO"}, message)
+
+                actV = objs[int(order[0]) - 1].matrix_world.decompose()[0]
+                othV = objs[int(order[1]) - 1].matrix_world.decompose()[0]
+                lstV = objs[int(order[2]) - 1].matrix_world.decompose()[0]
+                fstV = objs[int(order[3]) - 1].matrix_world.decompose()[0]
+            vector_delta, done = intersection(actV, othV, lstV, fstV, plane)
+            if not done:
+                errmsg = f"{PDT_ERR_INT_LINES} {plane}  {PDT_LAB_PLANE}"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            if oper == "CU":
+                scene.cursor.location = vector_delta
+            elif oper == "PP":
+                pg.pivot_loc = vector_delta
+            elif oper == "MV":
+                context.view_layer.objects.active.location = vector_delta
+                infmsg = PDT_INF_OBJ_MOVED + message
+                self.report({"INFO"}, infmsg)
+            else:
+                errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_INTERSECT}"
+                self.report({"ERROR"}, errmsg)
+            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.
+
+        Valid Options for pg.operation; CU PP MV NV EV
+        - Reads pg.operation from Operation Mode Selector as 'oper'
+        - Reads pg.extend scene variable 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.
+
+        Local vector variable 'vector_delta' used to reposition features.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        oper = pg.operation
+        ext_a = pg.extend
+        obj = context.view_layer.objects.active
+
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        if obj.mode == "EDIT":
+            obj = context.view_layer.objects.active
+            obj_loc = obj.matrix_world.decompose()[0]
+            bm = bmesh.from_edit_mesh(obj.data)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 3:
+                actV = verts[0].co
+                othV = verts[1].co
+                lstV = verts[2].co
+            else:
+                errmsg = f"{PDT_ERR_SEL_3_VERTS} {len(verts)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            vector_delta, radius = arcCentre(actV, othV, lstV)
+            if str(radius) == "inf":
+                errmsg = PDT_ERR_STRIGHT_LINE
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            pg.distance = radius
+            if oper == "CU":
+                scene.cursor.location = obj_loc + vector_delta
+            elif oper == "PP":
+                pg.pivot_loc = obj_loc + vector_delta
+            elif oper == "NV":
+                vNew = vector_delta
+                nVert = bm.verts.new(vNew)
+                for v in [v for v in bm.verts if v.select]:
+                    v.select_set(False)
+                nVert.select_set(True)
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+                nVert.select_set(True)
+            elif oper == "MV":
+                if obj.mode == "EDIT":
+                    if ext_a:
+                        for v in [v for v in bm.verts if v.select]:
+                            v.co = vector_delta
+                        bm.select_history.clear()
+                        bmesh.ops.remove_doubles(
+                            bm, verts=[v for v in bm.verts if v.select], dist=0.0001
+                        )
+                    else:
+                        bm.select_history[-1].co = vector_delta
+                        bm.select_history.clear()
+                    bmesh.update_edit_mesh(obj.data)
+                elif obj.mode == "OBJECT":
+                    context.view_layer.objects.active.location = vector_delta
+            elif oper == "EV":
+                nVert = bm.verts.new(vector_delta)
+                if ext_a:
+                    for v in [v for v in bm.verts if v.select]:
+                        bm.edges.new([v, nVert])
+                        v.select_set(False)
+                    nVert.select_set(True)
+                    bm.select_history.clear()
+                    bmesh.ops.remove_doubles(
+                        bm, verts=[v for v in bm.verts if v.select], dist=0.0001
+                    )
+                    bmesh.update_edit_mesh(obj.data)
+                else:
+                    bm.edges.new([bm.select_history[-1], nVert])
+                    bmesh.update_edit_mesh(obj.data)
+                    bm.select_history.clear()
+            else:
+                errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_ARCCENTRE}"
+                self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        elif obj.mode == "OBJECT":
+            if len(context.view_layer.objects.selected) != 3:
+                errmsg = f"{PDT_ERR_SEL_3_OBJS} {len(context.view_layer.objects.selected)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            else:
+                actV = context.view_layer.objects.selected[0].matrix_world.decompose()[0]
+                othV = context.view_layer.objects.selected[1].matrix_world.decompose()[0]
+                lstV = context.view_layer.objects.selected[2].matrix_world.decompose()[0]
+                vector_delta, radius = arcCentre(actV, othV, lstV)
+                pg.distance = radius
+                if oper == "CU":
+                    scene.cursor.location = vector_delta
+                elif oper == "PP":
+                    pg.pivot_loc = vector_delta
+                elif oper == "MV":
+                    context.view_layer.objects.active.location = vector_delta
+                else:
+                    errmsg = f"{oper} {PDT_ERR_NON_VALID} {PDT_LAB_ARCCENTRE}"
+                    self.report({"ERROR"}, errmsg)
+                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.
+
+        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.
+        """
+
+        obj = context.view_layer.objects.active
+        bm = bmesh.from_edit_mesh(obj.data)
+        verts = [v for v in bm.verts if v.select]
+        if len(verts) == 2:
+            try:
+                bm.edges.new([verts[-1], verts[-2]])
+                bmesh.update_edit_mesh(obj.data)
+                bm.select_history.clear()
+                return {"FINISHED"}
+            except ValueError:
+                errmsg = PDT_ERR_CONNECTED
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        else:
+            errmsg = f"{PDT_ERR_SEL_2_VERTS} {len(verts)})"
+            self.report({"ERROR"}, errmsg)
+            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.
+
+        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
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj = context.view_layer.objects.active
+        bm = bmesh.from_edit_mesh(obj.data)
+        verts = [v for v in bm.verts if v.select]
+        if len(verts) == 0:
+            errmsg = PDT_ERR_SEL_1_VERT
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        else:
+            bpy.ops.mesh.bevel(
+                offset_type="OFFSET",
+                offset=pg.fillet_radius,
+                segments=pg.fillet_segments,
+                profile=pg.fillet_profile,
+                vertex_only=pg.fillet_vertices_only,
+            )
+            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.
+
+        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 Oject Modes.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        plane = pg.plane
+        flip_a = pg.flip_angle
+        obj = context.view_layer.objects.active
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        if obj.mode == "EDIT":
+            bm = bmesh.from_edit_mesh(obj.data)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 2:
+                if len(bm.select_history) == 2:
+                    actV, othV = checkSelection(2, bm, obj)
+                    if actV is None:
+                        errmsg = PDT_ERR_VERT_MODE
+                        self.report({"ERROR"}, errmsg)
+                        return {"FINISHED"}
+                else:
+                    errmsg = f"{PDT_ERR_SEL_2_VERTIO} {len(bm.select_history)})"
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            else:
+                errmsg = f"{PDT_ERR_SEL_2_VERTIO} {len(verts)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        elif obj.mode == "OBJECT":
+            objs = context.view_layer.objects.selected
+            if len(objs) < 2:
+                errmsg = f"{PDT_ERR_SEL_2_OBJS} {len(objs)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            objs_s = [ob for ob in objs if ob.name != obj.name]
+            actV = obj.matrix_world.decompose()[0]
+            othV = objs_s[-1].matrix_world.decompose()[0]
+        if plane == "LO":
+            disV = othV - actV
+            othV = viewCoordsI(disV.x, disV.y, disV.z)
+            actV = Vector((0, 0, 0))
+            v0 = np.array([actV.x + 1, actV.y]) - np.array([actV.x, actV.y])
+            v1 = np.array([othV.x, othV.y]) - np.array([actV.x, actV.y])
+        else:
+            a1, a2, _ = setMode(plane)
+            v0 = np.array([actV[a1] + 1, actV[a2]]) - np.array([actV[a1], actV[a2]])
+            v1 = np.array([othV[a1], othV[a2]]) - np.array([actV[a1], actV[a2]])
+        ang = np.rad2deg(np.arctan2(np.linalg.det([v0, v1]), np.dot(v0, v1)))
+        if flip_a:
+            if ang > 0:
+                pg.angle = ang - 180
+            else:
+                pg.angle = ang + 180
+        else:
+            pg.angle = ang
+        if plane == "LO":
+            pg.distance = sqrt((actV.x - othV.x) ** 2 + (actV.y - othV.y) ** 2)
+        else:
+            pg.distance = sqrt((actV[a1] - othV[a1]) ** 2 + (actV[a2] - othV[a2]) ** 2)
+        pg.cartesian_coords = othV - actV
+        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.
+
+        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 Oject Modes.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        pg = context.scene.pdt_pg
+        flip_a = pg.flip_angle
+        obj = context.view_layer.objects.active
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        if obj.mode == "EDIT":
+            bm = bmesh.from_edit_mesh(obj.data)
+            verts = [v for v in bm.verts if v.select]
+            if len(verts) == 3:
+                if len(bm.select_history) == 3:
+                    actV, othV, lstV = checkSelection(3, bm, obj)
+                    if actV is None:
+                        errmsg = PDT_ERR_VERT_MODE
+                        self.report({"ERROR"}, errmsg)
+                        return {"FINISHED"}
+                else:
+                    errmsg = f"{PDT_ERR_SEL_3_VERTIO} {len(bm.select_history)})"
+                    self.report({"ERROR"}, errmsg)
+                    return {"FINISHED"}
+            else:
+                errmsg = f"{PDT_ERR_SEL_3_VERTIO} {len(verts)})"
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+        elif obj.mode == "OBJECT":
+            objs = context.view_layer.objects.selected
+            if len(objs) < 3:
+                errmsg = PDT_ERR_SEL_3_OBJS + str(len(objs))
+                self.report({"ERROR"}, errmsg)
+                return {"FINISHED"}
+            objs_s = [ob for ob in objs if ob.name != obj.name]
+            actV = obj.matrix_world.decompose()[0]
+            othV = objs_s[-1].matrix_world.decompose()[0]
+            lstV = objs_s[-2].matrix_world.decompose()[0]
+        ba = np.array([othV.x, othV.y, othV.z]) - np.array([actV.x, actV.y, actV.z])
+        bc = np.array([lstV.x, lstV.y, lstV.z]) - np.array([actV.x, actV.y, actV.z])
+        cosA = np.dot(ba, bc) / (np.linalg.norm(ba) * np.linalg.norm(bc))
+        ang = np.degrees(np.arccos(cosA))
+        if flip_a:
+            if ang > 0:
+                pg.angle = ang - 180
+            else:
+                pg.angle = ang + 180
+        else:
+            pg.angle = ang
+        pg.distance = (actV - othV).length
+        pg.cartesian_coords = othV - actV
+        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.
+
+        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.
+        """
+
+        scene = context.scene
+        obj = context.view_layer.objects.active
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        obj_loc = obj.matrix_world.decompose()[0]
+        cur_loc = scene.cursor.location
+        diff_v = obj_loc - cur_loc
+        if obj.mode == "EDIT":
+            bm = bmesh.from_edit_mesh(obj.data)
+            for v in bm.verts:
+                v.co = v.co + diff_v
+            obj.location = cur_loc
+            bmesh.update_edit_mesh(obj.data)
+            bm.select_history.clear()
+        elif obj.mode == "OBJECT":
+            for v in obj.data.vertices:
+                v.co = v.co + diff_v
+            obj.location = cur_loc
+        else:
+            errmsg = f"{PDT_ERR_EDOB_MODE} {obj.mode})"
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        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.
+
+        Similar to 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.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        tap_ax = pg.taper
+        ang_v = pg.angle
+        obj = context.view_layer.objects.active
+        if ang_v > 80 or ang_v < -80:
+            errmsg = f"{PDT_ERR_TAPER_ANG} {ang_v})"
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        if obj is None:
+            errmsg = PDT_ERR_NO_ACT_OBJ
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        _, a2, a3 = setAxis(tap_ax)
+        bm = bmesh.from_edit_mesh(obj.data)
+        if len(bm.select_history) >= 1:
+            rotV = bm.select_history[-1]
+            viewV = viewCoords(rotV.co.x, rotV.co.y, rotV.co.z)
+        else:
+            errmsg = f"{PDT_ERR_TAPER_SEL} {len(bm.select_history)})"
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        for v in [v for v in bm.verts if v.select]:
+            if pg.plane == "LO":
+                v_loc = viewCoords(v.co.x, v.co.y, v.co.z)
+                dis_v = sqrt((viewV.x - v_loc.x) ** 2 + (viewV.y - v_loc.y) ** 2)
+                x_loc = dis_v * tan(ang_v * pi / 180)
+                vm = viewDir(x_loc, 0)
+                v.co = v.co - vm
+            else:
+                dis_v = sqrt((rotV.co[a3] - v.co[a3]) ** 2 + (rotV.co[a2] - v.co[a2]) ** 2)
+                v.co[a2] = v.co[a2] - (dis_v * tan(ang_v * pi / 180))
+        bmesh.update_edit_mesh(obj.data)
+        bm.select_history.clear()
+        return {"FINISHED"}
diff --git a/precision_drawing_tools/pdt_etof.py b/precision_drawing_tools/pdt_etof.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c2fbc43964cd715888eef97b0ac7def7b31c3c6
--- /dev/null
+++ b/precision_drawing_tools/pdt_etof.py
@@ -0,0 +1,132 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+#
+# <pep8 compliant>
+#
+# ----------------------------------------------------------
+# Author: Zeffii
+# Modified by: Alan Odom (Clockmender) & Rune Morling (ermo)
+# ----------------------------------------------------------
+#
+#
+import bpy
+import bmesh
+from mathutils.geometry import intersect_line_plane
+from .pdt_msg_strings import (
+    PDT_ERR_NOINT,
+    PDT_ERR_SEL_1_E_1_F
+)
+
+
+def failure_message(self):
+    """Warn to the user to select 1 edge and 1 face."""
+    self.report({"WARNING"}, PDT_ERR_SEL_1_E_1_F)
+
+
+def failure_message_on_plane(self):
+    """Report an informative error message in a popup."""
+    msg2 = """\
+Edge2Face expects the edge to intersect at one point on the plane of the selected face. You're
+seeing this warning because mathutils.geometry.intersect_line_plane is being called on an edge/face
+combination that has no clear intersection point ( both points of the edge either touch the same
+plane as the face or they lie in a plane that is offset along the face's normal )"""
+    lines = msg2.split("\n")
+    for line in lines:
+        self.report({"INFO"}, line)
+    self.report({"ERROR"}, PDT_ERR_NOINT)
+
+
+def extend_vertex(self):
+    """Computes Edge Extension to Face.
+
+    Args:
+        None
+
+    Returns:
+        Nothing."""
+
+    obj = bpy.context.edit_object
+    me = obj.data
+    bm = bmesh.from_edit_mesh(me)
+    verts = bm.verts
+    faces = bm.faces
+
+    planes = [f for f in faces if f.select]
+    if not len(planes) == 1:
+        failure_message(self)
+        return
+
+    plane = planes[0]
+    plane_vert_indices = plane.verts[:]
+    all_selected_vert_indices = [v for v in verts if v.select]
+
+    M = set(plane_vert_indices)
+    N = set(all_selected_vert_indices)
+    O = N.difference(M)
+    O = list(O)
+
+    if not len(O) == 2:
+        failure_message(self)
+        return
+
+    (v1_ref, v1), (v2_ref, v2) = [(i, i.co) for i in O]
+
+    plane_co = plane.calc_center_median()
+    plane_no = plane.normal
+
+    new_co = intersect_line_plane(v1, v2, plane_co, plane_no, False)
+
+    if new_co:
+        new_vertex = verts.new(new_co)
+        A_len = (v1 - new_co).length
+        B_len = (v2 - new_co).length
+
+        vertex_reference = v1_ref if (A_len < B_len) else v2_ref
+        bm.edges.new([vertex_reference, new_vertex])
+        bmesh.update_edit_mesh(me, True)
+
+    else:
+        failure_message_on_plane(self)
+
+
+class PDT_OT_EdgeToFace(bpy.types.Operator):
+    """Extend Selected Edge to Projected Intersection with Selected Face."""
+
+    bl_idname = "pdt.edge_to_face"
+    bl_label = "Extend Edge to Face"
+    bl_options = {"REGISTER", "UNDO"}
+
+    @classmethod
+    def poll(cls, context):
+        """Only allow this to work if a mesh is selected in EDIT mode."""
+        ob = context.object
+        if ob is None:
+            return False
+        return all([bool(ob), ob.type == "MESH", ob.mode == "EDIT"])
+
+    def execute(self, context):
+        """Extends Disconnected Edge to Intersect with Face.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set."""
+
+        extend_vertex(self)
+        return {"FINISHED"}
diff --git a/precision_drawing_tools/pdt_functions.py b/precision_drawing_tools/pdt_functions.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9573ac8b256cc718960e4141b3a52fc4ec05ffb
--- /dev/null
+++ b/precision_drawing_tools/pdt_functions.py
@@ -0,0 +1,688 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+# Common Functions used in more than one place in PDT Operations
+
+import bpy
+import bmesh
+import bgl
+import gpu
+import numpy as np
+from mathutils import Vector, Quaternion
+from gpu_extras.batch import batch_for_shader
+from math import cos, sin, pi
+from .pdt_msg_strings import (
+    PDT_ERR_VERT_MODE,
+    PDT_ERR_SEL_2_V_1_E,
+    PDT_ERR_SEL_2_OBJS,
+    PDT_ERR_NO_ACT_OBJ,
+    PDT_ERR_SEL_1_EDGEM
+)
+
+
+def debug(msg, prefix=""):
+    """Print a debug message to the console if PDT's or Blender's debug flags are set.
+
+    The printed message will be of the form:
+
+    {prefix}{caller file name:line number}| {msg}
+    """
+
+    pdt_debug = bpy.context.preferences.addons[__package__].preferences.debug
+    if  bpy.app.debug or bpy.app.debug_python or pdt_debug:
+        import traceback
+
+        def extract_filename(fullpath):
+            """Return only the filename part of fullpath (excluding its path)."""
+            # Expected to end up being a string containing only the filename
+            # (i.e. excluding its preceding '/' separated path)
+            filename = fullpath.split('/')[-1]
+            #print(filename)
+            # something went wrong
+            if len(filename) < 1:
+                return fullpath
+            # since this is a string, just return it
+            return filename
+
+        # stack frame corresponding to the line where debug(msg) was called
+        #print(traceback.extract_stack()[-2])
+        laststack = traceback.extract_stack()[-2]
+        #print(laststack[0])
+        # laststack[0] is the caller's full file name, laststack[1] is the line number
+        print(f"{prefix}{extract_filename(laststack[0])}:{laststack[1]}| {msg}")
+
+def oops(self, context):
+    """Error Routine.
+
+    Displays error message in a popup.
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Note:
+        Uses pg.error scene variable
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    self.layout.label(text=pg.error)
+
+
+def setMode(mode_pl):
+    """Sets Active Axes for View Orientation.
+
+    Sets indices of axes for locational vectors
+
+    Args:
+        mode_pl: Plane Selector variable as input
+
+    Returns:
+        3 Integer indices.
+    """
+
+    if mode_pl == "XY":
+        # a1 = x a2 = y a3 = z
+        return 0, 1, 2
+    if mode_pl == "XZ":
+        # a1 = x a2 = z a3 = y
+        return 0, 2, 1
+    if mode_pl == "YZ":
+        # a1 = y a2 = z a3 = x
+        return 1, 2, 0
+    #FIXME: This needs a proper specification and a default
+
+
+def setAxis(mode_pl):
+    """Sets Active Axes for View Orientation.
+
+    Sets indices for axes from taper vectors
+
+    Args:
+        mode_pl: Taper Axis Selector variable as input
+
+    Note:
+        Axis order: Rotate Axis, Move Axis, Height Axis
+
+    Returns:
+        3 Integer Indicies.
+    """
+
+    if mode_pl == "RX-MY":
+        return 0, 1, 2
+    if mode_pl == "RX-MZ":
+        return 0, 2, 1
+    if mode_pl == "RY-MX":
+        return 1, 0, 2
+    if mode_pl == "RY-MZ":
+        return 1, 2, 0
+    if mode_pl == "RZ-MX":
+        return 2, 0, 1
+    if mode_pl == "RZ-MY":
+        return 2, 1, 0
+    #FIXME: This needs a proper specification and a default
+
+
+def checkSelection(num, bm, obj):
+    """Check that the Object's select_history has sufficient entries.
+
+    If selection history is not Verts, clears selection and history.
+
+    Args:
+        num: The number of entries required for each operation
+        bm: The Bmesh from the Object
+        obj: The Object
+
+    Returns:
+        list of 3D points as Vectors.
+    """
+
+    if len(bm.select_history) < num:
+        return None
+    else:
+        actE = bm.select_history[-1]
+    if isinstance(actE, bmesh.types.BMVert):
+        actV = actE.co
+        if num == 1:
+            return actV
+        elif num == 2:
+            othV = bm.select_history[-2].co
+            return actV, othV
+        elif num == 3:
+            othV = bm.select_history[-2].co
+            lstV = bm.select_history[-3].co
+            return actV, othV, lstV
+        elif num == 4:
+            othV = bm.select_history[-2].co
+            lstV = bm.select_history[-3].co
+            fstV = bm.select_history[-4].co
+            return actV, othV, lstV, fstV
+    else:
+        for f in bm.faces:
+            f.select_set(False)
+        for e in bm.edges:
+            e.select_set(False)
+        for v in bm.verts:
+            v.select_set(False)
+        bmesh.update_edit_mesh(obj.data)
+        bm.select_history.clear()
+    return None
+
+
+def updateSel(bm, verts, edges, faces):
+    """Updates Vertex, Edge and Face Selections following a function.
+
+    Args:
+        bm: Object Bmesh
+        verts: New Selection for Vertices
+        edges: The Edges on which to operate
+        faces: The Faces on which to operate
+
+    Returns:
+        Nothing.
+    """
+    for f in bm.faces:
+        f.select_set(False)
+    for e in bm.edges:
+        e.select_set(False)
+    for v in bm.verts:
+        v.select_set(False)
+    for v in verts:
+        v.select_set(True)
+    for e in edges:
+        e.select_set(True)
+    for f in faces:
+        f.select_set(True)
+
+
+def viewCoords(x_loc, y_loc, z_loc):
+    """Converts input Vector values to new Screen Oriented Vector.
+
+    Args:
+        x_loc: X coordinate from vector
+        y_loc: Y coordinate from vector
+        z_loc: Z coordinate from vector
+
+    Returns:
+        Vector adjusted to View's Inverted Tranformation Matrix.
+    """
+
+    areas = [a for a in bpy.context.screen.areas if a.type == "VIEW_3D"]
+    if len(areas) > 0:
+        vm = areas[0].spaces.active.region_3d.view_matrix
+        vm = vm.to_3x3().normalized().inverted()
+        vl = Vector((x_loc, y_loc, z_loc))
+        vw = vm @ vl
+        return vw
+    else:
+        return Vector((0, 0, 0))
+
+
+def viewCoordsI(x_loc, y_loc, z_loc):
+    """Converts Screen Oriented input Vector values to new World Vector.
+
+    Converts View tranformation Matrix to Rotational Matrix
+
+    Args:
+        x_loc: X coordinate from vector
+        y_loc: Y coordinate from vector
+        z_loc: Z coordinate from vector
+
+    Returns:
+        Vector adjusted to View's Transformation Matrix.
+    """
+
+    areas = [a for a in bpy.context.screen.areas if a.type == "VIEW_3D"]
+    if len(areas) > 0:
+        vm = areas[0].spaces.active.region_3d.view_matrix
+        vm = vm.to_3x3().normalized()
+        vl = Vector((x_loc, y_loc, z_loc))
+        vw = vm @ vl
+        return vw
+    else:
+        return Vector((0, 0, 0))
+
+
+def viewDir(dis_v, ang_v):
+    """Converts Distance and Angle to View Oriented Vector.
+
+    Converts View Transformation Matrix to Rotational Matrix (3x3)
+    Angles are converted to Radians from degrees.
+
+    Args:
+        dis_v: Scene distance
+        ang_v: Scene angle
+
+    Returns:
+        World Vector.
+    """
+
+    areas = [a for a in bpy.context.screen.areas if a.type == "VIEW_3D"]
+    if len(areas) > 0:
+        vm = areas[0].spaces.active.region_3d.view_matrix
+        vm = vm.to_3x3().normalized().inverted()
+        vl = Vector((0, 0, 0))
+        vl.x = dis_v * cos(ang_v * pi / 180)
+        vl.y = dis_v * sin(ang_v * pi / 180)
+        vw = vm @ vl
+        return vw
+    else:
+        return Vector((0, 0, 0))
+
+
+def euler_to_quaternion(roll, pitch, yaw):
+    """Converts Euler Rotation to Quaternion Rotation.
+
+    Args:
+        roll: Roll in Euler rotation
+        pitch: Pitch in Euler rotation
+        yaw: Yaw in Euler rotation
+
+    Returns:
+        Quaternion Rotation.
+    """
+
+    # fmt: off
+    qx = (np.sin(roll/2) * np.cos(pitch/2) * np.cos(yaw/2)
+          - np.cos(roll/2) * np.sin(pitch/2) * np.sin(yaw/2))
+    qy = (np.cos(roll/2) * np.sin(pitch/2) * np.cos(yaw/2)
+          + np.sin(roll/2) * np.cos(pitch/2) * np.sin(yaw/2))
+    qz = (np.cos(roll/2) * np.cos(pitch/2) * np.sin(yaw/2)
+          - np.sin(roll/2) * np.sin(pitch/2) * np.cos(yaw/2))
+    qw = (np.cos(roll/2) * np.cos(pitch/2) * np.cos(yaw/2)
+          + np.sin(roll/2) * np.sin(pitch/2) * np.sin(yaw/2))
+    # fmt: on
+    return Quaternion((qw, qx, qy, qz))
+
+
+def arcCentre(actV, othV, lstV):
+    """Calculates Centre of Arc from 3 Vector Locations using standard Numpy routine
+
+    Args:
+        actV: Active vector location
+        othV: Other vector location
+        lstV: Last vector location
+
+    Returns:
+        Vector representing Arc Centre and Float representing Arc Radius.
+    """
+
+    A = np.array([actV.x, actV.y, actV.z])
+    B = np.array([othV.x, othV.y, othV.z])
+    C = np.array([lstV.x, lstV.y, lstV.z])
+    a = np.linalg.norm(C - B)
+    b = np.linalg.norm(C - A)
+    c = np.linalg.norm(B - A)
+    # fmt: off
+    s = (a+b+c) / 2
+    R = a*b*c/4 / np.sqrt(s * (s-a) * (s-b) * (s-c))
+    b1 = a*a * (b*b + c*c - a*a)
+    b2 = b*b * (a*a + c*c - b*b)
+    b3 = c*c * (a*a + b*b - c*c)
+    # fmt: on
+    P = np.column_stack((A, B, C)).dot(np.hstack((b1, b2, b3)))
+    P /= b1 + b2 + b3
+    return Vector((P[0], P[1], P[2])), R
+
+
+def intersection(actV, othV, lstV, fstV, plane):
+    """Calculates Intersection Point of 2 Imagined Lines from 4 Vectors.
+
+    Calculates Converging Intersect Location and indication of
+    whether the lines are convergent using standard Numpy Routines
+
+    Args:
+        actV: Active vector location of first line
+        othV: Other vector location of first line
+        lstV: Last vector location of 2nd line
+        fstV: First vector location of 2nd line
+        plane: Working Plane 4 Vector Locations representing 2 lines and Working Plane
+
+    Returns:
+        Intersection Vector and Boolean for convergent state.
+    """
+
+    if plane == "LO":
+        disV = othV - actV
+        othV = viewCoordsI(disV.x, disV.y, disV.z)
+        disV = lstV - actV
+        lstV = viewCoordsI(disV.x, disV.y, disV.z)
+        disV = fstV - actV
+        fstV = viewCoordsI(disV.x, disV.y, disV.z)
+        refV = Vector((0, 0, 0))
+        ap1 = (fstV.x, fstV.y)
+        ap2 = (lstV.x, lstV.y)
+        bp1 = (othV.x, othV.y)
+        bp2 = (refV.x, refV.y)
+    else:
+        a1, a2, a3 = setMode(plane)
+        ap1 = (fstV[a1], fstV[a2])
+        ap2 = (lstV[a1], lstV[a2])
+        bp1 = (othV[a1], othV[a2])
+        bp2 = (actV[a1], actV[a2])
+    s = np.vstack([ap1, ap2, bp1, bp2])
+    h = np.hstack((s, np.ones((4, 1))))
+    l1 = np.cross(h[0], h[1])
+    l2 = np.cross(h[2], h[3])
+    x, y, z = np.cross(l1, l2)
+    if z == 0:
+        return Vector((0, 0, 0)), False
+    nx = x / z
+    nz = y / z
+    if plane == "LO":
+        ly = 0
+    else:
+        ly = actV[a3]
+    # Order Vector Delta
+    if plane == "XZ":
+        vector_delta = Vector((nx, ly, nz))
+    elif plane == "XY":
+        vector_delta = Vector((nx, nz, ly))
+    elif plane == "YZ":
+        vector_delta = Vector((ly, nx, nz))
+    elif plane == "LO":
+        vector_delta = viewCoords(nx, nz, ly) + actV
+    return vector_delta, True
+
+
+def getPercent(obj, flip_p, per_v, data, scene):
+    """Calculates a Percentage Distance between 2 Vectors.
+
+    Calculates a point that lies a set percentage between two given points
+    using standard Numpy Routines.
+
+    Works for either 2 vertices for an object in Edit mode
+    or 2 selected objects in Object mode.
+
+    Args:
+        obj: The Object under consideration
+        flip_p: Setting this to True measures the percentage starting from the second vector
+        per_v: Percentage Input Value
+        data: pg.flip, pg.percent scene variables & Operational Mode
+        scene: Context Scene
+
+    Returns:
+        World Vector.
+    """
+
+    pg = scene.pdt_pg
+
+    if obj.mode == "EDIT":
+        bm = bmesh.from_edit_mesh(obj.data)
+        verts = [v for v in bm.verts if v.select]
+        if len(verts) == 2:
+            actV = verts[0].co
+            othV = verts[1].co
+            if actV is None:
+                pg.error = PDT_ERR_VERT_MODE
+                bpy.context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return None
+        else:
+            pg.error = PDT_ERR_SEL_2_V_1_E + str(len(verts)) + " Vertices"
+            bpy.context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return None
+        p1 = np.array([actV.x, actV.y, actV.z])
+        p2 = np.array([othV.x, othV.y, othV.z])
+    if obj.mode == "OBJECT":
+        objs = bpy.context.view_layer.objects.selected
+        if len(objs) != 2:
+            pg.error = PDT_ERR_SEL_2_OBJS + str(len(objs)) + ")"
+            bpy.context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+            return None
+        p1 = np.array(
+            [
+                objs[-1].matrix_world.decompose()[0].x,
+                objs[-1].matrix_world.decompose()[0].y,
+                objs[-1].matrix_world.decompose()[0].z,
+            ]
+        )
+        p2 = np.array(
+            [
+                objs[-2].matrix_world.decompose()[0].x,
+                objs[-2].matrix_world.decompose()[0].y,
+                objs[-2].matrix_world.decompose()[0].z,
+            ]
+        )
+    p4 = np.array([0, 0, 0])
+    p3 = p2 - p1
+    _per_v = per_v
+    if (flip_p and data != "MV") or data == "MV":
+        _per_v = 100 - per_v
+    V = (p4+p3) * (_per_v / 100) + p1
+    return Vector((V[0], V[1], V[2]))
+
+
+def objCheck(obj, scene, oper):
+    """Check Object & Selection Validity.
+
+    Args:
+        obj: Active Object
+        scene: Active Scene
+        oper: Operation to check
+
+    Returns:
+        Object Bmesh and Validity Boolean.
+    """
+
+    pg = scene.pdt_pg
+    _oper = oper.upper()
+
+    if obj is None:
+        pg.error = PDT_ERR_NO_ACT_OBJ
+        bpy.context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+        return None, False
+    if obj.mode == "EDIT":
+        bm = bmesh.from_edit_mesh(obj.data)
+        if _oper == "S":
+            if len(bm.edges) < 1:
+                pg.error = f"{PDT_ERR_SEL_1_EDGEM} {len(bm.edges)})"
+                bpy.context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return None, False
+            else:
+                return bm, True
+        if len(bm.select_history) >= 1:
+            if _oper not in {"D", "E", "G", "N", "S"}:
+                actV = checkSelection(1, bm, obj)
+            else:
+                verts = [v for v in bm.verts if v.select]
+                if len(verts) > 0:
+                    actV = verts[0]
+                else:
+                    actV = None
+            if actV is None:
+                pg.error = PDT_ERR_VERT_MODE
+                bpy.context.window_manager.popup_menu(oops, title="Error", icon="ERROR")
+                return None, False
+        return bm, True
+    elif obj.mode == "OBJECT":
+        return None, True
+
+
+def disAng(vals, flip_a, plane, scene):
+    """Set Working Axes when using Direction command.
+
+    Args:
+        vals: Input Arguments (Values)
+        flip_a: Whether to flip the angle
+        plane: Working Plane
+        scene: Current Scene
+
+    Returns:
+        Directional Offset as a Vector.
+    """
+
+    pg = scene.pdt_pg
+    dis_v = float(vals[0])
+    ang_v = float(vals[1])
+    if flip_a:
+        if ang_v > 0:
+            ang_v = ang_v - 180
+        else:
+            ang_v = ang_v + 180
+        pg.angle = ang_v
+    if plane == "LO":
+        vector_delta = viewDir(dis_v, ang_v)
+    else:
+        a1, a2, _ = setMode(plane)
+        vector_delta = Vector((0, 0, 0))
+        # fmt: off
+        vector_delta[a1] = vector_delta[a1] + (dis_v * cos(ang_v * pi/180))
+        vector_delta[a2] = vector_delta[a2] + (dis_v * sin(ang_v * pi/180))
+        # FIXME: Is a3 just ignored?
+        # fmt: on
+    return vector_delta
+
+
+# Shader for displaying the Pivot Point as Graphics.
+#
+shader = gpu.shader.from_builtin("3D_UNIFORM_COLOR") if not bpy.app.background else None
+
+
+def draw3D(coords, gtype, rgba, context):
+    """Draw Pivot Point Graphics.
+
+    Draws either Lines Points, or Tris using defined shader
+
+    Args:
+        coords: Input Coordinates List
+        gtype: Graphic Type
+        rgba: Colour in RGBA format
+        context: Blender bpy.context instance.
+
+    Returns:
+        Nothing.
+    """
+
+    batch = batch_for_shader(shader, gtype, {"pos": coords})
+
+    try:
+        if coords is not None:
+            bgl.glEnable(bgl.GL_BLEND)
+            shader.bind()
+            shader.uniform_float("color", rgba)
+            batch.draw(shader)
+    except:
+        pass
+
+
+def drawCallback3D(self, context):
+    """Create Coordinate List for Pivot Point Graphic.
+
+    Creates coordinates for Pivot Point Graphic consisting of 6 Tris
+    and one Point colour coded Red; X axis, Green; Y axis, Blue; Z axis
+    and a yellow point based upon screen scale
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Returns:
+        Nothing.
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    w = context.region.width
+    x = pg.pivot_loc.x
+    y = pg.pivot_loc.y
+    z = pg.pivot_loc.z
+    # Scale it from view
+    areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+    if len(areas) > 0:
+        sf = abs(areas[0].spaces.active.region_3d.window_matrix.decompose()[2][1])
+    a = w / sf / 10000 * pg.pivot_size
+    b = a * 0.65
+    c = a * 0.05 + (pg.pivot_width * a * 0.02)
+    o = c / 3
+
+    # fmt: off
+    # X Axis
+    coords = [
+        (x, y, z),
+        (x+b, y-o, z),
+        (x+b, y+o, z),
+        (x+a, y, z),
+        (x+b, y+c, z),
+        (x+b, y-c, z),
+    ]
+    # fmt: on
+    colour = (1.0, 0.0, 0.0, pg.pivot_alpha)
+    draw3D(coords, "TRIS", colour, context)
+    coords = [(x, y, z), (x+a, y, z)]
+    draw3D(coords, "LINES", colour, context)
+    # fmt: off
+    # Y Axis
+    coords = [
+        (x, y, z),
+        (x-o, y+b, z),
+        (x+o, y+b, z),
+        (x, y+a, z),
+        (x+c, y+b, z),
+        (x-c, y+b, z),
+    ]
+    # fmt: on
+    colour = (0.0, 1.0, 0.0, pg.pivot_alpha)
+    draw3D(coords, "TRIS", colour, context)
+    coords = [(x, y, z), (x, y + a, z)]
+    draw3D(coords, "LINES", colour, context)
+    # fmt: off
+    # Z Axis
+    coords = [
+        (x, y, z),
+        (x-o, y, z+b),
+        (x+o, y, z+b),
+        (x, y, z+a),
+        (x+c, y, z+b),
+        (x-c, y, z+b),
+    ]
+    # fmt: on
+    colour = (0.2, 0.5, 1.0, pg.pivot_alpha)
+    draw3D(coords, "TRIS", colour, context)
+    coords = [(x, y, z), (x, y, z + a)]
+    draw3D(coords, "LINES", colour, context)
+    # Centre
+    coords = [(x, y, z)]
+    colour = (1.0, 1.0, 0.0, pg.pivot_alpha)
+    draw3D(coords, "POINTS", colour, context)
+
+
+def scale_set(self, context):
+    """Sets Scale by dividing Pivot Distance by System Distance.
+
+    Sets Pivot Point Scale Factors by Measurement
+
+    Args:
+        context: Blender bpy.context instance.
+
+    Note:
+        Uses pg.pivotdis & pg.distance scene variables
+
+    Returns:
+        Status Set.
+    """
+
+    scene = context.scene
+    pg = scene.pdt_pg
+    sys_dis = pg.distance
+    scale_dis = pg.pivot_dis
+    if scale_dis > 0:
+        scale_fac = scale_dis / sys_dis
+        pg.pivot_scale = Vector((scale_fac, scale_fac, scale_fac))
diff --git a/precision_drawing_tools/pdt_library.py b/precision_drawing_tools/pdt_library.py
new file mode 100644
index 0000000000000000000000000000000000000000..30f2640840ff44b2cca1e773c2880744c175a10c
--- /dev/null
+++ b/precision_drawing_tools/pdt_library.py
@@ -0,0 +1,173 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+import bpy
+from bpy.types import Operator
+from mathutils import Vector
+from pathlib import Path
+from .pdt_functions import debug, oops
+from .pdt_msg_strings import PDT_ERR_NO_LIBRARY
+
+
+class PDT_OT_LibShow(Operator):
+    """Show Library File Details."""
+    bl_idname = "pdt.lib_show"
+    bl_label = "Show Library Details"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """Shows Location Of PDT Library File.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        file_path = context.preferences.addons[__package__].preferences.pdt_library_path
+        pg.error = str(Path(file_path))
+        debug("PDT Parts Library:")
+        debug(f"{pg.error}")
+        bpy.context.window_manager.popup_menu(oops, title="Information - Parts Library File", icon="INFO")
+        return {"FINISHED"}
+
+
+class PDT_OT_Append(Operator):
+    """Append from Library at cursor Location."""
+
+    bl_idname = "pdt.append"
+    bl_label = "Append"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """Appends Objects from PDT Library file.
+
+        Appended Objects are placed at Cursor Location.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.lib_objects, pg.lib_collections & pg.lib_materials
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj_names = [o.name for o in context.view_layer.objects]
+        file_path = context.preferences.addons[__package__].preferences.pdt_library_path
+        path = Path(file_path)
+
+        if path.is_file() and ".blend" in str(path):
+            if pg.lib_mode == "OBJECTS":
+                # Force object Mode
+                bpy.ops.object.mode_set(mode='OBJECT')
+                bpy.ops.wm.append(
+                    filepath=str(path), directory=str(path) + "/Object", filename=pg.lib_objects
+                )
+                for obj in context.view_layer.objects:
+                    if obj.name not in obj_names:
+                        obj.select_set(False)
+                        obj.location = Vector(
+                            (scene.cursor.location.x, scene.cursor.location.y, scene.cursor.location.z)
+                        )
+                return {"FINISHED"}
+            elif pg.lib_mode == "COLLECTIONS":
+                bpy.ops.wm.append(
+                    filepath=str(path), directory=str(path) + "/Collection", filename=pg.lib_collections
+                )
+                for obj in context.view_layer.objects:
+                    if obj.name not in obj_names:
+                        obj.select_set(False)
+                        obj.location = Vector(
+                            (scene.cursor.location.x, scene.cursor.location.y, scene.cursor.location.z)
+                        )
+                return {"FINISHED"}
+            elif pg.lib_mode == "MATERIALS":
+                bpy.ops.wm.append(
+                    filepath=str(path), directory=str(path) + "/Material", filename=pg.lib_materials
+                )
+                return {"FINISHED"}
+        else:
+            errmsg = PDT_ERR_NO_LIBRARY
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+
+
+class PDT_OT_Link(Operator):
+    """Link from Library at Object's Origin."""
+
+    bl_idname = "pdt.link"
+    bl_label = "Link"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """Links Objects from PDT Library file.
+
+        Linked Objects are placed at Cursor Location
+
+        Args:
+            context
+
+        Notes:
+            Uses pg.lib_objects, pg.lib_collections & pg.lib_materials
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        file_path = context.preferences.addons[__package__].preferences.pdt_library_path
+        path = Path(file_path)
+        if path.is_file() and ".blend" in str(path):
+            if pg.lib_mode == "OBJECTS":
+                # Force object Mode
+                bpy.ops.object.mode_set(mode='OBJECT')
+                bpy.ops.wm.link(
+                    filepath=str(path), directory=str(path) + "/Object", filename=pg.lib_objects
+                )
+                for obj in context.view_layer.objects:
+                    obj.select_set(False)
+                return {"FINISHED"}
+            elif pg.lib_mode == "COLLECTIONS":
+                bpy.ops.wm.link(
+                    filepath=str(path), directory=str(path) + "/Collection", filename=pg.lib_collections
+                )
+                for obj in context.view_layer.objects:
+                    obj.select_set(False)
+                return {"FINISHED"}
+            elif pg.lib_mode == "MATERIALS":
+                bpy.ops.wm.link(
+                    filepath=str(path), directory=str(path) + "/Material", filename=pg.lib_materials
+                )
+                return {"FINISHED"}
+        else:
+            errmsg = PDT_ERR_NO_LIBRARY
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
diff --git a/precision_drawing_tools/pdt_menus.py b/precision_drawing_tools/pdt_menus.py
new file mode 100644
index 0000000000000000000000000000000000000000..0949f7a08b5c4835aaa6e66877a16f2a710ddd85
--- /dev/null
+++ b/precision_drawing_tools/pdt_menus.py
@@ -0,0 +1,330 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+from bpy.types import Panel
+from .pdt_msg_strings import (
+    PDT_LAB_ABS,
+    PDT_LAB_AD2D,
+    PDT_LAB_AD3D,
+    PDT_LAB_ALLACTIVE,
+    PDT_LAB_ANGLEVALUE,
+    PDT_LAB_ARCCENTRE,
+    PDT_LAB_BISECT,
+    PDT_LAB_CVALUE,
+    PDT_LAB_DEL,
+    PDT_LAB_DIR,
+    PDT_LAB_DISVALUE,
+    PDT_LAB_EDGETOEFACE,
+    PDT_LAB_FILLET,
+    PDT_LAB_FLIPANGLE,
+    PDT_LAB_FLIPPERCENT,
+    PDT_LAB_INTERSECT,
+    PDT_LAB_INTERSETALL,
+    PDT_LAB_JOIN2VERTS,
+    PDT_LAB_MODE,
+    PDT_LAB_NOR,
+    PDT_LAB_OPERATION,
+    PDT_LAB_ORDER,
+    PDT_LAB_ORIGINCURSOR,
+    PDT_LAB_PERCENT,
+    PDT_LAB_PERCENTS,
+    PDT_LAB_PIVOTALPHA,
+    PDT_LAB_PIVOTLOC,
+    PDT_LAB_PIVOTLOCH,
+    PDT_LAB_PIVOTSIZE,
+    PDT_LAB_PIVOTWIDTH,
+    PDT_LAB_PLANE,
+    PDT_LAB_PROFILE,
+    PDT_LAB_RADIUS,
+    PDT_LAB_SEGMENTS,
+    PDT_LAB_TAPER,
+    PDT_LAB_TAPERAXES,
+    PDT_LAB_TOOLS,
+    PDT_LAB_USEVERTS,
+    PDT_LAB_VARIABLES
+)
+
+
+# PDT Panel menus
+#
+class PDT_PT_PanelDesign(Panel):
+    bl_idname = "PDT_PT_PanelDesign"
+    bl_label = "PDT Design"
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "UI"
+    bl_category = "PDT"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        pdt_pg = context.scene.pdt_pg
+        row = layout.row()
+        col = row.column()
+        col.prop(pdt_pg, "plane", text=PDT_LAB_PLANE)
+        col = row.column()
+        col.prop(pdt_pg, "select", text=PDT_LAB_MODE)
+        box = layout.box()
+        row = box.row()
+        row.prop(pdt_pg, "operation", text=PDT_LAB_OPERATION)
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.absolute", icon="EMPTY_AXIS", text=PDT_LAB_ABS)
+        col = row.column()
+        col.operator("pdt.delta", icon="EMPTY_AXIS", text=PDT_LAB_DEL)
+        col = row.column()
+        col.operator("pdt.distance", icon="EMPTY_AXIS", text=PDT_LAB_DIR)
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.percent", text=PDT_LAB_PERCENT)
+        col = row.column()
+        col.operator("pdt.normal", text=PDT_LAB_NOR)
+        col = row.column()
+        col.operator("pdt.centre", text=PDT_LAB_ARCCENTRE)
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.intersect", text=PDT_LAB_INTERSECT)
+        col = row.column()
+        col.prop(pdt_pg, "object_search_string", text=PDT_LAB_ORDER)
+        row = box.row()
+        col = row.column()
+        col.prop(pdt_pg, "flip_angle", text=PDT_LAB_FLIPANGLE)
+        col = row.column()
+        col.prop(pdt_pg, "flip_percent", text=PDT_LAB_FLIPPERCENT)
+        col = row.column()
+        col.prop(pdt_pg, "extend", text=PDT_LAB_ALLACTIVE)
+        box = layout.box()
+        row = box.row()
+        row.label(text=PDT_LAB_VARIABLES)
+        row = box.row()
+        row.prop(pdt_pg, "cartesian_coords", text=PDT_LAB_CVALUE)
+        row = box.row()
+        col = row.column()
+        col.prop(pdt_pg, "distance", text=PDT_LAB_DISVALUE)
+        col = row.column()
+        col.prop(pdt_pg, "angle", text=PDT_LAB_ANGLEVALUE)
+        col = row.column()
+        col.prop(pdt_pg, "percent", text=PDT_LAB_PERCENTS)
+        box = layout.box()
+        row = box.row()
+        row.label(text=PDT_LAB_TOOLS)
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.angle2", text=PDT_LAB_AD2D)
+        col = row.column()
+        col.operator("pdt.angle3", text=PDT_LAB_AD3D)
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.join", text=PDT_LAB_JOIN2VERTS)
+        col = row.column()
+        col.operator("pdt.origin", text=PDT_LAB_ORIGINCURSOR)
+        row = box.row()
+        col = row.column()
+        col.prop(pdt_pg, "taper", text=PDT_LAB_TAPERAXES)
+        col = row.column()
+        col.operator("pdt.taper", text=PDT_LAB_TAPER)
+        # New for 1.1.5
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.intersectall", text=PDT_LAB_INTERSETALL)
+        col = row.column()
+        col.operator("pdt.linetobisect", text=PDT_LAB_BISECT)
+        col = row.column()
+        col.operator("pdt.edge_to_face", text=PDT_LAB_EDGETOEFACE)
+        #
+        # Add Fillet Tool
+        row = box.row()
+        col = row.column()
+        col.operator("pdt.fillet", text=PDT_LAB_FILLET)
+        col = row.column()
+        col.prop(pdt_pg, "fillet_segments", text=PDT_LAB_SEGMENTS)
+        col = row.column()
+        col.prop(pdt_pg, "fillet_vertices_only", text=PDT_LAB_USEVERTS)
+        row = box.row()
+        col = row.column()
+        col.prop(pdt_pg, "fillet_radius", text=PDT_LAB_RADIUS)
+        col = row.column()
+        col.prop(pdt_pg, "fillet_profile", text=PDT_LAB_PROFILE)
+
+
+class PDT_PT_PanelPivotPoint(Panel):
+    bl_idname = "PDT_PT_PanelPivotPoint"
+    bl_label = "PDT Pivot Point"
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "UI"
+    bl_category = "PDT"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        pdt_pg = context.scene.pdt_pg
+        layout = self.layout
+        row = layout.row()
+        split = row.split(factor=0.4, align=True)
+        if context.window_manager.pdt_run_opengl is False:
+            icon = "PLAY"
+            txt = "Show"
+        else:
+            icon = "PAUSE"
+            txt = "Hide"
+        split.operator("pdt.modaldraw", icon=icon, text=txt)
+        split.prop(pdt_pg, "pivot_size", text=PDT_LAB_PIVOTSIZE)
+        split.prop(pdt_pg, "pivot_width", text=PDT_LAB_PIVOTWIDTH)
+        split.prop(pdt_pg, "pivot_alpha", text=PDT_LAB_PIVOTALPHA)
+        row = layout.row()
+        row.label(text=PDT_LAB_PIVOTLOCH)
+        row = layout.row()
+        row.prop(pdt_pg, "pivot_loc", text=PDT_LAB_PIVOTLOC)
+        row = layout.row()
+        col = row.column()
+        col.operator("pdt.pivotselected", icon="EMPTY_AXIS", text="Selection")
+        col = row.column()
+        col.operator("pdt.pivotcursor", icon="EMPTY_AXIS", text="Cursor")
+        col = row.column()
+        col.operator("pdt.pivotorigin", icon="EMPTY_AXIS", text="Origin")
+        row = layout.row()
+        col = row.column()
+        col.operator("pdt.viewplanerot", icon="EMPTY_AXIS", text="Rotate")
+        col = row.column()
+        col.prop(pdt_pg, "pivot_ang", text="Angle")
+        row = layout.row()
+        col = row.column()
+        col.operator("pdt.viewscale", icon="EMPTY_AXIS", text="Scale")
+        col = row.column()
+        col.operator("pdt.cursorpivot", icon="EMPTY_AXIS", text="Cursor To Pivot")
+        row = layout.row()
+        col = row.column()
+        col.prop(pdt_pg, "pivot_dis", text="Scale Distance")
+        col = row.column()
+        col.prop(pdt_pg, "distance", text="System Distance")
+        row = layout.row()
+        row.label(text="Pivot Point Scale Factors")
+        row = layout.row()
+        row.prop(pdt_pg, "pivot_scale", text="")
+        row = layout.row()
+        col = row.column()
+        col.operator("pdt.pivotwrite", icon="FILE_TICK", text="PP Write")
+        col = row.column()
+        col.operator("pdt.pivotread", icon="FILE", text="PP Read")
+
+
+class PDT_PT_PanelPartsLibrary(Panel):
+    bl_idname = "PDT_PT_PanelPartsLibrary"
+    bl_label = "PDT Parts Library"
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "UI"
+    bl_category = "PDT"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        pdt_pg = context.scene.pdt_pg
+        row = layout.row()
+        col = row.column()
+        col.operator("pdt.append", text="Append")
+        col = row.column()
+        col.operator("pdt.link", text="Link")
+        col = row.column()
+        col.prop(pdt_pg, "lib_mode", text="")
+        box = layout.box()
+        row = box.row()
+        col = row.column()
+        col.label(text="Objects")
+        col = row.column()
+        col.prop(pdt_pg, "object_search_string")
+        row = box.row()
+        row.prop(pdt_pg, "lib_objects", text="")
+        box = layout.box()
+        row = box.row()
+        col = row.column()
+        col.label(text="Collections")
+        col = row.column()
+        col.prop(pdt_pg, "collection_search_string")
+        row = box.row()
+        row.prop(pdt_pg, "lib_collections", text="")
+        box = layout.box()
+        row = box.row()
+        col = row.column()
+        col.label(text="Materials")
+        col = row.column()
+        col.prop(pdt_pg, "material_search_string")
+        row = box.row()
+        row.prop(pdt_pg, "lib_materials", text="")
+        row = box.row()
+        row.operator("pdt.lib_show", text="Show Library File", icon='INFO')
+
+
+class PDT_PT_PanelViewControl(Panel):
+    bl_idname = "PDT_PT_PanelViewControl"
+    bl_label = "PDT View Control"
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "UI"
+    bl_category = "PDT"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        pdt_pg = context.scene.pdt_pg
+        box = layout.box()
+        row = box.row()
+        col = row.column()
+        col.label(text="View Rotation")
+        col = row.column()
+        col.operator("pdt.viewrot", text="Rotate Abs")
+        row = box.row()
+        row.prop(pdt_pg, "rotation_coords", text="Rotation")
+        row = box.row()
+        col = row.column()
+        col.prop(pdt_pg, "vrotangle", text="Angle")
+        col = row.column()
+        col.operator("pdt.viewleft", text="", icon="TRIA_LEFT")
+        col = row.column()
+        col.operator("pdt.viewright", text="", icon="TRIA_RIGHT")
+        col = row.column()
+        col.operator("pdt.viewup", text="", icon="TRIA_UP")
+        col = row.column()
+        col.operator("pdt.viewdown", text="", icon="TRIA_DOWN")
+        col = row.column()
+        col.operator("pdt.viewroll", text="", icon="RECOVER_LAST")
+        row = box.row()
+        row.operator("pdt.viewiso", text="Isometric View")
+
+
+class PDT_PT_PanelCommandLine(Panel):
+    bl_idname = "PDT_PT_PanelCommandLine"
+    bl_label = "PDT Command Line (? for help)"
+    bl_space_type = "VIEW_3D"
+    bl_region_type = "UI"
+    bl_category = "PDT"
+    bl_options = {'DEFAULT_CLOSED'}
+
+    def draw(self, context):
+        layout = self.layout
+        pdt_pg = context.scene.pdt_pg
+        row = layout.row()
+        col = row.column()
+        col.prop(pdt_pg, "plane", text="Plane")
+        col = row.column()
+        col.prop(pdt_pg, "select", text="Mode")
+        row = layout.row()
+        row.label(text="Comand Line, uses Plane & Mode Options")
+        row = layout.row()
+        row.prop(pdt_pg, "command", text="")
diff --git a/precision_drawing_tools/pdt_msg_strings.py b/precision_drawing_tools/pdt_msg_strings.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c6cab25b907461b51eb00ea7e27ebab7678879b
--- /dev/null
+++ b/precision_drawing_tools/pdt_msg_strings.py
@@ -0,0 +1,171 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+# English Version
+#
+# If you edit this file do not change any of the PDT_ format, just the Text Value in "'s
+# Do not delete any of the PDT_ lines
+#
+# Menu Labels
+#
+PDT_LAB_ABS           = "Absolute"    # "Global"
+PDT_LAB_DEL           = "Delta"       # "Relative"
+PDT_LAB_DIR           = "Direction"   # "Polar"
+PDT_LAB_NOR           = "Normal"      # "Perpendicular"
+PDT_LAB_ARCCENTRE     = "Arc Centre"
+PDT_LAB_PLANE         = "Plane"
+PDT_LAB_MODE          = "Mode"
+PDT_LAB_OPERATION     = "Operation"
+PDT_LAB_PERCENT       = "Percent"
+PDT_LAB_INTERSECT     = "Intersect"   # "Convergance"
+PDT_LAB_ORDER         = "Order"
+PDT_LAB_FLIPANGLE     = "Flip Angle"
+PDT_LAB_FLIPPERCENT   = "Flip %"
+PDT_LAB_ALLACTIVE     = "All/Active"
+PDT_LAB_VARIABLES     = "Coordinates/Delta Offsets & Other Variables"
+PDT_LAB_CVALUE        = ""            # Intentionally left blank
+PDT_LAB_DISVALUE      = "Distance"
+PDT_LAB_ANGLEVALUE    = "Angle"
+PDT_LAB_PERCENTS      = "%"
+PDT_LAB_TOOLS         = "Tools"
+PDT_LAB_JOIN2VERTS    = "Join 2 Verts"
+PDT_LAB_ORIGINCURSOR  = "Origin To Cursor"
+PDT_LAB_AD2D          = "Set A/D 2D"
+PDT_LAB_AD3D          = "Set A/D 3D"
+PDT_LAB_TAPERAXES     = ""            # Intentionally left blank
+PDT_LAB_TAPER         = "Taper"
+PDT_LAB_INTERSETALL   = "Intersect All"
+PDT_LAB_BISECT        = "Bisect"
+PDT_LAB_EDGETOEFACE   = "Edge-Face"
+PDT_LAB_FILLET        = "Fillet"
+PDT_LAB_SEGMENTS      = "Segments"
+PDT_LAB_USEVERTS      = "Use Verts"
+PDT_LAB_RADIUS        = "Radius"
+PDT_LAB_PROFILE       = "Profile"
+PDT_LAB_PIVOTSIZE     = ""            # Intentionally left blank
+PDT_LAB_PIVOTWIDTH    = ""            # Intentionally left blank
+PDT_LAB_PIVOTALPHA    = ""            # Intentionally left blank
+PDT_LAB_PIVOTLOC      = ""            # Intentionally left blank
+PDT_LAB_PIVOTLOCH     = "Pivot Point Location"
+#
+# Error Message
+#
+PDT_ERR_NO_ACT_OBJ    = "No Active Object - Please Select an Object"
+PDT_ERR_NO_ACT_VERT   = "No Active Vertex - Select One Vertex Individually"
+PDT_ERR_NO_SEL_GEOM   = "No Geometry/Objects Selected"
+PDT_ERR_NO_ACT_VERTS  = "No Selected Geometry - Please Select some Geometry"
+PDT_ERR_NON_VALID     = "is Not a Valid Option in Selected Object's Mode for Command:"
+PDT_ERR_VERT_MODE     = "Work in Vertex Mode for this Function"
+PDT_ERR_NOPPLOC       = "Custom Property PDT_PP_LOC for this object not found, have you Written it yet?"
+PDT_ERR_NO_LIBRARY    = "PDT Library Blend File (parts_library.blend) is Missing from Addons/clockworxpdt Folder"
+
+PDT_ERR_SEL_1_VERTI   = "Select at least 1 Vertex Individually (Currently selected:"
+PDT_ERR_SEL_1_VERT    = "Select at least 1 Vertex (Currently selected:"
+PDT_ERR_SEL_2_VERTI   = "Select at least 2 Vertices Individually (Currently selected:"
+PDT_ERR_SEL_2_VERTIO  = "Select Exactly 2 Vertices Individually (Currently selected:"
+PDT_ERR_SEL_2_VERTS   = "Select Exactly 2 Vertices (Currently selected:"
+PDT_ERR_SEL_3_VERTS   = "Select Exactly 3 Vertices (Currently selected:"
+PDT_ERR_SEL_3_VERTIO  = "Select Exactly 3 Vertices Individually (Currently selected:"
+PDT_ERR_SEL_2_V_1_E   = "Select 2 Vertices Individually, or 1 Edge (Currently selected:"
+PDT_ERR_SEL_4_VERTS   = "Select 4 Vertices Individually, or 2 Edges (Currently selected:"
+PDT_ERR_SEL_1_E_1_F   = "Select 1 Face and 1 Detached Edge"
+
+PDT_ERR_SEL_1_EDGE    = "Select Exactly 1 Edge (Currently selected:"
+PDT_ERR_SEL_1_EDGEM   = "Select at least 1 Edge (Currently selected:"
+
+PDT_ERR_SEL_1_OBJ     = "Select Exactly 1 Object (Currently selected:"
+PDT_ERR_SEL_2_OBJS    = "Select Exactly 2 Objects (Currently selected:"
+PDT_ERR_SEL_3_OBJS    = "Select Exactly 3 Objects (Currently selected:"
+PDT_ERR_SEL_4_OBJS    = "Select Exactly 4 Objects (Currently selected:"
+
+PDT_ERR_FACE_SEL      = "You have a Face Selected, this would have ruined the Topology"
+
+PDT_ERR_INT_LINES     = "Implied Lines Do Not Intersect in"
+PDT_ERR_INT_NO_ALL    = "Active Vertex was not Closest to Intersection and All/Act was not Selected"
+PDT_ERR_STRIGHT_LINE  = "Selected Points all lie in a Straight Line"
+PDT_ERR_CONNECTED     = "Vertices are already Connected"
+PDT_ERR_EDIT_MODE     = "Only Works in EDIT Mode (Current mode:"
+PDT_ERR_EDOB_MODE     = "Only Works in EDIT, or OBJECT Modes (Current mode:"
+PDT_ERR_TAPER_ANG     = "Angle must be in Range -80 to +80 (Currently set to:"
+PDT_ERR_TAPER_SEL     = "Select at Least 2 Vertices Individually - Active is Rotation Point (Currently selected:"
+PDT_ERR_NO3DVIEW      = "View3D not found, cannot run operator"
+PDT_ERR_SCALEZERO     = "Scale Distance is 0"
+
+PDT_ERR_CHARS_NUM     = "Bad Command Format, not enough Characters"
+PDT_ERR_BADFLETTER    = "Bad Operator (1st Letter); C D E F G N M P S V or ? only"
+PDT_ERR_BADSLETTER    = "Bad Mode (2nd Letter); A D I or P only (+ X Y & Z for Maths) (+ V & G for Fillet)"
+PDT_ERR_BADMATHS      = "Not a Valid Mathematical Expression!"
+PDT_ERR_BADCOORDL     = "X Y & Z Not permitted in anything other than Maths Operations"
+PDT_ERR_BAD1VALS      = "Bad Command - 1 Value needed"
+PDT_ERR_BAD2VALS      = "Bad Command - 2 Values needed"
+PDT_ERR_BAD3VALS      = "Bad Command - 3 Coords needed"
+PDT_ERR_ADDVEDIT      = "Only Add New Vertices in Edit Mode"
+PDT_ERR_SPLITEDIT     = "Only Split Edges in Edit Mode"
+PDT_ERR_EXTEDIT       = "Only Extrude Vertices in Edit Mode"
+PDT_ERR_DUPEDIT       = "Only Duplicate Geometry in Edit Mode"
+PDT_ERR_FILEDIT       = "Only Fillet Geometry in Edit Mode"
+PDT_ERR_NOCOMMAS      = "No commas allowed in Maths Command"
+
+PDT_ERR_2CPNPE        = "Select 2 Co-Planar Non-Parallel Edges"
+PDT_ERR_NCEDGES       = "Edges must be Co-Planar Non-Parallel Edges, Selected Edges aren't"
+PDT_ERR_1EDGE1FACE    = "Select 1 face and 1 Detached Edge"
+PDT_ERR_NOINT         = "No Intersection Found, see the Info Panel for Details"
+
+# Info messages
+#
+PDT_INF_OBJ_MOVED     = "Active Object Moved to Intersection, "
+
+# Confirm Messages
+#
+PDT_CON_AREYOURSURE   = "Are You Sure About This?"
+
+# Descriptions
+#
+PDT_DES_COORDS        = "Cartesian Inputs"
+PDT_DES_OFFDIS        = "Offset Distance"
+PDT_DES_OFFANG        = "Offset Angle"
+PDT_DES_OFFPER        = "Offset Percentage"
+PDT_DES_WORPLANE      = "Choose Working Plane"
+PDT_DES_MOVESEL       = "Select Move Mode"
+PDT_DES_OPMODE        = "Select Operation Mode"
+PDT_DES_ROTMOVAX      = "Rotational Axis - Movement Axis"
+PDT_DES_FLIPANG       = "Flip Angle 180 degrees"
+PDT_DES_FLIPPER       = "Flip Percent to 100 - %"
+PDT_DES_TRIM          = "Trim/Extend only Active Vertex, or All"
+PDT_DES_LIBOBS        = "Objects in Library"
+PDT_DES_LIBCOLS       = "Collections in Library"
+PDT_DES_LIBMATS       = "Materials in Library"
+PDT_DES_LIBMODE       = "Library Mode"
+PDT_DES_LIBSER        = "Enter A Search String (Contained)"
+PDT_DES_OBORDER       = "Object Order to Lines"
+PDT_DES_VALIDLET      = "Valid 1st letters; C D E G N P S V, Valid 2nd letters: A D I P"
+PDT_DES_PPLOC         = "Location of PivotPoint"
+PDT_DES_PPSCALEFAC    = "Scale Factors"
+PDT_DES_PPSIZE        = "Pivot Size Factor"
+PDT_DES_PPWIDTH       = "Pivot Line Width in Pixels"
+PDT_DES_PPTRANS       = "Pivot Point Transparency"
+PDT_DES_PIVOTDIS      = "Input Distance to Compare with Sytem Distance to set Scales"
+PDT_DES_FILLETRAD     = "Fillet Radius"
+PDT_DES_FILLETSEG     = "Number of Fillet Segments"
+PDT_DES_FILLETPROF    = "Fillet Profile"
+PDT_DES_FILLETVERTS   = "Use Vertices, or Edges, Set to False for Extruded Geometry"
diff --git a/precision_drawing_tools/pdt_pivot_point.py b/precision_drawing_tools/pdt_pivot_point.py
new file mode 100644
index 0000000000000000000000000000000000000000..f09ac1da661ee9aeb6fad6d65bf4ac090c7fc396
--- /dev/null
+++ b/precision_drawing_tools/pdt_pivot_point.py
@@ -0,0 +1,442 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+import bpy
+import bmesh
+from bpy.types import Operator, SpaceView3D
+from mathutils import Vector, Matrix
+from math import pi
+from .pdt_functions import viewCoords, drawCallback3D
+from .pdt_msg_strings import (
+    PDT_CON_AREYOURSURE,
+    PDT_ERR_EDIT_MODE,
+    PDT_ERR_NO3DVIEW,
+    PDT_ERR_NOPPLOC,
+    PDT_ERR_NO_ACT_OBJ,
+    PDT_ERR_NO_SEL_GEOM
+)
+
+
+class PDT_OT_ModalDrawOperator(bpy.types.Operator):
+    """Show/Hide Pivot Point."""
+
+    bl_idname = "pdt.modaldraw"
+    bl_label = "PDT Modal Draw"
+
+    _handle = None  # keep function handler
+
+    @staticmethod
+    def handle_add(self, context):
+        """Draw Pivot Point Graphic if not displayed.
+
+        Draws 7 element Pivot Point Graphic
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Nothing.
+        """
+
+        if PDT_OT_ModalDrawOperator._handle is None:
+            PDT_OT_ModalDrawOperator._handle = SpaceView3D.draw_handler_add(
+                drawCallback3D, (self, context), "WINDOW", "POST_VIEW"
+            )
+            context.window_manager.pdt_run_opengl = True
+
+    @staticmethod
+    def handle_remove(self, context):
+        """Remove Pivot Point Graphic if displayed.
+
+        Removes 7 element Pivot Point Graphic
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Nothing.
+        """
+
+        if PDT_OT_ModalDrawOperator._handle is not None:
+            SpaceView3D.draw_handler_remove(PDT_OT_ModalDrawOperator._handle, "WINDOW")
+        PDT_OT_ModalDrawOperator._handle = None
+        context.window_manager.pdt_run_opengl = False
+
+    def execute(self, context):
+        """Pivot Point Show/Hide Button Function.
+
+        Operational execute function for Show/Hide Pivot Point function
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        if context.area.type == "VIEW_3D":
+            if context.window_manager.pdt_run_opengl is False:
+                self.handle_add(self, context)
+                context.area.tag_redraw()
+            else:
+                self.handle_remove(self, context)
+                context.area.tag_redraw()
+
+            return {"FINISHED"}
+        else:
+            self.report({"ERROR"}, PDT_ERR_NO3DVIEW)
+
+        return {"CANCELLED"}
+
+
+class PDT_OT_ViewPlaneRotate(Operator):
+    """Rotate Selected Vertices about Pivot Point in View Plane."""
+
+    bl_idname = "pdt.viewplanerot"
+    bl_label = "PDT View Rotate"
+
+    @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):
+        """Rotate Selected Vertices about Pivot Point.
+
+        Rotates any selected vertices about the Pivot Point
+        in View Oriented coordinates, works in any view orientation.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.pivot_loc, pg.pivot_ang scene variables
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj = bpy.context.view_layer.objects.active
+        if obj is None:
+            self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
+            return {"FINISHED"}
+        if obj.mode != "EDIT":
+            errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        bm = bmesh.from_edit_mesh(obj.data)
+        v1 = Vector((0, 0, 0))
+        v2 = viewCoords(0, 0, 1)
+        axis = (v2 - v1).normalized()
+        rot = Matrix.Rotation((pg.pivot_ang * pi / 180), 4, axis)
+        verts = verts = [v for v in bm.verts if v.select]
+        bmesh.ops.rotate(
+            bm, cent=pg.pivot_loc - obj.matrix_world.decompose()[0], matrix=rot, verts=verts
+        )
+        bmesh.update_edit_mesh(obj.data)
+        return {"FINISHED"}
+
+
+class PDT_OT_ViewPlaneScale(Operator):
+    """Scale Selected Vertices about Pivot Point."""
+
+    bl_idname = "pdt.viewscale"
+    bl_label = "PDT View Scale"
+
+    @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):
+        """Scales Selected Vertices about Pivot Point.
+
+        Scales any selected vertices about the Pivot Point
+        in View Oriented coordinates, works in any view orientation
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Note:
+            Uses pg.pivot_loc, pg.pivot_scale scene variables
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj = bpy.context.view_layer.objects.active
+        if obj is None:
+            self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
+            return {"FINISHED"}
+        if obj.mode != "EDIT":
+            errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        bm = bmesh.from_edit_mesh(obj.data)
+        verts = verts = [v for v in bm.verts if v.select]
+        for v in verts:
+            dx = (pg.pivot_loc.x - obj.matrix_world.decompose()[0].x - v.co.x) * (
+                1 - pg.pivot_scale.x
+            )
+            dy = (pg.pivot_loc.y - obj.matrix_world.decompose()[0].y - v.co.y) * (
+                1 - pg.pivot_scale.y
+            )
+            dz = (pg.pivot_loc.z - obj.matrix_world.decompose()[0].z - v.co.z) * (
+                1 - pg.pivot_scale.z
+            )
+            dv = Vector((dx, dy, dz))
+            v.co = v.co + dv
+        bmesh.update_edit_mesh(obj.data)
+        return {"FINISHED"}
+
+
+class PDT_OT_PivotToCursor(Operator):
+    """Set The Pivot Point to Cursor Location."""
+
+    bl_idname = "pdt.pivotcursor"
+    bl_label = "PDT Pivot To Cursor"
+
+    def execute(self, context):
+        """Moves Pivot Point to Cursor Location.
+
+        Moves Pivot Point to Cursor Location in active scene
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+             Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        pg.pivot_loc = scene.cursor.location
+        return {"FINISHED"}
+
+
+class PDT_OT_CursorToPivot(Operator):
+    """Set The Cursor Location to Pivot Point."""
+
+    bl_idname = "pdt.cursorpivot"
+    bl_label = "PDT Cursor To Pivot"
+
+    def execute(self, context):
+        """Moves Cursor to Pivot Point Location.
+
+        Moves Cursor to Pivot Point Location in active scene
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        scene.cursor.location = pg.pivot_loc
+        return {"FINISHED"}
+
+
+class PDT_OT_PivotSelected(Operator):
+    """Set Pivot Point to Selected Geometry."""
+
+    bl_idname = "pdt.pivotselected"
+    bl_label = "PDT Pivot to Selected"
+
+    @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):
+        """Moves Pivot Point centroid of Selected Geometry.
+
+        Moves Pivot Point centroid of Selected Geometry in active scene
+        using Snap_Cursor_To_Selected, then puts cursor back to original location.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        obj = bpy.context.view_layer.objects.active
+        if obj is None:
+            self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
+            return {"FINISHED"}
+        if obj.mode != "EDIT":
+            errmsg = f"{PDT_ERR_EDIT_MODE} {obj.mode})"
+            self.report({"ERROR"}, errmsg)
+            return {"FINISHED"}
+        bm = bmesh.from_edit_mesh(obj.data)
+        verts = verts = [v for v in bm.verts if v.select]
+        if len(verts) > 0:
+            old_cursor_loc = scene.cursor.location.copy()
+            bpy.ops.view3d.snap_cursor_to_selected()
+            pg.pivot_loc = scene.cursor.location
+            scene.cursor.location = old_cursor_loc
+            return {"FINISHED"}
+        else:
+            self.report({"ERROR"}, PDT_ERR_NO_SEL_GEOM)
+            return {"FINISHED"}
+
+
+class PDT_OT_PivotOrigin(Operator):
+    """Set Pivot Point at Object Origin."""
+
+    bl_idname = "pdt.pivotorigin"
+    bl_label = "PDT Pivot to Object Origin"
+
+    @classmethod
+    def poll(cls, context):
+        ob = context.object
+        if ob is None:
+            return False
+        return all([bool(ob), ob.type == "MESH"])
+
+    def execute(self, context):
+        """Moves Pivot Point to Object Origin.
+
+        Moves Pivot Point to Object Origin in active scene
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj = bpy.context.view_layer.objects.active
+        if obj is None:
+            self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
+            return {"FINISHED"}
+        obj_loc = obj.matrix_world.decompose()[0]
+        pg.pivot_loc = obj_loc
+        return {"FINISHED"}
+
+
+class PDT_OT_PivotWrite(Operator):
+    """Write Pivot Point Location to Object."""
+
+    bl_idname = "pdt.pivotwrite"
+    bl_label = "PDT Write PP to Object?"
+
+    @classmethod
+    def poll(cls, context):
+        ob = context.object
+        if ob is None:
+            return False
+        return all([bool(ob), ob.type == "MESH"])
+
+    def execute(self, context):
+        """Writes Pivot Point Location to Object's Custom Properties.
+
+        Writes Pivot Point Location to Object's Custom Properties
+        as Vector to 'PDT_PP_LOC' - Requires Confirmation through dialogue
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Note:
+            Uses pg.pivot_loc scene variable
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj = bpy.context.view_layer.objects.active
+        if obj is None:
+            self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
+            return {"FINISHED"}
+        obj["PDT_PP_LOC"] = pg.pivot_loc
+        return {"FINISHED"}
+
+    def invoke(self, context, event):
+        return context.window_manager.invoke_props_dialog(self)
+
+    def draw(self, context):
+        row = self.layout
+        row.label(text=PDT_CON_AREYOURSURE)
+
+
+class PDT_OT_PivotRead(Operator):
+    """Read Pivot Point Location from Object."""
+
+    bl_idname = "pdt.pivotread"
+    bl_label = "PDT Read PP"
+
+    @classmethod
+    def poll(cls, context):
+        ob = context.object
+        if ob is None:
+            return False
+        return all([bool(ob), ob.type == "MESH"])
+
+    def execute(self, context):
+        """Reads Pivot Point Location from Object's Custom Properties.
+
+        Sets Pivot Point Location from Object's Custom Properties
+        using 'PDT_PP_LOC'
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Note:
+            Uses pg.pivot_loc scene variable
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        obj = bpy.context.view_layer.objects.active
+        if obj is None:
+            self.report({"ERROR"}, PDT_ERR_NO_ACT_OBJ)
+            return {"FINISHED"}
+        if "PDT_PP_LOC" in obj:
+            pg.pivot_loc = obj["PDT_PP_LOC"]
+            return {"FINISHED"}
+        else:
+            self.report({"ERROR"}, PDT_ERR_NOPPLOC)
+            return {"FINISHED"}
diff --git a/precision_drawing_tools/pdt_view.py b/precision_drawing_tools/pdt_view.py
new file mode 100644
index 0000000000000000000000000000000000000000..77919877936b7c577defcd85f8a43b21ac6e9799
--- /dev/null
+++ b/precision_drawing_tools/pdt_view.py
@@ -0,0 +1,241 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ***** END GPL LICENCE BLOCK *****
+#
+# -----------------------------------------------------------------------
+# Author: Alan Odom (Clockmender), Rune Morling (ermo) Copyright (c) 2019
+# -----------------------------------------------------------------------
+#
+import bpy
+from bpy.types import Operator
+from math import pi
+from mathutils import Quaternion
+from .pdt_functions import euler_to_quaternion
+
+
+class PDT_OT_ViewRot(Operator):
+    """Rotate View using X Y Z Absolute Rotations."""
+
+    bl_idname = "pdt.viewrot"
+    bl_label = "Rotate View"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """View Rotation by Absolute Values.
+
+        Rotations are converted to 3x3 Quaternion Rotation Matrix.
+        This is an Absolute Rotation, not an Incremental Orbit.
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.rotation_coords scene variables
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            roll_value = euler_to_quaternion(
+                pg.rotation_coords.x * pi / 180,
+                pg.rotation_coords.y * pi / 180,
+                pg.rotation_coords.z * pi / 180
+            )
+            areas[0].spaces.active.region_3d.view_rotation = roll_value
+        return {"FINISHED"}
+
+
+class PDT_OT_vRotL(Operator):
+    """Orbit View to Left by Angle."""
+
+    bl_idname = "pdt.viewleft"
+    bl_label = "Rotate Left"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """View Orbit Left by Delta Value.
+
+        Orbits view to the left about its vertical axis
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.vrotangle scene variable
+
+        Returns: Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            bpy.ops.view3d.view_orbit(angle=(pg.vrotangle * pi / 180), type="ORBITLEFT")
+        return {"FINISHED"}
+
+
+class PDT_OT_vRotR(Operator):
+    """Orbit View to Right by Angle."""
+
+    bl_idname = "pdt.viewright"
+    bl_label = "Rotate Right"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """View Orbit Right by Delta Value.
+
+        Orbits view to the right about its vertical axis
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.vrotangle scene variable
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            bpy.ops.view3d.view_orbit(angle=(pg.vrotangle * pi / 180), type="ORBITRIGHT")
+        return {"FINISHED"}
+
+
+class PDT_OT_vRotU(Operator):
+    """Orbit View to Up by Angle."""
+
+    bl_idname = "pdt.viewup"
+    bl_label = "Rotate Up"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """View Orbit Up by Delta Value.
+
+        Orbits view up about its horizontal axis
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.vrotangle scene variable
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            bpy.ops.view3d.view_orbit(angle=(pg.vrotangle * pi / 180), type="ORBITUP")
+        return {"FINISHED"}
+
+
+class PDT_OT_vRotD(Operator):
+    """Orbit View to Down by Angle."""
+
+    bl_idname = "pdt.viewdown"
+    bl_label = "Rotate Down"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """View Orbit Down by Delta Value.
+
+        Orbits view down about its horizontal axis
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.vrotangle scene variable
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            bpy.ops.view3d.view_orbit(angle=(pg.vrotangle * pi / 180), type="ORBITDOWN")
+        return {"FINISHED"}
+
+
+class PDT_OT_vRoll(Operator):
+    """Roll View by Angle."""
+
+    bl_idname = "pdt.viewroll"
+    bl_label = "Roll View"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """View Roll by Delta Value.
+
+        Rolls view about its normal axis
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Notes:
+            Uses pg.vrotangle scene variable
+
+        Returns:
+            Status Set.
+        """
+
+        scene = context.scene
+        pg = scene.pdt_pg
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            bpy.ops.view3d.view_roll(angle=(pg.vrotangle * pi / 180), type="ANGLE")
+        return {"FINISHED"}
+
+
+class PDT_OT_viso(Operator):
+    """Isometric View."""
+
+    bl_idname = "pdt.viewiso"
+    bl_label = "Isometric View"
+    bl_options = {"REGISTER", "UNDO"}
+
+    def execute(self, context):
+        """Set Isometric View.
+
+        Set view orientation to Isometric
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        areas = [a for a in context.screen.areas if a.type == "VIEW_3D"]
+        if len(areas) > 0:
+            # Try working this out in your head!
+            areas[0].spaces.active.region_3d.view_rotation = Quaternion(
+                (0.8205, 0.4247, -0.1759, -0.3399)
+            )
+        return {"FINISHED"}
diff --git a/precision_drawing_tools/pdt_xall.py b/precision_drawing_tools/pdt_xall.py
new file mode 100644
index 0000000000000000000000000000000000000000..f27ae6c50df920ba494a97b52e09edb705408389
--- /dev/null
+++ b/precision_drawing_tools/pdt_xall.py
@@ -0,0 +1,195 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+#
+# <pep8 compliant>
+#
+# ----------------------------------------------------------
+# Author: Zeffii
+# Modified by: Alan Odom (Clockmender) & Rune Morling (ermo)
+# ----------------------------------------------------------
+#
+import bpy
+import bmesh
+from mathutils.geometry import intersect_line_line as LineIntersect
+import itertools
+from collections import defaultdict
+from . import pdt_cad_module as cm
+
+
+def order_points(edge, point_list):
+    """Order these edges from distance to v1, then sandwich the sorted list with v1, v2."""
+    v1, v2 = edge
+
+    def dist(co):
+        return (v1 - co).length
+
+    point_list = sorted(point_list, key=dist)
+    return [v1] + point_list + [v2]
+
+
+def remove_permutations_that_share_a_vertex(bm, permutations):
+    """Get useful Permutations."""
+
+    final_permutations = []
+    for edges in permutations:
+        raw_vert_indices = cm.vertex_indices_from_edges_tuple(bm, edges)
+        if len(set(raw_vert_indices)) < 4:
+            continue
+
+        # reaches this point if they do not share.
+        final_permutations.append(edges)
+
+    return final_permutations
+
+
+def get_valid_permutations(bm, edge_indices):
+    """Get useful Permutations."""
+
+    raw_permutations = itertools.permutations(edge_indices, 2)
+    permutations = [r for r in raw_permutations if r[0] < r[1]]
+    return remove_permutations_that_share_a_vertex(bm, permutations)
+
+
+def can_skip(closest_points, vert_vectors):
+    """Check if the intersection lies on both edges and return True
+    when criteria are not met, and thus this point can be skipped."""
+
+    if not closest_points:
+        return True
+    if not isinstance(closest_points[0].x, float):
+        return True
+    if cm.num_edges_point_lies_on(closest_points[0], vert_vectors) < 2:
+        return True
+
+    # if this distance is larger than than 1.0e-5, we can skip it.
+    cpa, cpb = closest_points
+    return (cpa - cpb).length > 1.0e-5
+
+
+def get_intersection_dictionary(bm, edge_indices):
+    """Return a dictionary of edge indices and points found on those edges."""
+
+    bm.verts.ensure_lookup_table()
+    bm.edges.ensure_lookup_table()
+
+    permutations = get_valid_permutations(bm, edge_indices)
+
+    k = defaultdict(list)
+    d = defaultdict(list)
+
+    for edges in permutations:
+        raw_vert_indices = cm.vertex_indices_from_edges_tuple(bm, edges)
+        vert_vectors = cm.vectors_from_indices(bm, raw_vert_indices)
+
+        points = LineIntersect(*vert_vectors)
+
+        # some can be skipped.    (NaN, None, not on both edges)
+        if can_skip(points, vert_vectors):
+            continue
+
+        # reaches this point only when an intersection happens on both edges.
+        [k[edge].append(points[0]) for edge in edges]
+
+    # k will contain a dict of edge indices and points found on those edges.
+    for edge_idx, unordered_points in k.items():
+        tv1, tv2 = bm.edges[edge_idx].verts
+        v1 = bm.verts[tv1.index].co
+        v2 = bm.verts[tv2.index].co
+        ordered_points = order_points((v1, v2), unordered_points)
+        d[edge_idx].extend(ordered_points)
+
+    return d
+
+
+def update_mesh(bm, int_dict):
+    """Make new geometry (delete old first)."""
+
+    oe = bm.edges
+    ov = bm.verts
+
+    new_verts = []
+    collect = new_verts.extend
+    for _, point_list in int_dict.items():
+        num_edges_to_add = len(point_list) - 1
+        for i in range(num_edges_to_add):
+            a = ov.new(point_list[i])
+            b = ov.new(point_list[i + 1])
+            oe.new((a, b))
+            bm.normal_update()
+            collect([a, b])
+
+    bmesh.ops.delete(bm, geom=[edge for edge in bm.edges if edge.select], context="EDGES")
+    bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001)
+
+
+def unselect_nonintersecting(bm, d_edges, edge_indices):
+    """Deselects Non-Intersection Edges"""
+
+    if len(edge_indices) > len(d_edges):
+        reserved_edges = set(edge_indices) - set(d_edges)
+        for edge in reserved_edges:
+            bm.edges[edge].select = False
+
+
+class PDT_OT_IntersectAllEdges(bpy.types.Operator):
+    """Cut Selected Edges at All Intersections."""
+
+    bl_idname = "pdt.intersectall"
+    bl_label = "Intersect All Edges"
+    bl_options = {"REGISTER", "UNDO"}
+
+    @classmethod
+    def poll(cls, context):
+        ob = context.active_object
+        if ob is None:
+            return False
+        return ob is not None and ob.type == "MESH" and ob.mode == "EDIT"
+
+    def execute(self, context):
+        """Computes All intersections with Crossing Geometry.
+
+        Deletes original edges and replaces with new intersected edges
+
+        Args:
+            context: Blender bpy.context instance.
+
+        Returns:
+            Status Set.
+        """
+
+        # must force edge selection mode here
+        bpy.context.tool_settings.mesh_select_mode = (False, True, False)
+
+        obj = context.active_object
+        if obj.mode == "EDIT":
+            bm = bmesh.from_edit_mesh(obj.data)
+
+            selected_edges = [edge for edge in bm.edges if edge.select]
+            edge_indices = [i.index for i in selected_edges]
+
+            int_dict = get_intersection_dictionary(bm, edge_indices)
+
+            unselect_nonintersecting(bm, int_dict.keys(), edge_indices)
+            update_mesh(bm, int_dict)
+
+            bmesh.update_edit_mesh(obj.data)
+        else:
+            msg = PDT_ERR_EDIT_MODE + obj.mode + ")"
+            self.report({"ERROR"}, msg)
+
+        return {"FINISHED"}