[graphics/krita/krita/4.3] /: Implement "Scale handles proportionally" feature for the mesh transform

Dmitry Kazakov null at kde.org
Thu Apr 1 13:24:00 BST 2021


Git commit 27d5b128c30490201b52aa1ffd070aa70c634dce by Dmitry Kazakov.
Committed on 01/04/2021 at 12:20.
Pushed by dkazakov into branch 'krita/4.3'.

Implement "Scale handles proportionally" feature for the mesh transform

When the user moves a node, the handles of the neighbouring nodes may
overlap. To avoid that we should shrink the size of the handles when
the distance between two nodes decreases.

The patch implements an option for the mesh transform tool that
activates such scaling (disabled by default).

This feature cannot go into 4.4.2 because of the string freeze.

CC:kimageshop at kde.org

# Conflicts:
#	plugins/tools/tool_transform2/tool_transform_args.h

M  +39   -1    libs/global/KisBezierMesh.h
M  +12   -11   plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
M  +10   -0    plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
M  +1    -1    plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
M  +15   -0    plugins/tools/tool_transform2/tool_transform_args.cc
M  +4    -0    plugins/tools/tool_transform2/tool_transform_args.h
M  +1    -0    plugins/tools/tool_transform2/wdg_tool_transform.ui

https://invent.kde.org/graphics/krita/commit/27d5b128c30490201b52aa1ffd070aa70c634dce

diff --git a/libs/global/KisBezierMesh.h b/libs/global/KisBezierMesh.h
index db9d4a4d15..600413a6df 100644
--- a/libs/global/KisBezierMesh.h
+++ b/libs/global/KisBezierMesh.h
@@ -1434,16 +1434,54 @@ template<typename NodeArg, typename PatchArg>
 void smartMoveControl(Mesh<NodeArg, PatchArg> &mesh,
                       typename Mesh<NodeArg, PatchArg>::ControlPointIndex index,
                       const QPointF &move,
-                      SmartMoveMeshControlMode mode)
+                      SmartMoveMeshControlMode mode,
+                      bool scaleNodeMoves)
 {
     using ControlType = typename Mesh<NodeArg, PatchArg>::ControlType;
     using ControlPointIndex = typename Mesh<NodeArg, PatchArg>::ControlPointIndex;
+    using ControlPointIterator = typename Mesh<NodeArg, PatchArg>::control_point_iterator;
+    using SegmentIterator = typename Mesh<NodeArg, PatchArg>::segment_iterator;
 
     auto it = mesh.find(index);
     KIS_SAFE_ASSERT_RECOVER_RETURN(it != mesh.endControlPoints());
 
     if (it.isNode()) {
+        auto preAdjustSegment = [] (Mesh<NodeArg, PatchArg> &mesh,
+                                    SegmentIterator it,
+                                    const QPointF &normalizedOffset) {
+
+            if (it == mesh.endSegments()) return;
+
+            const QPointF base1 = it.p3() - it.p0();
+            const QPointF base2 = it.p3() - it.p0() - normalizedOffset;
+
+            {
+                const QPointF control = it.p1() - it.p0();
+                const qreal dist0 = KisAlgebra2D::norm(base1);
+                const qreal dist1 = KisAlgebra2D::dotProduct(base2, base1) / dist0;
+                const qreal coeff = dist1 / dist0;
+
+                it.p1() = it.p0() + coeff * (control);
+            }
+            {
+                const QPointF control = it.p2() - it.p3();
+                const qreal dist0 = KisAlgebra2D::norm(base1);
+                const qreal dist1 = KisAlgebra2D::dotProduct(base2, base1) / dist0;
+                const qreal coeff = dist1 / dist0;
+
+                it.p2() = it.p3() + coeff * (control);
+            }
+        };
+
+        if (scaleNodeMoves) {
+            preAdjustSegment(mesh, it.topSegment(), -move);
+            preAdjustSegment(mesh, it.leftSegment(), -move);
+            preAdjustSegment(mesh, it.bottomSegment(), move);
+            preAdjustSegment(mesh, it.rightSegment(), move);
+        }
+
         it.node().translate(move);
+
     } else {
         const QPointF newPos = *it + move;
 
diff --git a/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp b/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
index 8bfe4f66bc..af0d1ac63c 100644
--- a/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
+++ b/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
@@ -583,7 +583,8 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
         smartMoveControl(*m_d->currentArgs.meshTransform(),
                          *m_d->hoveredControl,
                          pt - m_d->lastMousePos,
-                         mode);
+                         mode,
+                         m_d->currentArgs.meshScaleHandles());
 
     } else if (m_d->mode == Private::OVER_SEGMENT || m_d->mode == Private::OVER_SEGMENT_SYMMETRIC) {
         KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->hoveredSegment);
@@ -608,8 +609,8 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
             KisSmartMoveMeshControlMode::MoveSymmetricLock :
             KisSmartMoveMeshControlMode::MoveFree;
 
-        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, mode);
-        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, mode);
+        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, mode, m_d->currentArgs.meshScaleHandles());
+        smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, mode, m_d->currentArgs.meshScaleHandles());
 
     } else if (m_d->mode == Private::OVER_PATCH || m_d->mode == Private::OVER_PATCH_LOCKED) {
         KIS_SAFE_ASSERT_RECOVER_RETURN(m_d->hoveredPatch);
@@ -636,8 +637,8 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
                 KisBezierUtils::offsetSegment(t, offset);
 
 
-            smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, KisSmartMoveMeshControlMode::MoveSymmetricLock);
-            smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, KisSmartMoveMeshControlMode::MoveSymmetricLock);
+            smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP1().controlIndex(), offsetP1, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
+            smartMoveControl(*m_d->currentArgs.meshTransform(), it.itP2().controlIndex(), offsetP2, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
         };
 
 
@@ -717,13 +718,13 @@ void KisMeshTransformStrategy::continuePrimaryAction(const QPointF &pt, bool shi
         const qreal coeffN0 =
             alpha < 0.5 ? std::max(0.0, linearReshapeFunc(alpha, 0.25, 0.4, 0.0, 1.0)) : 1.0;
 
-        nearestSegment.itP0().node().translate(offset * coeffN0);
-        nearestSegment.itP3().node().translate(offset * coeffN1);
+        smartMoveControl(*m_d->currentArgs.meshTransform(), nearestSegment.itP0().controlIndex(), offset * coeffN0, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
+        smartMoveControl(*m_d->currentArgs.meshTransform(), nearestSegment.itP3().controlIndex(), offset * coeffN1, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
 
-        patchIt.nodeTopLeft().node().translate(translationOffset);
-        patchIt.nodeTopRight().node().translate(translationOffset);
-        patchIt.nodeBottomLeft().node().translate(translationOffset);
-        patchIt.nodeBottomRight().node().translate(translationOffset);
+        smartMoveControl(*m_d->currentArgs.meshTransform(), patchIt.nodeTopLeft().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
+        smartMoveControl(*m_d->currentArgs.meshTransform(), patchIt.nodeTopRight().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
+        smartMoveControl(*m_d->currentArgs.meshTransform(), patchIt.nodeBottomLeft().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
+        smartMoveControl(*m_d->currentArgs.meshTransform(), patchIt.nodeBottomRight().controlIndex(), translationOffset, KisSmartMoveMeshControlMode::MoveSymmetricLock, m_d->currentArgs.meshScaleHandles());
 
         offsetSegment(nearestSegment, nearestSegmentParam, segmentOffset);
 
diff --git a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
index b58ec86415..68cf30a181 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
@@ -292,6 +292,7 @@ KisToolTransformConfigWidget::KisToolTransformConfigWidget(TransformTransactionP
 
     connect(chkShowControlPoints, SIGNAL(toggled(bool)), this, SLOT(slotMeshShowHandlesChanged()));
     connect(chkSymmetricalHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshSymmetricalHandlesChanged()));
+    connect(chkScaleHandles, SIGNAL(toggled(bool)), this, SLOT(slotMeshScaleHandlesChanged()));
     connect(intNumColumns, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
     connect(intNumRows, SIGNAL(valueChanged(int)), this, SLOT(slotMeshSizeChanged()));
     connect(intNumColumns, SIGNAL(editingFinished()), this, SLOT(notifyEditingFinished()));
@@ -642,6 +643,7 @@ void KisToolTransformConfigWidget::updateConfig(const ToolTransformArgs &config)
         intNumRows->setValue(config.meshTransform()->size().height() - 1);
         chkShowControlPoints->setChecked(config.meshShowHandles());
         chkSymmetricalHandles->setChecked(config.meshSymmetricalHandles());
+        chkScaleHandles->setChecked(config.meshScaleHandles());
     }
 
     unblockUiSlots();
@@ -1309,3 +1311,11 @@ void KisToolTransformConfigWidget::slotMeshSymmetricalHandlesChanged()
     notifyConfigChanged();
     notifyEditingFinished();
 }
+
+void KisToolTransformConfigWidget::slotMeshScaleHandlesChanged()
+{
+    if (m_uiSlotsBlocked) return;
+    ToolTransformArgs *config = m_transaction->currentConfig();
+    config->setMeshScaleHandles(this->chkScaleHandles->isChecked());
+    notifyConfigChanged();
+}
diff --git a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
index 24512750ee..49df4a2c29 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
@@ -121,7 +121,7 @@ public Q_SLOTS:
     void slotMeshSizeChanged();
     void slotMeshShowHandlesChanged();
     void slotMeshSymmetricalHandlesChanged();
-
+    void slotMeshScaleHandlesChanged();
 private:
 
     void blockNotifications();
diff --git a/plugins/tools/tool_transform2/tool_transform_args.cc b/plugins/tools/tool_transform2/tool_transform_args.cc
index 45d41d20ae..c1883e8acf 100644
--- a/plugins/tools/tool_transform2/tool_transform_args.cc
+++ b/plugins/tools/tool_transform2/tool_transform_args.cc
@@ -39,6 +39,7 @@ ToolTransformArgs::ToolTransformArgs()
     m_transformAroundRotationCenter = configGroup.readEntry("transformAroundRotationCenter", "0").toInt();
     m_meshShowHandles = configGroup.readEntry("meshShowHandles", true);
     m_meshSymmetricalHandles = configGroup.readEntry("meshSymmetricalHandles", true);
+    m_meshScaleHandles = configGroup.readEntry("meshScaleHandles", false);
 }
 
 void ToolTransformArgs::setFilterId(const QString &id) {
@@ -92,10 +93,24 @@ void ToolTransformArgs::init(const ToolTransformArgs& args)
     m_meshTransform = args.m_meshTransform;
     m_meshShowHandles = args.m_meshShowHandles;
     m_meshSymmetricalHandles = args.m_meshSymmetricalHandles;
+    m_meshScaleHandles = args.m_meshScaleHandles;
 
     m_continuedTransformation.reset(args.m_continuedTransformation ? new ToolTransformArgs(*args.m_continuedTransformation) : 0);
 }
 
+bool ToolTransformArgs::meshScaleHandles() const
+{
+    return m_meshScaleHandles;
+}
+
+void ToolTransformArgs::setMeshScaleHandles(bool meshScaleHandles)
+{
+    m_meshScaleHandles = meshScaleHandles;
+
+    KConfigGroup configGroup =  KSharedConfig::openConfig()->group("KisToolTransform");
+    configGroup.writeEntry("meshScaleHandles", meshScaleHandles);
+}
+
 void ToolTransformArgs::clear()
 {
     m_origPoints.clear();
diff --git a/plugins/tools/tool_transform2/tool_transform_args.h b/plugins/tools/tool_transform2/tool_transform_args.h
index 16c096499c..801af1b74a 100644
--- a/plugins/tools/tool_transform2/tool_transform_args.h
+++ b/plugins/tools/tool_transform2/tool_transform_args.h
@@ -319,6 +319,9 @@ public:
     bool meshSymmetricalHandles() const;
     void setMeshSymmetricalHandles(bool meshSymmetricalHandles);
 
+    bool meshScaleHandles() const;
+    void setMeshScaleHandles(bool meshScaleHandles);
+
 private:
     void clear();
     void init(const ToolTransformArgs& args);
@@ -365,6 +368,7 @@ private:
     KisBezierTransformMesh m_meshTransform;
     bool m_meshShowHandles = true;
     bool m_meshSymmetricalHandles = true;
+    bool m_meshScaleHandles = false;
 
     /**
      * When we continue a transformation, m_continuedTransformation
diff --git a/plugins/tools/tool_transform2/wdg_tool_transform.ui b/plugins/tools/tool_transform2/wdg_tool_transform.ui
index 4263b69ade..2caa0b81df 100644
--- a/plugins/tools/tool_transform2/wdg_tool_transform.ui
+++ b/plugins/tools/tool_transform2/wdg_tool_transform.ui
@@ -2213,5 +2213,6 @@
   <buttongroup name="cageTransformButtonGroup"/>
   <buttongroup name="freeTransformRadioGroup"/>
   <buttongroup name="buttonGroup"/>
+  <buttongroup name="freeTransformRadioGroup"/>
  </buttongroups>
 </ui>


More information about the kimageshop mailing list