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

List:       kde-commits
Subject:    [calligra] krita: Implement line smoothing in Krita
From:       Boudewijn Rempt <boud () valdyas ! org>
Date:       2012-12-31 16:30:20
Message-ID: 20121231163020.8F489A6091 () git ! kde ! org
[Download RAW message or body]

Git commit f9cbf6a1bd864be88059255aa9c4b5b35208085d by Boudewijn Rempt.
Committed on 31/12/2012 at 17:04.
Pushed by rempt into branch 'master'.

Implement line smoothing in Krita

REVIEW:108049
BUG:281267

M  +1    -1    krita/image/brushengine/kis_paint_information.h
M  +17   -3    krita/image/kis_distance_information.h
M  +5    -3    krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc
M  +54   -18   krita/plugins/tools/defaulttools/kis_tool_brush.cc
M  +8    -3    krita/plugins/tools/defaulttools/kis_tool_brush.h
M  +1    -0    krita/ui/CMakeLists.txt
C  +8    -17   krita/ui/tool/kis_smoothing_options.cpp [from: \
krita/image/kis_distance_information.h - 057% similarity] C  +20   -14   \
krita/ui/tool/kis_smoothing_options.h [from: krita/image/kis_distance_information.h - \
057% similarity] M  +1    -8    krita/ui/tool/kis_tool_freehand.cc
M  +3    -3    krita/ui/tool/kis_tool_freehand.h
M  +111  -31   krita/ui/tool/kis_tool_freehand_helper.cpp
M  +7    -3    krita/ui/tool/kis_tool_freehand_helper.h

http://commits.kde.org/calligra/f9cbf6a1bd864be88059255aa9c4b5b35208085d

diff --git a/krita/image/brushengine/kis_paint_information.h \
b/krita/image/brushengine/kis_paint_information.h index 0142a33..8e1de78 100644
--- a/krita/image/brushengine/kis_paint_information.h
+++ b/krita/image/brushengine/kis_paint_information.h
@@ -111,7 +111,7 @@ public:
     
     /// Number of ms since the beginning of the stroke
     int currentTime() const;
-    
+
     void toXML(QDomDocument&, QDomElement&) const;
 
     static KisPaintInformation fromXML(const QDomElement&);
diff --git a/krita/image/kis_distance_information.h \
b/krita/image/kis_distance_information.h index c1ab409..f2bd080 100644
--- a/krita/image/kis_distance_information.h
+++ b/krita/image/kis_distance_information.h
@@ -24,9 +24,23 @@
  * to be passed for the next call.
  */
 struct KisDistanceInformation {
-    KisDistanceInformation() : distance(0), spacing(0) {}
-    KisDistanceInformation(double _distance, double _spacing) : distance(_distance), \
                spacing(_spacing) {}
-    void clear() { distance = 0; spacing = 0;}
+
+    KisDistanceInformation()
+        : distance(0)
+        , spacing(0)
+    {}
+
+    KisDistanceInformation(double _distance, double _spacing)
+        : distance(_distance)
+        , spacing(_spacing)
+    {}
+
+    void clear()
+    {
+        distance = 0;
+        spacing = 0;
+    }
+
     double distance;
     double spacing;
 };
diff --git a/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc \
b/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc index \
                16473f5..c1aa015 100644
--- a/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc
+++ b/krita/plugins/paintops/libpaintop/sensors/kis_dynamic_sensors.cc
@@ -30,10 +30,12 @@ KisDynamicSensorSpeed::KisDynamicSensorSpeed() : \
KisDynamicSensor(SpeedId)  }
 
 qreal KisDynamicSensorSpeed::value(const KisPaintInformation& info) {
-    int dt = qMax(1, info.currentTime() - m_lastTime); // make sure dt > 1
+    int deltaTime = qMax(1, info.currentTime() - m_lastTime); // make sure deltaTime \
> 1  m_lastTime = info.currentTime();
-    double currentMove = info.movement().norm() / dt;
-    m_speed = qMin(1.0, (m_speed * 0.9 + currentMove * 0.1)); // average it to get \
nicer result, at the price of being less mathematically correct, but we quicly reach \
a situation where dt = 1 and currentMove = 1 +    double currentMove = \
info.movement().norm() / deltaTime; +    // Average it to get nicer result, at the \
price of being less mathematically correct, +    // but we quickly reach a situation \
where dt = 1 and currentMove = 1 +    m_speed = qMin(1.0, (m_speed * 0.9 + \
currentMove * 0.1));  return m_speed;
 }
 
diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.cc \
b/krita/plugins/tools/defaulttools/kis_tool_brush.cc index 536b81d..3b6491b 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_brush.cc
+++ b/krita/plugins/tools/defaulttools/kis_tool_brush.cc
@@ -21,19 +21,22 @@
 #include "kis_tool_brush.h"
 
 #include <QCheckBox>
+#include <QComboBox>
 
 #include <klocale.h>
 
 #include "kis_cursor.h"
 #include "kis_slider_spin_box.h"
 
-
-#define MAXIMUM_SMOOTHNESS 1000
+#define MAXIMUM_SMOOTHNESS_QUALITY 100 // 0..100
+#define MAXIMUM_SMOOTHNESS_FACTOR 1000.0 // 0..1000.0 == weight in gui
 #define MAXIMUM_MAGNETISM 1000
 
 
 KisToolBrush::KisToolBrush(KoCanvasBase * canvas)
-        : KisToolFreehand(canvas, KisCursor::load("tool_freehand_cursor.png", 5, 5), \
i18nc("(qtundo-format)", "Brush")) +    : KisToolFreehand(canvas,
+                      KisCursor::load("tool_freehand_cursor.png", 5, 5),
+                      i18nc("(qtundo-format)", "Brush"))
 {
     setObjectName("tool_brush");
 }
@@ -42,9 +45,35 @@ KisToolBrush::~KisToolBrush()
 {
 }
 
-void KisToolBrush::slotSetSmoothness(int smoothness)
+void KisToolBrush::slotSetSmoothingType(int index)
 {
-    m_smoothness = smoothness / (double)MAXIMUM_SMOOTHNESS;
+    switch (index) {
+    case 0:
+        m_smoothingOptions.smoothingType = KisSmoothingOptions::NO_SMOOTHING;
+        m_sliderSmoothnessFactor->setEnabled(false);
+        m_sliderSmoothnessQuality->setEnabled(false);
+        break;
+    case 1:
+        m_smoothingOptions.smoothingType = KisSmoothingOptions::SIMPLE_SMOOTHING;
+        m_sliderSmoothnessFactor->setEnabled(false);
+        m_sliderSmoothnessQuality->setEnabled(false);
+        break;
+    case 2:
+    default:
+        m_smoothingOptions.smoothingType = KisSmoothingOptions::WEIGHTED_SMOOTHING;
+        m_sliderSmoothnessFactor->setEnabled(true);
+        m_sliderSmoothnessQuality->setEnabled(true);
+    }
+}
+
+void KisToolBrush::slotSetSmoothnessQuality(int quality)
+{
+    m_smoothingOptions.smoothnessQuality = quality;
+}
+
+void KisToolBrush::slotSetSmoothnessFactor(qreal factor)
+{
+    m_smoothingOptions.smoothnessFactor = factor;
 }
 
 void KisToolBrush::slotSetMagnetism(int magnetism)
@@ -54,23 +83,30 @@ void KisToolBrush::slotSetMagnetism(int magnetism)
 
 QWidget * KisToolBrush::createOptionWidget()
 {
-
     QWidget * optionWidget = KisToolFreehand::createOptionWidget();
     optionWidget->setObjectName(toolId() + "option widget");
 
-    m_chkSmooth = new QCheckBox(i18nc("smooth out the curves while drawing", \
                "Smoothness:"), optionWidget);
-    m_chkSmooth->setObjectName("chkSmooth");
-    m_chkSmooth->setChecked(m_smooth);
-    connect(m_chkSmooth, SIGNAL(toggled(bool)), this, SLOT(setSmooth(bool)));
+    // Line smoothing configuration
+    m_cmbSmoothingType = new QComboBox(optionWidget);
+    m_cmbSmoothingType->addItems(QStringList() << i18n("No Smoothing") << \
i18n("Basic Smoothing") << i18n("Weighted Smoothing")); +    \
m_cmbSmoothingType->setCurrentIndex(2); +    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_sliderSmoothness = new KisSliderSpinBox(optionWidget);
-    m_sliderSmoothness->setRange(0, MAXIMUM_SMOOTHNESS);
-    m_sliderSmoothness->setEnabled(true);
-    connect(m_chkSmooth, SIGNAL(toggled(bool)), m_sliderSmoothness, \
                SLOT(setEnabled(bool)));
-    connect(m_sliderSmoothness, SIGNAL(valueChanged(int)), \
                SLOT(slotSetSmoothness(int)));
-    m_sliderSmoothness->setValue(m_smoothness * MAXIMUM_SMOOTHNESS);
+    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_sliderSmoothness, m_chkSmooth);
+    addOptionWidgetOption(m_sliderSmoothnessFactor, new QLabel(i18n("Weight:")));
 
     // Drawing assistant configuration
     m_chkAssistant = new QCheckBox(i18n("Assistant:"), optionWidget);
