[graphics/krita/krita/4.3] /: Fix inconvenient default shortcuts in the Mesh Transform tool

Dmitry Kazakov null at kde.org
Mon Nov 30 13:02:34 GMT 2020


Git commit 40f8c1689c3809bd098fbf4a2b3cd35d65db97af by Dmitry Kazakov.
Committed on 30/11/2020 at 12:53.
Pushed by dkazakov into branch 'krita/4.3'.

Fix inconvenient default shortcuts in the Mesh Transform tool

The patch bascially makes symmetric transforms be the default choice
available without any modifiers.

Now the shortcuts are the following:

1) Mesh node:
   - click+drag --- move node

2) Border node:
   - click+drag --- move node
   - shift+click+drag --- move whole row/column
   - ctrl+alt+click+drag --- split/slide row/column
   - ctrl+alt+click+drag-away --- remove split

3) Control point:
   - click+drag --- change control point symmetrically
   - shift+click+drag --- change control point non-symmetrically;
                          this action will create angular texture
                          artifacts
4) Node or Control:
   - ctrl+click --- select multiple nodes

5) Patch area:
   - click+drag --- free patch deform
   - shift+click+drag --- move whole mesh

6) Empty area outside mesh:
   - click+drag --- rotate mesh or selection
   - ctrl+click+drag --- scale mesh or selection
   - shift+click+drag --- move mesh or selection

CC:kimageshop at kde.org

M  +25   -11   libs/global/KisBezierMesh.h
M  +60   -29   plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp

https://invent.kde.org/graphics/krita/commit/40f8c1689c3809bd098fbf4a2b3cd35d65db97af

diff --git a/libs/global/KisBezierMesh.h b/libs/global/KisBezierMesh.h
index d23333fb1e..f31ff62a55 100644
--- a/libs/global/KisBezierMesh.h
+++ b/libs/global/KisBezierMesh.h
@@ -1305,11 +1305,17 @@ Mesh<NodeArg, PatchArg>::control_point_iterator_impl<is_const>::rightSegment() c
     return SegmentIteratorType(m_mesh, m_col, m_row, true);
 }
 
+enum SmartMoveMeshControlMode {
+    MoveFree,
+    MoveSymmetricLock,
+    MoveRotationLock
+};
+
 template<typename NodeArg, typename PatchArg>
 void smartMoveControl(Mesh<NodeArg, PatchArg> &mesh,
                       typename Mesh<NodeArg, PatchArg>::ControlPointIndex index,
                       const QPointF &move,
-                      bool lockNodes)
+                      SmartMoveMeshControlMode mode)
 {
     using ControlType = typename Mesh<NodeArg, PatchArg>::ControlType;
     using ControlPointIndex = typename Mesh<NodeArg, PatchArg>::ControlPointIndex;
@@ -1322,7 +1328,7 @@ void smartMoveControl(Mesh<NodeArg, PatchArg> &mesh,
     } else {
         const QPointF newPos = *it + move;
 
-        if (lockNodes) {
+        if (mode == MoveRotationLock || mode == MoveSymmetricLock) {
             const qreal rotation = KisAlgebra2D::angleBetweenVectors(*it - it.node().node,
                                                                      newPos - it.node().node);
             QTransform R;
@@ -1333,19 +1339,26 @@ void smartMoveControl(Mesh<NodeArg, PatchArg> &mesh,
                     R *
                     QTransform::fromTranslate(it.node().node.x(), it.node().node.y());
 
-            for (int intType = 0; intType < 4; intType++) {
-                ControlType type = static_cast<ControlType>(intType);
+            if (mode == MoveRotationLock) {
+                for (int intType = 0; intType < 4; intType++) {
+                    ControlType type = static_cast<ControlType>(intType);
 
-                if (type == ControlType::Node ||
-                    type == index.controlType) {
+                    if (type == ControlType::Node ||
+                            type == index.controlType) {
 
-                    continue;
-                }
+                        continue;
+                    }
 
-                auto neighbourIt = mesh.find(ControlPointIndex(index.nodeIndex, type));
-                if (neighbourIt == mesh.endControlPoints()) continue;
+                    auto neighbourIt = mesh.find(ControlPointIndex(index.nodeIndex, type));
+                    if (neighbourIt == mesh.endControlPoints()) continue;
 
-                *neighbourIt = t.map(*neighbourIt);
+                    *neighbourIt = t.map(*neighbourIt);
+                }
+            } else {
+                auto neighbourIt = it.symmetricControl();
+                if (neighbourIt != mesh.endControlPoints()) {
+                    *neighbourIt = t.map(*neighbourIt);
+                }
             }
         }
 
@@ -1372,6 +1385,7 @@ using KisBezierMeshDetails::saveValue;
 template <typename Node, typename Patch>
 using KisBezierMeshBase = KisBezierMeshDetails::Mesh<Node, Patch>;
 
+using KisSmartMoveMeshControlMode = KisBezierMeshDetails::SmartMoveMeshControlMode;
 using KisBezierMesh = KisBezierMeshDetails::Mesh<KisBezierMeshDetails::BaseMeshNode, KisBezierPatch>;
 
 
diff --git a/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp b/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
index bfe95f5285..b3d593de06 100644
--- a/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
+++ b/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
@@ -55,6 +55,8 @@ struct KisMeshTransformStrategy::Private
     enum Mode {
         OVER_POINT = 0,
         OVER_POINT_SYMMETRIC,
+        OVER_NODE,
+        OVER_NODE_WHOLE_LINE,
         OVER_SEGMENT,
         OVER_SEGMENT_SYMMETRIC,
         OVER_PATCH,
@@ -134,15 +136,19 @@ void KisMeshTransformStrategy::setTransformFunction(const QPointF &mousePos, boo
         auto index = m_d->currentArgs.meshTransform()->hitTestControlPoint(mousePos, grabRadius);
         if (m_d->currentArgs.meshTransform()->isIndexValid(index)) {
             hoveredControl = index;
-            mode = Private::OVER_POINT;
+            mode = !shiftModifierActive ? Private::OVER_POINT_SYMMETRIC : Private::OVER_POINT;
         }
     }
 
     if (mode == Private::NOTHING) {
         auto index = m_d->currentArgs.meshTransform()->hitTestNode(mousePos, grabRadius);
-        if (m_d->currentArgs.meshTransform()->isIndexValid(index)) {
+        auto nodeIt = m_d->currentArgs.meshTransform()->find(index);
+
+        if (nodeIt != m_d->currentArgs.meshTransform()->endControlPoints()) {
             hoveredControl = index;
-            mode = Private::OVER_POINT;
+            mode = shiftModifierActive && nodeIt.isBorderNode() && !nodeIt.isCornerNode() ?
+                Private::OVER_NODE_WHOLE_LINE :
+                Private::OVER_NODE;
         }
     }
 
@@ -150,7 +156,7 @@ void KisMeshTransformStrategy::setTransformFunction(const QPointF &mousePos, boo
         auto index = m_d->currentArgs.meshTransform()->hitTestSegment(mousePos, grabRadius, &localSegmentPos);
         if (m_d->currentArgs.meshTransform()->isIndexValid(index)) {
             hoveredSegment = index;
-            mode = Private::OVER_SEGMENT;
+            mode = !shiftModifierActive ? Private::OVER_SEGMENT_SYMMETRIC : Private::OVER_SEGMENT;
         }
     }
 
@@ -158,7 +164,7 @@ void KisMeshTransformStrategy::setTransformFunction(const QPointF &mousePos, boo
         auto index = m_d->currentArgs.meshTransform()->hitTestPatch(mousePos, &localPatchPos);
         if (m_d->currentArgs.meshTransform()->isIndexValid(index)) {
             hoveredPatch = index;
-            mode = Private::OVER_PATCH;
+            mode = !shiftModifierActive ? Private::OVER_PATCH : Private::MOVE_MODE;
         }
     }
 
@@ -185,13 +191,6 @@ void KisMeshTransformStrategy::setTransformFunction(const QPointF &mousePos, boo
 
         mode = Private::SPLIT_SEGMENT;
 
-    } else if (shiftModifierActive &&
-               hoveredControl &&
-               !hoveredControl->isNode()) {
-        mode = Private::OVER_POINT_SYMMETRIC;
-    } else if (shiftModifierActive &&
-               hoveredSegment) {
-        mode = Private::OVER_SEGMENT_SYMMETRIC;
     } else {
         if (hoveredControl || hoveredSegment) {
             if (perspectiveModifierActive) {
@@ -202,13 +201,12 @@ void KisMeshTransformStrategy::setTransformFunction(const QPointF &mousePos, boo
                        m_d->selectedNodes.contains(hoveredControl->nodeIndex)) {
 
                 mode = Private::MOVE_MODE;
-
             }
-        } else {
-            if (hoveredPatch) {
-                mode = shiftModifierActive ? Private::OVER_PATCH : Private::MOVE_MODE;
-            } else if (perspectiveModifierActive) {
+        } else if (!hoveredPatch) {
+            if (perspectiveModifierActive) {
                 mode = Private::SCALE_MODE;
+            } else if (shiftModifierActive) {
+                mode = Private::MOVE_MODE;
             } else {
                 mode = Private::ROTATE_MODE;
             }
@@ -315,10 +313,12 @@ QCursor KisMeshTransformStrategy::getCurrentCursor() const
     QCursor cursor;
 
     switch (m_d->mode) {
+    case Private::OVER_NODE:
     case Private::OVER_POINT:
     case Private::OVER_SEGMENT:
         cursor = KisCursor::meshCursorFree();
         break;
+    case Private::OVER_NODE_WHOLE_LINE:
     case Private::OVER_POINT_SYMMETRIC:
     case Private::OVER_SEGMENT_SYMMETRIC:
     case Private::OVER_PATCH:
@@ -479,7 +479,9 @@ bool KisMeshTransformStrategy::beginPrimaryAction(const QPointF &pt)
 
     m_d->pointWasDragged = false;
 
-    if (m_d->mode == Private::OVER_POINT || m_d->mode == Private::OVER_POINT_SYMMETRIC) {
+    if (m_d->mode == Private::OVER_NODE ||
+        m_d->mode == Private::OVER_POINT ||
+        m_d->mode == Private::OVER_POINT_SYMMETRIC) {
         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->hoveredControl, false);
 
         if (m_d->selectedNodes.size() <= 1 ||
@@ -490,7 +492,21 @@ bool KisMeshTransformStrategy::beginPrimaryAction(const QPointF &pt)
         }
 
         retval = true;
+    } else if (m_d->mode == Private::OVER_NODE_WHOLE_LINE) {
+        m_d->selectedNodes.clear();
+        auto it = m_d->currentArgs.meshTransform()->find(*m_d->hoveredControl);
+        KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(it != m_d->currentArgs.meshTransform()->endControlPoints(), false);
 
+        if (it.isTopBorder() || it.isBottomBorder()) {
+            for (int i = 0; i < m_d->currentArgs.meshTransform()->size().height(); i++) {
+                m_d->selectedNodes << KisBezierTransformMesh::NodeIndex(m_d->hoveredControl->nodeIndex.x(), i);
+            }
+        } else {
+            for (int i = 0; i < m_d->currentArgs.meshTransform()->size().width(); i++) {
+                m_d->selectedNodes << KisBezierTransformMesh::NodeIndex(i, m_d->hoveredControl->nodeIndex.y());
+            }
+        }
+        retval = true;
     } else if (m_d->mode == Private::OVER_SEGMENT || m_d->mode == Private::OVER_SEGMENT_SYMMETRIC) {
         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(m_d->hoveredSegment, false);
 
@@ -543,13 +559,21 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
     Q_UNUSED(shiftModifierActve);
     Q_UNUSED(altModifierActive);
 
-    if (m_d->mode == Private::OVER_POINT || m_d->mode == Private::OVER_POINT_SYMMETRIC) {
+    if (m_d->mode == Private::OVER_POINT ||
+        m_d->mode == Private::OVER_POINT_SYMMETRIC ||
+        m_d->mode == Private::OVER_NODE) {
+
         KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->hoveredControl);
 
+        KisSmartMoveMeshControlMode mode =
+            m_d->mode == Private::OVER_POINT_SYMMETRIC ?
+            KisSmartMoveMeshControlMode::MoveSymmetricLock :
+            KisSmartMoveMeshControlMode::MoveFree;
+
         smartMoveControl(*m_d->currentArgs.meshTransform(),
                          *m_d->hoveredControl,
                          pt - m_d->lastMousePos,
-                         m_d->mode == Private::OVER_POINT_SYMMETRIC);
+                         mode);
 
     } else if (m_d->mode == Private::OVER_SEGMENT || m_d->mode == Private::OVER_SEGMENT_SYMMETRIC) {
         KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->hoveredSegment);
@@ -568,8 +592,14 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
         std::tie(offsetP1, offsetP2) =
             KisBezierUtils::offsetSegment(m_d->localSegmentPosition, offset);
 
-        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, m_d->mode == Private::OVER_SEGMENT_SYMMETRIC);
-        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, m_d->mode == Private::OVER_SEGMENT_SYMMETRIC);
+
+        KisSmartMoveMeshControlMode mode =
+            m_d->mode == Private::OVER_SEGMENT_SYMMETRIC ?
+            KisSmartMoveMeshControlMode::MoveSymmetricLock :
+            KisSmartMoveMeshControlMode::MoveFree;
+
+        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, mode);
+        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, mode);
 
     } else if (m_d->mode == Private::OVER_PATCH) {
         KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->hoveredPatch);
@@ -581,10 +611,10 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
         const QPointF offset = pt - m_d->mouseClickPos;
 
         auto offsetSegment =
-            [] (KisBezierTransformMesh::segment_iterator it,
-                qreal t,
-                qreal distance,
-                const QPointF &offset) {
+            [this] (KisBezierTransformMesh::segment_iterator it,
+                    qreal t,
+                    qreal distance,
+                    const QPointF &offset) {
 
             QPointF offsetP1;
             QPointF offsetP2;
@@ -592,8 +622,9 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
             std::tie(offsetP1, offsetP2) =
                 KisBezierUtils::offsetSegment(t, (1.0 - distance) * offset);
 
-            it.p1() += offsetP1;
-            it.p2() += offsetP2;
+
+            smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, KisSmartMoveMeshControlMode::MoveSymmetricLock);
+            smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, KisSmartMoveMeshControlMode::MoveSymmetricLock);
         };
 
         offsetSegment(patchIt.segmentP(), m_d->localPatchPosition.x(), m_d->localPatchPosition.y(), offset);
@@ -606,7 +637,7 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
         const bool sanitySplitResult = splitHoveredSegment(pt);
         KIS_SAFE_ASSERT_RECOVER_NOOP(sanitySplitResult);
 
-    } else if (m_d->mode == Private::MOVE_MODE) {
+    } else if (m_d->mode == Private::MOVE_MODE || m_d->mode == Private::OVER_NODE_WHOLE_LINE) {
         const QPointF offset = pt - m_d->lastMousePos;
         if (m_d->selectedNodes.size() > 1) {
             for (auto it = m_d->selectedNodes.begin(); it != m_d->selectedNodes.end(); ++it) {



More information about the kimageshop mailing list