Commit 6382948d authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

[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@kde.org
parent 55753fd9
......@@ -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);
......
......@@ -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;
};
......
......@@ -19,7 +19,7 @@
KisSmoothingOptions::KisSmoothingOptions()
: smoothingType(WEIGHTED_SMOOTHING)
, smoothnessFactor(50.0)
, smoothnessQuality(20)
, smoothnessDistance(50.0)
, tailAggressiveness(0.15)
{
}
......@@ -32,9 +32,8 @@ struct KisSmoothingOptions
KisSmoothingOptions();
SmoothingType smoothingType;
qreal smoothnessFactor;
int smoothnessQuality;
qreal smoothnessDistance;
qreal tailAggressiveness;
};
#endif // KIS_SMOOTHING_OPTIONS_H
......@@ -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;
}
}
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment