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

Dmitry Kazakov dimula73 at gmail.com
Sun Jun 23 17:22:05 UTC 2013


Git commit a896114462cf41ecbc3f488329510ee4429579a3 by Dmitry Kazakov.
Committed on 23/06/2013 at 17:21.
Pushed by dkazakov into branch 'master'.

[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/a896114462cf41ecbc3f488329510ee4429579a3

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 40c56fd..98c56e9 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.cpp
+++ b/krita/ui/tool/kis_tool_freehand_helper.cpp
@@ -245,8 +245,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!
@@ -255,22 +254,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);
@@ -288,10 +283,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();
@@ -301,9 +315,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