@@ -78,7 +114,7 @@ QWidget * KisToolBrush::createOptionWidget()
     connect(m_chkAssistant, SIGNAL(toggled(bool)), this, SLOT(setAssistant(bool)));
     m_sliderMagnetism = new KisSliderSpinBox(optionWidget);
     m_sliderMagnetism->setToolTip(i18n("Assistant Magnetism"));
-    m_sliderMagnetism->setRange(0, MAXIMUM_SMOOTHNESS);
+    m_sliderMagnetism->setRange(0, MAXIMUM_MAGNETISM);
     m_sliderMagnetism->setEnabled(false);
     connect(m_chkAssistant, SIGNAL(toggled(bool)), m_sliderMagnetism, \
SLOT(setEnabled(bool)));  m_sliderMagnetism->setValue(m_magnetism * \
                MAXIMUM_MAGNETISM);
diff --git a/krita/plugins/tools/defaulttools/kis_tool_brush.h \
b/krita/plugins/tools/defaulttools/kis_tool_brush.h index f849233..dbf5c37 100644
--- a/krita/plugins/tools/defaulttools/kis_tool_brush.h
+++ b/krita/plugins/tools/defaulttools/kis_tool_brush.h
@@ -32,6 +32,7 @@ class QGridLayout;
 
 class KoCanvasBase;
 class KisSliderSpinBox;
+class KisDoubleSliderSpinBox;
 
 class KisToolBrush : public KisToolFreehand
 {
@@ -44,15 +45,19 @@ public:
     QWidget * createOptionWidget();
 
 private slots:
-    void slotSetSmoothness(int smoothness);
+    void slotSetSmoothnessQuality(int quality);
+    void slotSetSmoothnessFactor(qreal factor);
     void slotSetMagnetism(int magnetism);
+    void slotSetSmoothingType(int index);
 
 private:
     QGridLayout *m_optionLayout;
-    QCheckBox *m_chkSmooth;
+    QComboBox *m_cmbSmoothingType;
+
     QCheckBox *m_chkAssistant;
     KisSliderSpinBox *m_sliderMagnetism;
-    KisSliderSpinBox *m_sliderSmoothness;
+    KisDoubleSliderSpinBox *m_sliderSmoothnessFactor;
+    KisSliderSpinBox *m_sliderSmoothnessQuality;
 };
 
 
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index 2d8abc4..3eebb0a 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -147,6 +147,7 @@ set(kritaui_LIB_SRCS
     tool/kis_tool_polyline_base.cpp
     tool/kis_color_picker_utils.cpp
     tool/kis_resources_snapshot.cpp
+    tool/kis_smoothing_options.cpp
     tool/strokes/freehand_stroke.cpp
     tool/strokes/kis_painter_based_stroke_strategy.cpp
     widgets/kis_channelflags_widget.cpp
diff --git a/krita/image/kis_distance_information.h \
b/krita/ui/tool/kis_smoothing_options.cpp similarity index 57%
copy from krita/image/kis_distance_information.h
copy to krita/ui/tool/kis_smoothing_options.cpp
index c1ab409..14326c4 100644
--- a/krita/image/kis_distance_information.h
+++ b/krita/ui/tool/kis_smoothing_options.cpp
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
+ *  Copyright (c) 2012 Boudewijn Rempt <boud@valdyas.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -15,20 +15,11 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
+#include "kis_smoothing_options.h"
 
-#ifndef _KIS_DISTANCE_INFORMATION_H_
-#define _KIS_DISTANCE_INFORMATION_H_
-
-/**
- * This function is used as return of paintLine to contains information that need
- * to be passed for the next call.
- */
-struct KisDistanceInformation {
-    KisDistanceInformation() : distance(0), spacing(0) {}
-    KisDistanceInformation(double _distance, double _spacing) : distance(_distance), \
                spacing(_spacing) {}
-    void clear() { distance = 0; spacing = 0;}
-    double distance;
-    double spacing;
-};
-
-#endif
+KisSmoothingOptions::KisSmoothingOptions()
+    : smoothingType(WEIGHTED_SMOOTHING)
+    , smoothnessFactor(50.0)
+    , smoothnessQuality(20)
+{
+}
diff --git a/krita/image/kis_distance_information.h \
b/krita/ui/tool/kis_smoothing_options.h similarity index 57%
copy from krita/image/kis_distance_information.h
copy to krita/ui/tool/kis_smoothing_options.h
index c1ab409..b28144d 100644
--- a/krita/image/kis_distance_information.h
+++ b/krita/ui/tool/kis_smoothing_options.h
@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) 2010 Cyrille Berger <cberger@cberger.net>
+ *  Copyright (c) 2012 Boudewijn Rempt <boud@valdyas.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -15,20 +15,26 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
+#ifndef KIS_SMOOTHING_OPTIONS_H
+#define KIS_SMOOTHING_OPTIONS_H
 
