[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-kimageshop
Subject:    [graphics/krita] /: Implement "Scale handles proportionally" feature for the mesh transform
From:       Dmitry Kazakov <null () kde ! org>
Date:       2021-01-08 17:46:49
Message-ID: 20210108174649.369A91240FAE () leptone ! kde ! org
[Download RAW message or body]

Git commit a7220ca0e5c91ec4dfb6cf3f890fc852c5ac0472 by Dmitry Kazakov.
Committed on 08/01/2021 at 17:46.
Pushed by dkazakov into branch 'master'.

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@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  +8    -1    plugins/tools/tool_transform2/wdg_tool_transform.ui

https://invent.kde.org/graphics/krita/commit/a7220ca0e5c91ec4dfb6cf3f890fc852c5ac0472

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 \
                a65b776080..26b783672d 100644
--- a/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
+++ b/plugins/tools/tool_transform2/kis_mesh_transform_strategy.cpp
@@ -571,7 +571,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); @@ -596,8 +597,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);
@@ -624,8 +625,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());  };
 
 
@@ -705,13 +706,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 \
                31e7115787..a69c2d32de 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.cpp
@@ -287,6 +287,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()));  
@@ -635,6 +636,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();
@@ -1300,3 +1302,11 @@ void \
                KisToolTransformConfigWidget::slotMeshSymmetricalHandlesChanged()
     config->setMeshSymmetricalHandles(this->chkSymmetricalHandles->isChecked());
     notifyConfigChanged();
 }
+
+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 \
                72b5854b49..fec6ea2d6e 100644
--- a/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
+++ b/plugins/tools/tool_transform2/kis_tool_transform_config_widget.h
@@ -109,7 +109,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 ea9e3742c1..2f8d3ecdae \
                100644
--- a/plugins/tools/tool_transform2/tool_transform_args.cc
+++ b/plugins/tools/tool_transform2/tool_transform_args.cc
@@ -27,6 +27,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) {
@@ -80,10 +81,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 d62e73f0a8..aaebf4195a \
                100644
--- a/plugins/tools/tool_transform2/tool_transform_args.h
+++ b/plugins/tools/tool_transform2/tool_transform_args.h
@@ -307,6 +307,9 @@ public:
     bool meshSymmetricalHandles() const;
     void setMeshSymmetricalHandles(bool meshSymmetricalHandles);
 
+    bool meshScaleHandles() const;
+    void setMeshScaleHandles(bool meshScaleHandles);
+
 private:
     void clear();
     void init(const ToolTransformArgs& args);
@@ -353,6 +356,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 bc33787137..9d4c527b10 \
                100644
--- a/plugins/tools/tool_transform2/wdg_tool_transform.ui
+++ b/plugins/tools/tool_transform2/wdg_tool_transform.ui
@@ -2197,6 +2197,13 @@
          </property>
         </widget>
        </item>
+       <item>
+        <widget class="QCheckBox" name="chkScaleHandles">
+         <property name="text">
+          <string>Scale handles proportionally</string>
+         </property>
+        </widget>
+       </item>
        <item>
         <spacer name="verticalSpacer_4">
          <property name="orientation">
@@ -2313,8 +2320,8 @@
  <resources/>
  <connections/>
  <buttongroups>
-  <buttongroup name="freeTransformRadioGroup"/>
   <buttongroup name="cageTransformButtonGroup"/>
   <buttongroup name="buttonGroup"/>
+  <buttongroup name="freeTransformRadioGroup"/>
  </buttongroups>
 </ui>
\ No newline at end of file


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic