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

Dmitry Kazakov null at kde.org
Mon Nov 30 13:03:16 GMT 2020


Git commit 4b4b1f4ae6cabc86d3936348d7a26e0da0611505 by Dmitry Kazakov.
Committed on 30/11/2020 at 13:03.
Pushed by dkazakov into branch 'master'.

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/4b4b1f4ae6cabc86d3936348d7a26e0da0611505

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 2c8d738d57..5cd3b6dbf7 100644
--- a/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
+++ b/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
@@ -43,6 +43,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,
@@ -122,15 +124,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;
         }
     }
 
@@ -138,7 +144,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;
         }
     }
 
@@ -146,7 +152,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;
         }
     }
 
@@ -173,13 +179,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) {
@@ -190,13 +189,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;
             }
@@ -303,10 +301,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:
@@ -467,7 +467,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 ||
@@ -478,7 +480,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);
 
@@ -531,13 +547,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);
@@ -556,8 +580,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);
@@ -569,10 +599,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;
@@ -580,8 +610,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);
@@ -594,7 +625,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