-#ifndef _KIS_DISTANCE_INFORMATION_H_
-#define _KIS_DISTANCE_INFORMATION_H_
+#include <qglobal.h>
+
+struct KisSmoothingOptions
+{
+
+    enum SmoothingType {
+        NO_SMOOTHING = 0,
+        SIMPLE_SMOOTHING,
+        WEIGHTED_SMOOTHING
+    };
+
+    KisSmoothingOptions();
+
+    SmoothingType smoothingType;
+    qreal smoothnessFactor;
+    int smoothnessQuality;
 
-/**
- * This function is used as return of paintLine to contains information that need
- * to be passed for the next call.
- */
-struct KisDistanceInformation {
-    KisDistanceInformation() : distance(0), spacing(0) {}
-    KisDistanceInformation(double _distance, double _spacing) : distance(_distance), \
                spacing(_spacing) {}
-    void clear() { distance = 0; spacing = 0;}
-    double distance;
-    double spacing;
 };
 
-#endif
+#endif // KIS_SMOOTHING_OPTIONS_H
diff --git a/krita/ui/tool/kis_tool_freehand.cc b/krita/ui/tool/kis_tool_freehand.cc
index 5640a4b..87595aa 100644
--- a/krita/ui/tool/kis_tool_freehand.cc
+++ b/krita/ui/tool/kis_tool_freehand.cc
@@ -71,9 +71,7 @@ KisToolFreehand::KisToolFreehand(KoCanvasBase * canvas, const \
QCursor & cursor,  {
     m_explicitShowOutline = false;
 
-    m_smooth = true;
     m_assistant = false;
-    m_smoothness = 1.0;
     m_magnetism = 1.0;
 
     setSupportOutline(true);
@@ -160,7 +158,7 @@ void KisToolFreehand::initStroke(KoPointerEvent *event)
 {
     setCurrentNodeLocked(true);
 
-    m_helper->setSmoothness(m_smooth, m_smoothness);
+    m_helper->setSmoothness(m_smoothingOptions);
     m_helper->initPaint(event, canvas()->resourceManager(),
                         image(),
                         image().data(),
@@ -336,11 +334,6 @@ bool KisToolFreehand::wantsAutoScroll() const
     return false;
 }
 
-void KisToolFreehand::setSmooth(bool smooth)
-{
-    m_smooth = smooth;
-}
-
 void KisToolFreehand::setAssistant(bool assistant)
 {
     m_assistant = assistant;
diff --git a/krita/ui/tool/kis_tool_freehand.h b/krita/ui/tool/kis_tool_freehand.h
index ee915d8..adc8445 100644
--- a/krita/ui/tool/kis_tool_freehand.h
+++ b/krita/ui/tool/kis_tool_freehand.h
@@ -25,6 +25,7 @@
 #include "kis_resources_snapshot.h"
 #include "kis_paintop_settings.h"
 #include "kis_distance_information.h"
+#include "kis_smoothing_options.h"
 
 #include "krita_export.h"
 
@@ -83,7 +84,6 @@ protected:
 
 protected slots:
 
-    void setSmooth(bool smooth);
     void setAssistant(bool assistant);
 
 private:
@@ -114,8 +114,8 @@ private slots:
     void hideOutline();
 
 protected:
-    bool m_smooth;
-    double m_smoothness;
+
+    KisSmoothingOptions m_smoothingOptions;
     bool m_assistant;
     double m_magnetism;
 
diff --git a/krita/ui/tool/kis_tool_freehand_helper.cpp \
b/krita/ui/tool/kis_tool_freehand_helper.cpp index dde51ea..0d63072 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.cpp
+++ b/krita/ui/tool/kis_tool_freehand_helper.cpp
@@ -30,7 +30,10 @@
 #include "kis_recording_adapter.h"
 #include "kis_image.h"
 #include "kis_painter.h"
+#include "kis_smoothing_options.h"
 
+#include <math.h>
+#include <qnumeric.h> // for qIsNaN
 
 struct KisToolFreehandHelper::Private
 {
@@ -41,7 +44,6 @@ struct KisToolFreehandHelper::Private
     bool haveTangent;
     QPointF previousTangent;
 
-
     bool hasPaintAtLeastOnce;
 
     QTime strokeTime;
@@ -54,10 +56,12 @@ struct KisToolFreehandHelper::Private
     KisPaintInformation previousPaintInformation;
     KisPaintInformation olderPaintInformation;
 
-    bool smooth;
-    qreal smoothness;
+    KisSmoothingOptions smoothingOptions;
 
     QTimer airbrushingTimer;
+
+    QList<KisPaintInformation> history;
+    QList<qreal> velocityHistory;
 };
 
 
@@ -68,9 +72,6 @@ KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder \
*info  m_d->infoBuilder = infoBuilder;
     m_d->recordingAdapter = recordingAdapter;
 
-    m_d->smooth = true;
-    m_d->smoothness = 1.0;
-
     m_d->strokeTimeoutTimer.setSingleShot(true);
     connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
 
@@ -82,10 +83,9 @@ KisToolFreehandHelper::~KisToolFreehandHelper()
     delete m_d;
 }
 
-void KisToolFreehandHelper::setSmoothness(bool smooth, qreal smoothness)
+void KisToolFreehandHelper::setSmoothness(const KisSmoothingOptions \
&smoothingOptions)  {
-    m_d->smooth = smooth;
-    m_d->smoothness = smoothness;
+    m_d->smoothingOptions = smoothingOptions;
 }
 
 void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
@@ -99,7 +99,6 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
 
     m_d->strokesFacade = strokesFacade;
 
-
     m_d->haveTangent = false;
     m_d->previousTangent = QPointF();
 
@@ -123,14 +122,18 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
     }
 
     KisStrokeStrategy *stroke =
-        new FreehandStrokeStrategy(indirectPainting,
-                                   m_d->resources, m_d->painterInfos, i18n("Freehand \
Stroke")); +            new FreehandStrokeStrategy(indirectPainting,
+                                       m_d->resources, m_d->painterInfos, \
i18n("Freehand Stroke"));  
     m_d->strokeId = m_d->strokesFacade->startStroke(stroke);
 
     m_d->previousPaintInformation =
-        m_d->infoBuilder->startStroke(event, m_d->strokeTime.elapsed());
+            m_d->infoBuilder->startStroke(event, m_d->strokeTime.elapsed());
 
+    m_d->history.clear();
+    m_d->history.append(m_d->previousPaintInformation);
+    m_d->velocityHistory.clear();
+    m_d->velocityHistory.append(std::numeric_limits<qreal>::signaling_NaN());
     if(m_d->resources->needsAirbrushing()) {
         m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate());
         m_d->airbrushingTimer.start();
@@ -140,19 +143,93 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
 void KisToolFreehandHelper::paint(KoPointerEvent *event)
 {
     KisPaintInformation info =
-        m_d->infoBuilder->continueStroke(event,
-                                         m_d->previousPaintInformation.pos(),
-                                         m_d->strokeTime.elapsed());
+            m_d->infoBuilder->continueStroke(event,
+                                             m_d->previousPaintInformation.pos(),
+                                             m_d->strokeTime.elapsed());
+
+    // Smooth the coordinates out using the history and the velocity. See
+    // 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->history.append(info);
+        m_d->velocityHistory.append(std::numeric_limits<qreal>::signaling_NaN()); // \
Fake velocity! +
+        qreal x = 0.0;
+        qreal y = 0.0;
+
+        if (m_d->history.size() > 3) {
+
+            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 velocitySum = 0.0;
+            qreal scaleSum = 0.0;
+
+            if (gaussianWeight2 != 0.0) {
+                gaussianWeight = 1 / (sqrt(2 * M_PI) * \
m_d->smoothingOptions.smoothnessFactor); +            }
+
+            Q_ASSERT(m_d->history.size() == m_d->velocityHistory.size());
+
+            for (int i = m_d->history.size() - 1; i >= minIndex; i--) {
+                qreal rate = 0.0;
+
+                const KisPaintInformation nextInfo = m_d->history.at(i);
+                double velocity = m_d->velocityHistory.at(i);
+
+                if (qIsNaN(velocity)) {
+
+                    int previousTime = nextInfo.currentTime();
+                    if (i > 0) {
+                        previousTime = m_d->history.at(i - 1).currentTime();
+                    }
+
+                    int deltaTime = qMax(1, nextInfo.currentTime() - previousTime); \
// make sure deltaTime > 1 +                    velocity = info.movement().norm() / \
deltaTime; +                    m_d->velocityHistory[i] = velocity;
+                }
+
+                if (gaussianWeight2 != 0.0) {
+                    velocitySum += velocity * 100;
+                    rate = gaussianWeight * exp(-velocitySum * velocitySum / (2 * \
gaussianWeight2)); +                }
+                scaleSum += rate;
+                x += rate * nextInfo.pos().x();
+                y += rate * nextInfo.pos().y();
+            }
+
+            if (scaleSum != 0.0) {
+                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.setPos(QPointF(x, y));
+            }
+        }
+    }
 
-    if (m_d->smooth) {
+    if (m_d->smoothingOptions.smoothingType == KisSmoothingOptions::SIMPLE_SMOOTHING
+            || m_d->smoothingOptions.smoothingType == \
KisSmoothingOptions::WEIGHTED_SMOOTHING) +    {
+        // Now paint between the coordinates, using the bezier curve interpolation
         if (!m_d->haveTangent) {
             m_d->haveTangent = true;
+
+            // XXX: 3.0 is a magic number I don't know anything about
+            //      1.0 was the old default value for smoothness, and anything lower \
than that +            //      gave horrible results, so remove that setting.
             m_d->previousTangent =
-                (info.pos() - m_d->previousPaintInformation.pos()) * m_d->smoothness \
                /
-                (3.0 * (info.currentTime() - \
m_d->previousPaintInformation.currentTime())); +                    (info.pos() - \
m_d->previousPaintInformation.pos()) / +                    (3.0 * \
(info.currentTime() - m_d->previousPaintInformation.currentTime()));  } else {
-            QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) * \
                m_d->smoothness /
-                                  (3.0 * (info.currentTime() - \
m_d->olderPaintInformation.currentTime())); +            QPointF newTangent = \
(info.pos() - m_d->olderPaintInformation.pos()) / +                    (3.0 * \
                (info.currentTime() - m_d->olderPaintInformation.currentTime()));
             qreal scaleFactor = (m_d->previousPaintInformation.currentTime() - \
                m_d->olderPaintInformation.currentTime());
             QPointF control1 = m_d->olderPaintInformation.pos() + \
                m_d->previousTangent * scaleFactor;
             QPointF control2 = m_d->previousPaintInformation.pos() - newTangent * \
scaleFactor; @@ -165,10 +242,13 @@ void KisToolFreehandHelper::paint(KoPointerEvent \
*event)  }
         m_d->olderPaintInformation = m_d->previousPaintInformation;
         m_d->strokeTimeoutTimer.start(100);
-    } else {
+    }
+    else {
         paintLine(m_d->painterInfos, m_d->previousPaintInformation, info);
     }
 
+
+
     m_d->previousPaintInformation = info;
 
     if(m_d->airbrushingTimer.isActive()) {
@@ -180,7 +260,7 @@ void KisToolFreehandHelper::endPaint()
 {
     if (!m_d->hasPaintAtLeastOnce) {
         paintAt(m_d->painterInfos, m_d->previousPaintInformation);
-    } else if (m_d->smooth) {
+    } else if (m_d->smoothingOptions.smoothingType != \
KisSmoothingOptions::NO_SMOOTHING) {  finishStroke();
     }
     m_d->strokeTimeoutTimer.stop();
@@ -215,7 +295,7 @@ void KisToolFreehandHelper::finishStroke()
     if(m_d->haveTangent) {
         m_d->haveTangent = false;
 
-        QPointF newTangent = (m_d->previousPaintInformation.pos() - \
m_d->olderPaintInformation.pos()) * m_d->smoothness / 3.0; +        QPointF \
newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) \
                / 3.0;
         qreal scaleFactor = (m_d->previousPaintInformation.currentTime() - \
                m_d->olderPaintInformation.currentTime());
         QPointF control1 = m_d->olderPaintInformation.pos() + m_d->previousTangent * \
                scaleFactor;
         QPointF control2 = m_d->previousPaintInformation.pos() - newTangent;
@@ -239,8 +319,8 @@ void KisToolFreehandHelper::paintAt(PainterInfo *painterInfo,
 {
     m_d->hasPaintAtLeastOnce = true;
     m_d->strokesFacade->addJob(m_d->strokeId,
-        new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
-                                         painterInfo, pi));
+                               new \
FreehandStrokeStrategy::Data(m_d->resources->currentNode(), +                         \
painterInfo, pi));  
     if(m_d->recordingAdapter) {
         m_d->recordingAdapter->addPoint(pi);
@@ -253,8 +333,8 @@ void KisToolFreehandHelper::paintLine(PainterInfo *painterInfo,
 {
     m_d->hasPaintAtLeastOnce = true;
     m_d->strokesFacade->addJob(m_d->strokeId,
-        new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
-                                         painterInfo, pi1, pi2));
+                               new \
FreehandStrokeStrategy::Data(m_d->resources->currentNode(), +                         \
painterInfo, pi1, pi2));  
     if(m_d->recordingAdapter) {
         m_d->recordingAdapter->addLine(pi1, pi2);
@@ -269,9 +349,9 @@ void KisToolFreehandHelper::paintBezierCurve(PainterInfo \
*painterInfo,  {
     m_d->hasPaintAtLeastOnce = true;
     m_d->strokesFacade->addJob(m_d->strokeId,
-        new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
-                                         painterInfo,
-                                         pi1, control1, control2, pi2));
+                               new \
FreehandStrokeStrategy::Data(m_d->resources->currentNode(), +                         \
painterInfo, +                                                                pi1, \
control1, control2, pi2));  
     if(m_d->recordingAdapter) {
         m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2);
diff --git a/krita/ui/tool/kis_tool_freehand_helper.h \
b/krita/ui/tool/kis_tool_freehand_helper.h index 4978c85..ebfd526 100644
--- a/krita/ui/tool/kis_tool_freehand_helper.h
+++ b/krita/ui/tool/kis_tool_freehand_helper.h
@@ -35,21 +35,23 @@ class KisStrokesFacade;
 class KisPostExecutionUndoAdapter;
 class KisPaintOp;
 class KisPainter;
-
+class KisSmoothingOptions;
 
 class KRITAUI_EXPORT KisToolFreehandHelper : public QObject
 {
     Q_OBJECT
 
 protected:
+
     typedef FreehandStrokeStrategy::PainterInfo PainterInfo;
 
 public:
+
     KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
                           KisRecordingAdapter *recordingAdapter = 0);
     ~KisToolFreehandHelper();
 
-    void setSmoothness(bool smooth, qreal smoothness);
+    void setSmoothness(const KisSmoothingOptions &smoothingOptions);
 
     void initPaint(KoPointerEvent *event,
                    KoCanvasResourceManager *resourceManager,
@@ -63,6 +65,7 @@ public:
     const KisPaintOp* currentPaintOp() const;
 
 protected:
+
     virtual void createPainters(QVector<PainterInfo*> &painterInfos);
 
     virtual void paintAt(const QVector<PainterInfo*> &painterInfos,
@@ -78,7 +81,7 @@ protected:
                                   const QPointF &control2,
                                   const KisPaintInformation &pi2);
 
-protected:
+
     void paintAt(PainterInfo *painterInfo, const KisPaintInformation &pi);
 
     void paintLine(PainterInfo *painterInfo,
@@ -92,6 +95,7 @@ protected:
                           const KisPaintInformation &pi2);
 
 private slots:
+
     void finishStroke();
     void doAirbrushing();
 


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

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