[calligra/calligra/2.7] krita: [FEATURE] Added enchanced weighted smoothing algorithm with tail configuration

Dmitry Kazakov dimula73 at gmail.com
Fri Jul 12 08:25:23 UTC 2013


Git commit 62ce8c0fb5b6e89d06b5b9504c098cf96623e944 by Dmitry Kazakov.
Committed on 23/06/2013 at 17:21.
Pushed by dkazakov into branch 'calligra/2.7'.

[FEATURE] Added enchanced weighted smoothing algorithm with tail configuration

Now when you lift up the stylus the line hurries to the tip of the pen
so that the line ends right at the position of the pen, not delayed.

There is a configuration option for this feature: "Tail Aggressiveness"
(probably, some other name would be better ;) )

1) Tail Aggressiveness == 0:    There is no tail at all. The line ends where it was.
2) Tail Aggressiveness == 0.15: The tail ends with the very thin tip.
3) Tail Aggressiveness >  0.15: The tail ends with bolder tip.

CCMAIL:kimageshop at kde.org

M  +24   -27   krita/plugins/tools/defaulttools/kis_tool_brush.cc
M  +4    -4    krita/plugins/tools/defaulttools/kis_tool_brush.h
M  +2    -2    krita/ui/tool/kis_smoothing_options.cpp
M  +2    -3    krita/ui/tool/kis_smoothing_options.h
M  +30   -14   krita/ui/tool/kis_tool_freehand_helper.cpp

http://commits.kde.org/calligra/62ce8c0fb5b6e89d06b5b9504c098cf96623e944

diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.cc b/krita/plugins/tools/defaulttools/kis_tool_brush.cc
index fad20b5..56ff0d2 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_brush.cc
+++ b/krita/plugins/tools/defaulttools/kis_tool_brush.cc
@@ -28,8 +28,7 @@
 #include "kis_cursor.h"
 #include "kis_slider_spin_box.h"
 
-#define MAXIMUM_SMOOTHNESS_QUALITY 100 // 0..100
-#define MAXIMUM_SMOOTHNESS_FACTOR 1000.0 // 0..1000.0 == weight in gui
+#define MAXIMUM_SMOOTHNESS_DISTANCE 1000.0 // 0..1000.0 == weight in gui
 #define MAXIMUM_MAGNETISM 1000
 
 
@@ -50,32 +49,31 @@ void KisToolBrush::slotSetSmoothingType(int index)
     switch (index) {
     case 0:
         m_smoothingOptions.smoothingType = KisSmoothingOptions::NO_SMOOTHING;
-        m_sliderSmoothnessFactor->setEnabled(false);
-        m_sliderSmoothnessQuality->setEnabled(false);
+        m_sliderSmoothnessDistance->setEnabled(false);
+        m_sliderTailAggressiveness->setEnabled(false);
         break;
     case 1:
         m_smoothingOptions.smoothingType = KisSmoothingOptions::SIMPLE_SMOOTHING;
-        m_sliderSmoothnessFactor->setEnabled(false);
-        m_sliderSmoothnessQuality->setEnabled(false);
+        m_sliderSmoothnessDistance->setEnabled(false);
+        m_sliderTailAggressiveness->setEnabled(false);
         break;
     case 2:
     default:
         m_smoothingOptions.smoothingType = KisSmoothingOptions::WEIGHTED_SMOOTHING;
-        m_sliderSmoothnessFactor->setEnabled(true);
-        m_sliderSmoothnessQuality->setEnabled(true);
+        m_sliderSmoothnessDistance->setEnabled(true);
+        m_sliderTailAggressiveness->setEnabled(true);
     }
 }
 
-void KisToolBrush::slotSetSmoothnessQuality(int quality)
+void KisToolBrush::slotSetSmoothnessDistance(qreal distance)
 {
-    m_smoothingOptions.smoothnessQuality = quality;
+    m_smoothingOptions.smoothnessDistance = distance;
 }
 
-void KisToolBrush::slotSetSmoothnessFactor(qreal factor)
+void KisToolBrush::slotSetTailAgressiveness(qreal argh_rhhrr)
 {
-    m_smoothingOptions.smoothnessFactor = factor;
+    m_smoothingOptions.tailAggressiveness = argh_rhhrr;
 }
-
 void KisToolBrush::slotSetMagnetism(int magnetism)
 {
     m_magnetism = expf(magnetism / (double)MAXIMUM_MAGNETISM) / expf(1.0);
@@ -93,20 +91,19 @@ QWidget * KisToolBrush::createOptionWidget()
     connect(m_cmbSmoothingType, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetSmoothingType(int)));
     addOptionWidgetOption(m_cmbSmoothingType);
 
