diff --git a/object_facemap_auto/__init__.py b/object_facemap_auto/__init__.py
deleted file mode 100644
index 1085633f9787ee7130863a4f20bef2d0e0cf3b46..0000000000000000000000000000000000000000
--- a/object_facemap_auto/__init__.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# ##### 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 #####
-
-bl_info = {
-    "name": "Auto Face Map Widgets",
-    "author": "Campbell Barton",
-    "version": (1, 0),
-    "blender": (2, 80, 0),
-    "location": "View3D",
-    "description": "Use face-maps in the 3D view when rigged meshes are selected.",
-    "warning": "This is currently a proof of concept.",
-    "doc_url": "",
-    "category": "Rigging",
-}
-
-submodules = (
-    "auto_fmap_widgets",
-    "auto_fmap_ops",
-)
-
-# reload at runtime, for development.
-USE_RELOAD = False
-USE_VERBOSE = False
-
-from bpy.utils import register_submodule_factory
-
-register, unregister = register_submodule_factory(__name__, submodules)
-
-if __name__ == "__main__":
-    register()
diff --git a/object_facemap_auto/auto_fmap_ops.py b/object_facemap_auto/auto_fmap_ops.py
deleted file mode 100644
index 4ed3aef0c8099eb5a7884182217764bdcaa2e492..0000000000000000000000000000000000000000
--- a/object_facemap_auto/auto_fmap_ops.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# ##### 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 #####
-
-import bpy
-from bpy.types import (
-    Operator,
-)
-from bpy.props import (
-    EnumProperty,
-)
-
-from . import USE_RELOAD
-
-
-class MyFaceMapClear(Operator):
-    """Clear face-map transform"""
-    bl_idname = "my_facemap.transform_clear"
-    bl_label = "My Face Map Clear Transform"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    clear_types: EnumProperty(
-        name="Clear Types",
-        options={'ENUM_FLAG'},
-        items=(
-            ('LOCATION', "Location", ""),
-            ('ROTATION', "Rotation", ""),
-            ('SCALE', "Scale", ""),
-        ),
-        description="Clear transform",
-        # default=set(),
-    )
-
-    @classmethod
-    def poll(cls, context):
-        return context.active_object is not None
-
-    def invoke(self, context, _event):
-        self._group = context.manipulator_group
-        return self.execute(context)
-
-    def execute(self, context):
-        # trick since redo wont have manipulator_group
-        group = self._group
-
-        from .auto_fmap_utils import import_reload_or_none
-        auto_fmap_widgets_xform = import_reload_or_none(
-            __package__ + "." + "auto_fmap_widgets_xform", reload=USE_RELOAD,
-        )
-
-        if auto_fmap_widgets_xform is None:
-            return {'CANCELED'}
-
-        for mpr in group.manipulators:
-            ob = mpr.fmap_mesh_object
-            fmap_target = mpr.fmap_target
-            fmap = mpr.fmap
-
-            if mpr.select:
-                if 'LOCATION' in self.clear_types:
-                    auto_fmap_widgets_xform.widget_clear_location(
-                        context, mpr, ob, fmap, fmap_target,
-                    )
-                if 'ROTATION' in self.clear_types:
-                    auto_fmap_widgets_xform.widget_clear_rotation(
-                        context, mpr, ob, fmap, fmap_target,
-                    )
-                if 'SCALE' in self.clear_types:
-                    auto_fmap_widgets_xform.widget_clear_scale(
-                        context, mpr, ob, fmap, fmap_target,
-                    )
-        return {'FINISHED'}
-
-
-classes = (
-    MyFaceMapClear,
-)
-
-
-def register():
-    from bpy.utils import register_class
-    for cls in classes:
-        register_class(cls)
-
-
-def unregister():
-    from bpy.utils import unregister_class
-    for cls in classes:
-        unregister_class(cls)
diff --git a/object_facemap_auto/auto_fmap_utils.py b/object_facemap_auto/auto_fmap_utils.py
deleted file mode 100644
index ba825c0972528570de5b7fc8145180ee8a56331c..0000000000000000000000000000000000000000
--- a/object_facemap_auto/auto_fmap_utils.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# ##### 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 #####
-
-
-# Use so we can develop modules without reloading the add-on.
-
-def import_reload_or_none(name, reload=True):
-    """
-    Import and reload a module.
-    """
-    try:
-        mod = __import__(name)
-        if reload:
-            import importlib
-            mod = importlib.reload(mod)
-        import sys
-        return sys.modules[name]
-    except Exception:
-        import traceback
-        traceback.print_exc()
-        return None
diff --git a/object_facemap_auto/auto_fmap_widgets.py b/object_facemap_auto/auto_fmap_widgets.py
deleted file mode 100644
index 24ddf103229d8c25faaf6a98fd1c2456596c144e..0000000000000000000000000000000000000000
--- a/object_facemap_auto/auto_fmap_widgets.py
+++ /dev/null
@@ -1,446 +0,0 @@
-# ##### 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 #####
-
-'''
-Face map manipulator:
-
-- Automatic face-map to bone/shape mapping, so tweaking a face-map edits the underlying bone or shape
-  by matching names.
-- Bones will: Translates, Rotates, Scales (in that order based on locks)
-- Face-map selection can be used.
-- Transforming multiple face maps at once works (with selection).
-- Alt-G/R/S can be used to clear face-map transformation.
-- Dot-key can be used for view selected face-maps.
-- Face-map names can store key-values, formatted:
-  "some_name;foo=bar;baz=spam".
-  Currently this is used to set the face-map action if it cant be usefully guessed from locks.
-  eg "Bone;action=scale".
-- Shapes simply change their influence.
-- Face-map colors from bone groups are used when available.
-- Supports precision (Shift) and increment snap (Ctrl).
-- Keyframes after each edit, regardless of auto-key setting (unless its canceled).
-  Note: I did this because there is no easy way to manually key - if you don't want the keyframe you can undo :)
-'''
-
-import bpy
-
-from bpy.types import (
-    GizmoGroup,
-    Gizmo,
-
-    PoseBone,
-    ShapeKey,
-)
-
-
-# USE_VERBOSE = True
-from . import (
-    USE_VERBOSE,
-    USE_RELOAD,
-)
-
-
-# -----------------------------------------------------------------------------
-# Utility functions
-
-def object_armatures(ob):
-    for mod in ob.modifiers:
-        if mod.type == 'ARMATURE':
-            if mod.show_viewport:
-                ob_arm = mod.object
-                if ob_arm is not None:
-                    yield ob_arm
-
-
-def face_map_find_target(ob, fmap_name):
-    """
-    Returns pose-bone or shape-key.
-    """
-    # first pose-bone
-    for ob_arm in object_armatures(ob):
-        pbone = ob_arm.pose.bones.get(fmap_name)
-        if pbone is not None:
-            return pbone
-
-    # second shape-keys
-    if ob.type == 'MESH':
-        ob_data = ob.data
-        shape_keys = ob_data.shape_keys
-        if shape_keys is not None:
-            shape_key = ob_data.shape_keys.key_blocks.get(fmap_name)
-            if shape_key is not None:
-                return shape_key
-
-    # can't find target
-    return None
-
-
-def pose_bone_get_color(pose_bone):
-    bone_group = pose_bone.bone_group
-    if bone_group is not None:
-        return bone_group.colors.active
-    else:
-        return None
-
-
-# -----------------------------------------------------------------------------
-# Face-map gizmos
-
-class AutoFaceMapWidget(Gizmo):
-    bl_idname = "VIEW3D_WT_auto_facemap"
-
-    __slots__ = (
-        # Face-map this widget displays.
-        "fmap",
-        # Face-Map index is used for drawing.
-        "fmap_index",
-        # Mesh object the face map is attached to.
-        "fmap_mesh_object",
-        # Item this widget controls:
-        # PoseBone or ShapeKey.
-        "fmap_target",
-        # list of rules, eg: ["action=rotate", ...]
-        "fmap_target_rules",
-        # Iterator to use while interacting.
-        "_iter",
-    )
-
-    def draw(self, context):
-        if USE_VERBOSE:
-            print("(draw)")
-        self.draw_preset_facemap(self.fmap_mesh_object, self.fmap_index)
-
-    def select_refresh(self):
-        fmap = getattr(self, "fmap", None)
-        if fmap is not None:
-            fmap.select = self.select
-
-    def setup(self):
-        if USE_VERBOSE:
-            print("(setup)", self)
-
-    def draw_select(self, context, select_id):
-        if USE_VERBOSE:
-            print("(draw_select)", self, context, select_id >> 8)
-        self.draw_preset_facemap(self.fmap_mesh_object, self.fmap_index, select_id=select_id)
-
-    def invoke(self, context, event):
-        if USE_VERBOSE:
-            print("(invoke)", self, event)
-
-        # Avoid having to re-register to test logic
-        from .auto_fmap_utils import import_reload_or_none
-        auto_fmap_widgets_xform = import_reload_or_none(
-            __package__ + "." + "auto_fmap_widgets_xform", reload=USE_RELOAD,
-        )
-
-        auto_fmap_widgets_xform.USE_VERBOSE = USE_VERBOSE
-
-        # ob = context.object
-        # fmap = ob.fmaps[self.fmap_index]
-        # fmap_target = fmap_find_target(ob, fmap)
-
-        mpr_list = [self]
-        for mpr in self.group.gizmos:
-            if mpr is not self:
-                if mpr.select:
-                    mpr_list.append(mpr)
-
-        self._iter = []
-
-        self.group.is_modal = True
-
-        for mpr in mpr_list:
-            ob = mpr.fmap_mesh_object
-            fmap_target = mpr.fmap_target
-            fmap = mpr.fmap
-
-            if isinstance(fmap_target, PoseBone):
-                # try get from rules first
-                action = mpr.fmap_target_rules.get("action")
-                if action is None:
-                    bone = fmap_target.bone
-                    if (not (bone.use_connect and bone.parent)) and (not all(fmap_target.lock_location)):
-                        action = "translate"
-                    elif not all(fmap_target.lock_rotation):
-                        action = "rotate"
-                    elif not all(fmap_target.lock_scale):
-                        action = "scale"
-                    del bone
-
-                # guess from pose
-                if action == "translate":
-                    mpr_iter = iter(auto_fmap_widgets_xform.widget_iter_pose_translate(
-                        context, mpr, ob, fmap, fmap_target))
-                elif action == "rotate":
-                    mpr_iter = iter(auto_fmap_widgets_xform.widget_iter_pose_rotate(
-                        context, mpr, ob, fmap, fmap_target))
-                elif action == "scale":
-                    mpr_iter = iter(auto_fmap_widgets_xform.widget_iter_pose_scale(
-                        context, mpr, ob, fmap, fmap_target))
-                elif action:
-                    print("Warning: action %r not known!" % action)
-
-            elif isinstance(fmap_target, ShapeKey):
-                mpr_iter = iter(auto_fmap_widgets_xform.widget_iter_shapekey(
-                    context, mpr, ob, fmap, fmap_target))
-
-            if mpr_iter is None:
-                mpr_iter = iter(auto_fmap_widgets_xform.widget_iter_template(
-                    context, mpr, ob, fmap, fmap_target))
-
-            # initialize
-            mpr_iter.send(None)
-            # invoke()
-            mpr_iter.send(event)
-
-            self._iter.append(mpr_iter)
-        return {'RUNNING_MODAL'}
-
-    def exit(self, context, cancel):
-        self.group.is_modal = False
-        # failed case
-        if not self._iter:
-            return
-
-        if USE_VERBOSE:
-            print("(exit)", self, cancel)
-
-        # last event, StopIteration is expected!
-        for mpr_iter in self._iter:
-            try:
-                mpr_iter.send((cancel, None))
-            except StopIteration:
-                continue  # expected state
-            raise Exception("for some reason the iterator lives on!")
-        if not cancel:
-            bpy.ops.ed.undo_push(message="Tweak Gizmo")
-
-    def modal(self, context, event, tweak):
-        # failed case
-        if not self._iter:
-            return {'CANCELLED'}
-
-        # iter prints
-        for mpr_iter in self._iter:
-            try:
-                mpr_iter.send((event, tweak))
-            except Exception:
-                import traceback
-                traceback.print_exc()
-                # avoid flooding output
-                # We might want to remove this from the list!
-                # self._iter = None
-        return {'RUNNING_MODAL'}
-
-
-class AutoFaceMapWidgetGroup(GizmoGroup):
-    bl_idname = "OBJECT_WGT_auto_facemap"
-    bl_label = "Auto Face Map"
-    bl_space_type = 'VIEW_3D'
-    bl_region_type = 'WINDOW'
-    bl_options = {'3D', 'DEPTH_3D', 'SELECT', 'PERSISTENT', 'SHOW_MODAL_ALL'}
-
-    __slots__ = (
-        # "widgets",
-        # need some comparison
-        "last_active_object",
-        "is_modal",
-    )
-
-    @classmethod
-    def poll(cls, context):
-        if context.mode != 'OBJECT':
-            return False
-        ob = context.object
-        return (ob and ob.type == 'MESH' and ob.face_maps)
-
-    @staticmethod
-    def mesh_objects_from_armature(ob, visible_mesh_objects):
-        """
-        Take a context and return all mesh objects we can face-map.
-        """
-        for ob_iter in visible_mesh_objects:
-            if ob in object_armatures(ob_iter):
-                yield ob_iter
-
-    @staticmethod
-    def mesh_objects_from_context(context):
-        """
-        Take a context and return all mesh objects we can face-map.
-        """
-        ob = context.object
-        if ob is None:
-            pass
-        ob_type = ob.type
-        visible_mesh_objects = [ob_iter for ob_iter in context.visible_objects if ob_iter.type == 'MESH']
-        if ob_type == 'ARMATURE':
-            yield from AutoFaceMapWidgetGroup.mesh_objects_from_armature(ob, visible_mesh_objects)
-        elif ob_type == 'MESH':
-            # Unlikely, but possible 2x armatures control the same meshes
-            # so double check we dont add the same meshes twice.
-            unique_objects = set()
-            for ob_iter in object_armatures(ob):
-                if ob_iter not in unique_objects:
-                    yield from AutoFaceMapWidgetGroup.mesh_objects_from_armature(ob_iter, visible_mesh_objects)
-                unique_objects.add(ob_iter)
-
-    def setup_manipulator_from_facemap(self, fmap_mesh_object, fmap, i):
-        color_fallback = 0.15, 0.62, 1.0
-
-        # foo;bar;baz --> ("foo", "bar;baz")
-        fmap_name = fmap.name
-        fmap_name_strip, fmap_rules = fmap_name.partition(";")[::2]
-        fmap_target = face_map_find_target(fmap_mesh_object, fmap_name_strip)
-
-        if fmap_target is None:
-            return None
-
-        mpr = self.gizmos.new(AutoFaceMapWidget.bl_idname)
-        mpr.fmap_index = i
-        mpr.fmap = fmap
-        mpr.fmap_mesh_object = fmap_mesh_object
-        mpr.fmap_target = fmap_target
-
-        # See 'select_refresh' which syncs back in the other direction.
-        mpr.select = fmap.select
-
-        # foo;bar=baz;bonzo=bingo --> {"bar": baz", "bonzo": bingo}
-        mpr.fmap_target_rules = dict(
-            item.partition("=")[::2] for item in fmap_rules
-        )
-
-        # XXX, we might want to have some way to extract a 'center' from a face-map
-        # for now use the pose-bones location.
-        if isinstance(fmap_target, PoseBone):
-            mpr.matrix_basis = (fmap_target.id_data.matrix_world @ fmap_target.matrix).normalized()
-
-        mpr.use_draw_hover = True
-        mpr.use_draw_modal = True
-
-        if isinstance(fmap_target, PoseBone):
-            mpr.color = pose_bone_get_color(fmap_target) or color_fallback
-        else:  # We could color shapes, for now not.
-            mpr.color = color_fallback
-
-        mpr.alpha = 0.5
-
-        mpr.color_highlight = mpr.color
-        mpr.alpha_highlight = 0.5
-        return mpr
-
-    def setup(self, context):
-        self.is_modal = False
-
-        # we could remove this,
-        # for now ensure keymaps are added on setup
-        self.evil_keymap_setup(context)
-
-        is_update = hasattr(self, "last_active_object")
-
-        # For weak sanity check - detects undo
-        if is_update and (self.last_active_object != context.active_object):
-            is_update = False
-            self.gizmos.clear()
-
-        self.last_active_object = context.active_object
-
-        if not is_update:
-            for fmap_mesh_object in self.mesh_objects_from_context(context):
-                for (i, fmap) in enumerate(fmap_mesh_object.face_maps):
-                    self.setup_manipulator_from_facemap(fmap_mesh_object, fmap, i)
-        else:
-            # first attempt simple update
-            force_full_update = False
-            mpr_iter_old = iter(self.gizmos)
-            for fmap_mesh_object in self.mesh_objects_from_context(context):
-                for (i, fmap) in enumerate(fmap_mesh_object.face_maps):
-                    mpr_old = next(mpr_iter_old, None)
-                    if (
-                            (mpr_old is None) or
-                            (mpr_old.fmap_mesh_object != fmap_mesh_object) or
-                            (mpr_old.fmap != fmap)
-                    ):
-                        force_full_update = True
-                        break
-                    # else we will want to update the base matrix at least
-                    # possibly colors
-                    # but its not so important
-            del mpr_iter_old
-
-            if force_full_update:
-                self.gizmos.clear()
-                # same as above
-                for fmap_mesh_object in self.mesh_objects_from_context(context):
-                    for (i, fmap) in enumerate(fmap_mesh_object.face_maps):
-                        self.setup_manipulator_from_facemap(fmap_mesh_object, fmap, i)
-
-    def refresh(self, context):
-        if self.is_modal:
-            return
-        # WEAK!
-        self.setup(context)
-
-    @classmethod
-    def evil_keymap_setup(cls, context):
-        # only once!
-        if hasattr(cls, "_keymap_init"):
-            return
-        cls._keymap_init = True
-
-        # TODO, lookup existing keys and use those.
-
-        # in-place of bpy.ops.object.location_clear and friends.
-        km = context.window_manager.keyconfigs.active.keymaps.get(cls.bl_idname)
-        if km is None:
-            return
-        kmi = km.keymap_items.new('my_facemap.transform_clear', 'G', 'PRESS', alt=True)
-        kmi.properties.clear_types = {'LOCATION'}
-        kmi = km.keymap_items.new('my_facemap.transform_clear', 'R', 'PRESS', alt=True)
-        kmi.properties.clear_types = {'ROTATION'}
-        kmi = km.keymap_items.new('my_facemap.transform_clear', 'S', 'PRESS', alt=True)
-        kmi.properties.clear_types = {'SCALE'}
-
-        # in-place of bpy.ops.view3d.view_selected
-        # km.keymap_items.new('my_facemap.view_selected', 'NUMPAD_PERIOD', 'PRESS')
-
-    '''
-    @classmethod
-    def setup_keymap(cls, keyconfig):
-        km = keyconfig.keymaps.new(name=cls.bl_label, space_type='VIEW_3D', region_type='WINDOW')
-        # XXX add keymaps
-        return km
-    '''
-
-
-classes = (
-    AutoFaceMapWidget,
-    AutoFaceMapWidgetGroup,
-)
-
-
-def register():
-    from bpy.utils import register_class
-    for cls in classes:
-        register_class(cls)
-
-
-def unregister():
-    from bpy.utils import unregister_class
-    for cls in classes:
-        unregister_class(cls)
diff --git a/object_facemap_auto/auto_fmap_widgets_xform.py b/object_facemap_auto/auto_fmap_widgets_xform.py
deleted file mode 100644
index 5b4ae1fecb128941d407f8bcd6c09e628b2ea669..0000000000000000000000000000000000000000
--- a/object_facemap_auto/auto_fmap_widgets_xform.py
+++ /dev/null
@@ -1,514 +0,0 @@
-# ##### 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 #####
-
-import bpy
-import math
-
-from bpy.types import (
-    PoseBone,
-    ShapeKey,
-)
-
-# may be overwritten by user!
-from . import USE_VERBOSE
-
-
-# -----------------------------------------------------------------------------
-# Utilities
-
-def coords_to_loc_3d(context, coords_2d_seq, depth_location):
-    from bpy_extras.view3d_utils import region_2d_to_location_3d
-    region = context.region
-    rv3d = context.region_data
-
-    return tuple((
-        region_2d_to_location_3d(region, rv3d, mval, depth_location)
-        for mval in coords_2d_seq
-    ))
-
-
-def calc_view_vector(context):
-    rv3d = context.region_data
-    viewinv = rv3d.view_matrix.inverted()
-    return -viewinv.col[2].xyz.normalized()
-
-
-def pose_bone_calc_transform_orientation(pose_bone):
-    ob_pose = pose_bone.id_data
-    return (ob_pose.matrix_world @ pose_bone.matrix).inverted().to_3x3()
-
-
-def pose_bone_rotation_attr_from_mode(pose_bone):
-    return {
-        # XYZ or any re-ordering maps to euler
-        'AXIS_ANGLE': 'rotation_axis_angle',
-        'QUATERNION': 'rotation_quaternion',
-    }.get(pose_bone.rotation_mode, 'rotation_euler')
-
-
-def pose_bone_autokey(pose_bone, attr_key, attr_lock):
-    ob_pose = pose_bone.id_data
-    value = getattr(pose_bone, attr_key)
-    locks = getattr(pose_bone, attr_lock)
-    data_path = pose_bone.path_from_id() + "." + attr_key
-    for i in range(len(value)):
-        # needed because quaternion has only 3 locks
-        try:
-            is_lock = locks[i]
-        except IndexError:
-            is_lock = False
-
-        ob_pose.keyframe_insert(data_path, frame=i)
-
-
-def pose_bone_set_attr_with_locks(pose_bone, attr_key, attr_lock, value):
-    ob_pose = pose_bone.id_data
-    locks = getattr(pose_bone, attr_lock)
-    if not any(locks):
-        setattr(pose_bone, attr_key, value)
-    else:
-        # keep wrapped
-        value_wrap = getattr(pose_bone, attr_key)
-        # update from 'value' for unlocked axis
-        value_new = value_wrap.copy()
-        for i in range(len(value)):
-            # needed because quaternion has only 3 locks
-            try:
-                is_lock = locks[i]
-            except IndexError:
-                is_lock = False
-
-            if not is_lock:
-                value_new[i] = value[i]
-        # Set all at once instead of per-axis to avoid multiple RNA updates.
-        value_wrap[:] = value_new
-
-
-# -----------------------------------------------------------------------------
-# Interactivity class
-#
-# Note, this could use asyncio, we may want to investigate that!
-# for now generators work fine.
-
-def widget_iter_template(context, mpr, ob, fmap, fmap_target):
-    # generic initialize
-    if USE_VERBOSE:
-        print("(iter-init)")
-
-    # invoke()
-    # ...
-    if USE_VERBOSE:
-        print("(iter-invoke)")
-
-    context.area.header_text_set("No operation found for: {}".format(fmap.name))
-
-    event = yield
-    tweak = set()
-
-    # modal(), first step
-    # Keep this loop fast! runs on mouse-movement.
-    while True:
-        event, tweak_next = yield
-        if event in {True, False}:
-            break
-        if event.type == 'INBETWEEN_MOUSEMOVE':
-            continue
-        tweak = tweak_next
-
-        if USE_VERBOSE:
-            print("(iter-modal)", event, tweak)
-
-    # exit()
-    if USE_VERBOSE:
-        print("(iter-exit)", event)
-
-    context.area.header_text_set(None)
-
-
-def widget_iter_pose_translate(context, mpr, ob, fmap, fmap_target):
-    from mathutils import (
-        Vector,
-    )
-    # generic initialize
-    if USE_VERBOSE:
-        print("(iter-init)")
-
-    context.area.header_text_set("Translating face-map: {}".format(fmap.name))
-
-    # invoke()
-    # ...
-    if USE_VERBOSE:
-        print("(iter-invoke)")
-    event = yield
-    tweak = set()
-
-    # modal(), first step
-    mval_init = Vector((event.mouse_region_x, event.mouse_region_y))
-    mval = mval_init.copy()
-
-    # impl vars
-    pose_bone = fmap_target
-    del fmap_target
-
-    tweak_attr = "location"
-    tweak_attr_lock = "lock_location"
-
-    # Could use face-map center too
-    # Don't update these while interacting
-    bone_matrix_init = pose_bone.matrix.copy()
-    depth_location = bone_matrix_init.to_translation()
-
-    world_to_local_3x3 = pose_bone_calc_transform_orientation(pose_bone)
-
-    loc_init = pose_bone.location.copy()
-
-    # Keep this loop fast! runs on mouse-movement.
-    while True:
-        event, tweak_next = yield
-        if event in {True, False}:
-            break
-        if event.type == 'INBETWEEN_MOUSEMOVE':
-            continue
-        tweak = tweak_next
-
-        if USE_VERBOSE:
-            print("(iter-modal)", event, tweak)
-
-        mval = Vector((event.mouse_region_x, event.mouse_region_y))
-
-        co_init, co = coords_to_loc_3d(context, (mval_init, mval), depth_location)
-        loc_delta = world_to_local_3x3 @ (co - co_init)
-
-        input_scale = 1.0
-        is_precise = 'PRECISE' in tweak
-        if is_precise:
-            input_scale /= 10.0
-
-        loc_delta *= input_scale
-        # relative snap
-        if 'SNAP' in tweak:
-            loc_delta[:] = [round(v, 2 if is_precise else 1) for v in loc_delta]
-
-        final_value = loc_init + loc_delta
-        pose_bone_set_attr_with_locks(pose_bone, tweak_attr, tweak_attr_lock, final_value)
-
-    # exit()
-    if USE_VERBOSE:
-        print("(iter-exit)", event)
-    if event is True:  # cancel
-        pose_bone.location = loc_init
-    else:
-        pose_bone_autokey(pose_bone, tweak_attr, tweak_attr_lock)
-
-    context.area.header_text_set(None)
-
-
-def widget_iter_pose_rotate(context, mpr, ob, fmap, fmap_target):
-    from mathutils import (
-        Vector,
-        Quaternion,
-    )
-    # generic initialize
-    if USE_VERBOSE:
-        print("(iter-init)")
-
-    tweak_attr = pose_bone_rotation_attr_from_mode(fmap_target)
-    context.area.header_text_set("Rotating ({}) face-map: {}".format(tweak_attr, fmap.name))
-    tweak_attr_lock = "lock_rotation"
-
-    # invoke()
-    # ...
-    if USE_VERBOSE:
-        print("(iter-invoke)")
-    event = yield
-    tweak = set()
-
-    # modal(), first step
-    mval_init = Vector((event.mouse_region_x, event.mouse_region_y))
-    mval = mval_init.copy()
-
-    # impl vars
-    pose_bone = fmap_target
-    del fmap_target
-
-    # Could use face-map center too
-    # Don't update these while interacting
-    bone_matrix_init = pose_bone.matrix.copy()
-    depth_location = bone_matrix_init.to_translation()
-    rot_center = bone_matrix_init.to_translation()
-
-    world_to_local_3x3 = pose_bone_calc_transform_orientation(pose_bone)
-
-    # for rotation
-    local_view_vector = (calc_view_vector(context) @ world_to_local_3x3).normalized()
-
-    rot_init = getattr(pose_bone, tweak_attr).copy()
-
-    # Keep this loop fast! runs on mouse-movement.
-    while True:
-        event, tweak_next = yield
-        if event in {True, False}:
-            break
-        if event.type == 'INBETWEEN_MOUSEMOVE':
-            continue
-        tweak = tweak_next
-
-        if USE_VERBOSE:
-            print("(iter-modal)", event, tweak)
-
-        mval = Vector((event.mouse_region_x, event.mouse_region_y))
-
-        # calculate rotation matrix from input
-        co_init, co = coords_to_loc_3d(context, (mval_init, mval), depth_location)
-        # co_delta = world_to_local_3x3 @ (co - co_init)
-
-        input_scale = 1.0
-        is_precise = 'PRECISE' in tweak
-        if is_precise:
-            input_scale /= 10.0
-
-        if False:
-            # Dial logic, not obvious enough unless we show graphical line to center...
-            # but this is typically too close to the center of the face-map.
-            rot_delta = (co_init - rot_center).rotation_difference(co - rot_center).to_matrix()
-        else:
-            # Steering wheel logic, left to rotate left, right to rotate right :)
-            # use X-axis only
-
-            # Calculate imaginary point as if mouse was moved 100px to the right
-            # then transform to local orientation and use to see where the cursor is
-
-            rotate_angle = ((mval.x - mval_init.x) / 100.0)
-            rotate_angle *= input_scale
-
-            if 'SNAP' in tweak:
-                v = math.radians(1.0 if is_precise else 15.0)
-                rotate_angle = round(rotate_angle / v) * v
-                del v
-
-            rot_delta = Quaternion(local_view_vector, rotate_angle).to_matrix()
-
-            # rot_delta = (co_init - rot_center).rotation_difference(co - rot_center).to_matrix()
-
-        rot_matrix = rot_init.to_matrix()
-        rot_matrix = rot_matrix @ rot_delta
-
-        if tweak_attr == "rotation_quaternion":
-            final_value = rot_matrix.to_quaternion()
-        elif tweak_attr == "rotation_euler":
-            final_value = rot_matrix.to_euler(pose_bone.rotation_mode, rot_init)
-        else:
-            assert(tweak_attr == "rotation_axis_angle")
-            final_value = rot_matrix.to_quaternion().to_axis_angle()
-            final_value = (*final_value[0], final_value[1])  # flatten
-
-        pose_bone_set_attr_with_locks(pose_bone, tweak_attr, tweak_attr_lock, final_value)
-
-    # exit()
-    if USE_VERBOSE:
-        print("(iter-exit)", event)
-    if event is True:  # cancel
-        setattr(pose_bone, tweak_attr, rot_init)
-    else:
-        pose_bone_autokey(pose_bone, tweak_attr, tweak_attr_lock)
-
-    context.area.header_text_set(None)
-
-
-def widget_iter_pose_scale(context, mpr, ob, fmap, fmap_target):
-    from mathutils import (
-        Vector,
-    )
-    # generic initialize
-    if USE_VERBOSE:
-        print("(iter-init)")
-
-    context.area.header_text_set("Scale face-map: {}".format(fmap.name))
-
-    # invoke()
-    # ...
-    if USE_VERBOSE:
-        print("(iter-invoke)")
-    event = yield
-    tweak = set()
-
-    # modal(), first step
-    mval_init = Vector((event.mouse_region_x, event.mouse_region_y))
-    mval = mval_init.copy()
-
-    # impl vars
-    pose_bone = fmap_target
-    del fmap_target
-    tweak_attr = "scale"
-    tweak_attr_lock = "lock_scale"
-
-    scale_init = pose_bone.scale.copy()
-
-    # Keep this loop fast! runs on mouse-movement.
-    while True:
-        event, tweak_next = yield
-        if event in {True, False}:
-            break
-        if event.type == 'INBETWEEN_MOUSEMOVE':
-            continue
-        tweak = tweak_next
-
-        if USE_VERBOSE:
-            print("(iter-modal)", event, tweak)
-
-        mval = Vector((event.mouse_region_x, event.mouse_region_y))
-
-        input_scale = 1.0
-        is_precise = 'PRECISE' in tweak
-        if is_precise:
-            input_scale /= 10.0
-
-        scale_factor = ((mval.y - mval_init.y) / 200.0) * input_scale
-        if 'SNAP' in tweak:
-            # relative
-            scale_factor = round(scale_factor, 2 if is_precise else 1)
-        final_value = scale_init * (1.0 + scale_factor)
-        pose_bone_set_attr_with_locks(pose_bone, tweak_attr, tweak_attr_lock, final_value)
-
-    # exit()
-    if USE_VERBOSE:
-        print("(iter-exit)", event)
-    if event is True:  # cancel
-        pose_bone.scale = scale_init
-    else:
-        pose_bone_autokey(pose_bone, tweak_attr, tweak_attr_lock)
-
-    context.area.header_text_set(None)
-
-
-def widget_iter_shapekey(context, mpr, ob, fmap, fmap_target):
-    from mathutils import (
-        Vector,
-    )
-    # generic initialize
-    if USE_VERBOSE:
-        print("(iter-init)")
-
-    context.area.header_text_set("ShapeKey face-map: {}".format(fmap.name))
-
-    # invoke()
-    # ...
-    if USE_VERBOSE:
-        print("(iter-invoke)")
-    event = yield
-    tweak = set()
-
-    # modal(), first step
-    mval_init = Vector((event.mouse_region_x, event.mouse_region_y))
-    mval = mval_init.copy()
-
-    # impl vars
-    shape = fmap_target
-    del fmap_target
-
-    value_init = shape.value
-
-    # Keep this loop fast! runs on mouse-movement.
-    while True:
-        event, tweak_next = yield
-        if event in {True, False}:
-            break
-        if event.type == 'INBETWEEN_MOUSEMOVE':
-            continue
-        tweak = tweak_next
-
-        if USE_VERBOSE:
-            print("(iter-modal)", event, tweak)
-
-        mval = Vector((event.mouse_region_x, event.mouse_region_y))
-
-        input_scale = 1.0
-        is_precise = 'PRECISE' in tweak
-        if is_precise:
-            input_scale /= 10.0
-
-        final_value = value_init + ((mval.y - mval_init.y) / 200.0) * input_scale
-        if 'SNAP' in tweak:
-            final_value = round(final_value, 2 if is_precise else 1)
-
-        shape.value = final_value
-
-    # exit()
-    if USE_VERBOSE:
-        print("(iter-exit)", event)
-    if event is True:  # cancel
-        shape.value = scale_init
-    else:
-        shape.id_data.keyframe_insert(shape.path_from_id() + ".value")
-
-    context.area.header_text_set(None)
-
-
-# -------------------------
-# Non Interactive Functions
-
-def widget_clear_location(context, mpr, ob, fmap, fmap_target):
-
-    if isinstance(fmap_target, ShapeKey):
-        shape = fmap_target
-        del fmap_target
-        # gets clamped
-        shape.value = 0.0
-    elif isinstance(fmap_target, PoseBone):
-        pose_bone = fmap_target
-        del fmap_target
-
-        tweak_attr = "location"
-        tweak_attr_lock = "lock_location"
-
-        pose_bone_set_attr_with_locks(pose_bone, tweak_attr, tweak_attr_lock, (0.0, 0.0, 0.0))
-        pose_bone_autokey(pose_bone, tweak_attr, tweak_attr_lock)
-
-
-def widget_clear_rotation(context, mpr, ob, fmap, fmap_target):
-
-    if isinstance(fmap_target, PoseBone):
-        pose_bone = fmap_target
-        del fmap_target
-        tweak_attr = pose_bone_rotation_attr_from_mode(pose_bone)
-        tweak_attr_lock = "lock_rotation"
-
-        value = getattr(pose_bone, tweak_attr).copy()
-        if tweak_attr == 'rotation_axis_angle':
-            # keep the axis
-            value[3] = 0.0
-        elif tweak_attr == 'rotation_quaternion':
-            value.identity()
-        elif tweak_attr == 'rotation_euler':
-            value.zero()
-
-        pose_bone_set_attr_with_locks(pose_bone, tweak_attr, tweak_attr_lock, value)
-        pose_bone_autokey(pose_bone, tweak_attr, tweak_attr_lock)
-
-
-def widget_clear_scale(context, mpr, ob, fmap, fmap_target):
-
-    if isinstance(fmap_target, PoseBone):
-        pose_bone = fmap_target
-        del fmap_target
-
-        tweak_attr = "scale"
-        tweak_attr_lock = "lock_scale"
-
-        pose_bone_set_attr_with_locks(pose_bone, tweak_attr, tweak_attr_lock, (1.0, 1.0, 1.0))
-        pose_bone_autokey(pose_bone, tweak_attr, tweak_attr_lock)