-    m_sliderSmoothnessQuality = new KisSliderSpinBox(optionWidget);
-    m_sliderSmoothnessQuality->setRange(1, MAXIMUM_SMOOTHNESS_QUALITY);
-    m_sliderSmoothnessQuality->setEnabled(true);
-    connect(m_sliderSmoothnessQuality, SIGNAL(valueChanged(int)), SLOT(slotSetSmoothnessQuality(int)));
-    m_sliderSmoothnessQuality->setValue(m_smoothingOptions.smoothnessQuality);
-    addOptionWidgetOption(m_sliderSmoothnessQuality, new QLabel(i18n("Quality:")));
-
-    m_sliderSmoothnessFactor = new KisDoubleSliderSpinBox(optionWidget);
-    m_sliderSmoothnessFactor->setRange(3.0, MAXIMUM_SMOOTHNESS_FACTOR, 1);
-    m_sliderSmoothnessFactor->setEnabled(true);
-    connect(m_sliderSmoothnessFactor, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessFactor(qreal)));
-    m_sliderSmoothnessFactor->setValue(m_smoothingOptions.smoothnessFactor);
-
-    addOptionWidgetOption(m_sliderSmoothnessFactor, new QLabel(i18n("Weight:")));
+    m_sliderSmoothnessDistance = new KisDoubleSliderSpinBox(optionWidget);
+    m_sliderSmoothnessDistance->setRange(3.0, MAXIMUM_SMOOTHNESS_DISTANCE, 1);
+    m_sliderSmoothnessDistance->setEnabled(true);
+    connect(m_sliderSmoothnessDistance, SIGNAL(valueChanged(qreal)), SLOT(slotSetSmoothnessDistance(qreal)));
+    m_sliderSmoothnessDistance->setValue(m_smoothingOptions.smoothnessDistance);
+    addOptionWidgetOption(m_sliderSmoothnessDistance, new QLabel(i18n("Weight:")));
+
+    m_sliderTailAggressiveness = new KisDoubleSliderSpinBox(optionWidget);
+    m_sliderTailAggressiveness->setRange(0.0, 1.0, 2);
+    m_sliderTailAggressiveness->setEnabled(true);
+    connect(m_sliderTailAggressiveness, SIGNAL(valueChanged(qreal)), SLOT(slotSetTailAgressiveness(qreal)));
+    m_sliderTailAggressiveness->setValue(m_smoothingOptions.tailAggressiveness);
+    addOptionWidgetOption(m_sliderTailAggressiveness, new QLabel(i18n("Tail Aggressiveness:")));
 
     slotSetSmoothingType(1);
 
diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.h b/krita/plugins/tools/defaulttools/kis_tool_brush.h
index dbf5c37..f38cbb7 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_brush.h
+++ b/krita/plugins/tools/defaulttools/kis_tool_brush.h
@@ -45,10 +45,10 @@ public:
     QWidget * createOptionWidget();
 
 private slots:
-    void slotSetSmoothnessQuality(int quality);
-    void slotSetSmoothnessFactor(qreal factor);
+    void slotSetSmoothnessDistance(qreal distance);
     void slotSetMagnetism(int magnetism);
     void slotSetSmoothingType(int index);
+    void slotSetTailAgressiveness(qreal argh_rhhrr);
 
 private:
     QGridLayout *m_optionLayout;
@@ -56,8 +56,8 @@ private:
 
     QCheckBox *m_chkAssistant;
     KisSliderSpinBox *m_sliderMagnetism;
-    KisDoubleSliderSpinBox *m_sliderSmoothnessFactor;
-    KisSliderSpinBox *m_sliderSmoothnessQuality;
+    KisDoubleSliderSpinBox *m_sliderSmoothnessDistance;
+    KisDoubleSliderSpinBox *m_sliderTailAggressiveness;
 };
 
 
diff --git a/krita/ui/tool/kis_smoothing_options.cpp b/krita/ui/tool/kis_smoothing_options.cpp
index 14326c4..d8b41ee 100644
--- a/krita/ui/tool/kis_smoothing_options.cpp
+++ b/krita/ui/tool/kis_smoothing_options.cpp
@@ -19,7 +19,7 @@
 
 KisSmoothingOptions::KisSmoothingOptions()
     : smoothingType(WEIGHTED_SMOOTHING)
-    , smoothnessFactor(50.0)
-    , smoothnessQuality(20)
+    , smoothnessDistance(50.0)
+    , tailAggressiveness(0.15)
 {
 }
diff --git a/krita/ui/tool/kis_smoothing_options.h b/krita/ui/tool/kis_smoothing_options.h
index b28144d..c74ba01 100644
--- a/krita/ui/tool/kis_smoothing_options.h
+++ b/krita/ui/tool/kis_smoothing_options.h
@@ -32,9 +32,8 @@ struct KisSmoothingOptions
     KisSmoothingOptions();
 
     SmoothingType smoothingType;
-    qreal smoothnessFactor;
-    int smoothnessQuality;
-
+    qreal smoothnessDistance;
+    qreal tailAggressiveness;
 };
 
 #endif // KIS_SMOOTHING_OPTIONS_H
diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp b/krita/ui/tool/kis_tool_freehand_helper.cpp
index 9e2cadb..ae4230d 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.cpp
+++ b/krita/ui/tool/kis_tool_freehand_helper.cpp
@@ -244,8 +244,7 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
     // https://bugs.kde.org/show_bug.cgi?id=281267 and http://www24.atwiki.jp/sigetch_2007/pages/17.html.
     // This is also implemented in gimp, which is where I cribbed the code from.
     if (m_d->smoothingOptions.smoothingType == KisSmoothingOptions::WEIGHTED_SMOOTHING
-            && m_d->smoothingOptions.smoothnessQuality > 1
-            && m_d->smoothingOptions.smoothnessFactor > 3.0) {
+        && m_d->smoothingOptions.smoothnessDistance > 0.0) {
 
         m_d->history.append(info);
         m_d->velocityHistory.append(std::numeric_limits<qreal>::signaling_NaN()); // Fake velocity!
@@ -254,22 +253,18 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
         qreal y = 0.0;
 
         if (m_d->history.size() > 3) {
+            const qreal avg_events_rate = 8; // ms
+            const qreal sigma = m_d->smoothingOptions.smoothnessDistance / (3.0 * avg_events_rate); // '3.0' for (3 * sigma) range
 
-            int length = qMin(m_d->smoothingOptions.smoothnessQuality, m_d->history.size());
-            int minIndex = m_d->history.size() - length;
-
-            qreal gaussianWeight = 0.0;
-            qreal gaussianWeight2 = m_d->smoothingOptions.smoothnessFactor * m_d->smoothingOptions.smoothnessFactor;
+            qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
+            qreal gaussianWeight2 = sigma * sigma;
             qreal velocitySum = 0.0;
             qreal scaleSum = 0.0;
-
-            if (gaussianWeight2 != 0.0) {
-                gaussianWeight = 1 / (sqrt(2 * M_PI) * m_d->smoothingOptions.smoothnessFactor);
-            }
+            qreal baseRate = 0.0;
 
             Q_ASSERT(m_d->history.size() == m_d->velocityHistory.size());
 
-            for (int i = m_d->history.size() - 1; i >= minIndex; i--) {
+            for (int i = m_d->history.size() - 1; i >= 0; i--) {
                 qreal rate = 0.0;
 
                 const KisPaintInformation nextInfo = m_d->history.at(i);
@@ -287,10 +282,29 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
                     m_d->velocityHistory[i] = velocity;
                 }
 
+                qreal pressureGrad = 0.0;
+                if (i < m_d->history.size() - 1) {
+                    pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
+
+                    const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions.tailAggressiveness;
+
+                    if (pressureGrad > 0.0 ) {
+                        pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
+                        velocity += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
+                    }
+                }
+
                 if (gaussianWeight2 != 0.0) {
-                    velocitySum += velocity * 100;
+                    velocitySum += velocity;
                     rate = gaussianWeight * exp(-velocitySum * velocitySum / (2 * gaussianWeight2));
                 }
+
+                if (m_d->history.size() - i == 1) {
+                    baseRate = rate;
+                } else if (baseRate / rate > 100) {
+                    break;
+                }
+
                 scaleSum += rate;
                 x += rate * nextInfo.pos().x();
                 y += rate * nextInfo.pos().y();
@@ -300,9 +314,11 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
                 x /= scaleSum;
                 y /= scaleSum;
             }
+
             if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
-                m_d->history.last().setPos(QPointF(x, y));
+                info.setMovement(toKisVector2D(info.pos() - QPointF(x, y)));
                 info.setPos(QPointF(x, y));
+                m_d->history.last() = info;
             }
         }
     }



More information about the kimageshop mailing list