Commit 0f1baf35 authored by Dmitry Kazakov's avatar Dmitry Kazakov
Browse files

Make the Weighted Smoothing algorithm use 'distance' instead of 'velocity'

The time measurements of the tablet events are really unstable,
especially when using openGL canvas, which causes the Weighted Smoothing
processed lines bended and waggy. This patch makes the algorithm use
distance parameter instead of the velocity parameter, which does not
depend on time, so does not fluctuate so drastically.

BUG:326512
parent d0bc58fe
......@@ -64,7 +64,7 @@ struct KisToolFreehandHelper::Private
QTimer airbrushingTimer;
QList<KisPaintInformation> history;
QList<qreal> velocityHistory;
QList<qreal> distanceHistory;
};
......@@ -138,7 +138,7 @@ void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
m_d->strokeId = m_d->strokesFacade->startStroke(stroke);
m_d->history.clear();
m_d->velocityHistory.clear();
m_d->distanceHistory.clear();
if(m_d->resources->needsAirbrushing()) {
m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate());
......@@ -243,28 +243,40 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
m_d->infoBuilder->continueStroke(event,
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.
/**
* Smooth the coordinates out using the history and the
* distance. This is a heavily modified version of an algo used in
* Gimp and described in https://bugs.kde.org/show_bug.cgi?id=281267 and
* http://www24.atwiki.jp/sigetch_2007/pages/17.html. The main
* differences are:
*
* 1) It uses 'distance' instead of 'velocity', since time
* measurements are too unstable in realworld environment
*
* 2) There is no 'Quality' parameter, since the number of samples
* is calculated automatically
*
* 3) 'Tail Aggressiveness' is used for controling the end of the
* stroke
*
* 4) The formila is a little bit different: 'Distance' parameter
* stands for $3 \Sigma$
*/
if (m_d->smoothingOptions.smoothingType == KisSmoothingOptions::WEIGHTED_SMOOTHING
&& m_d->smoothingOptions.smoothnessDistance > 0.0) {
{ // initialize current velocity
{ // initialize current distance
QPointF prevPos;
int prevTime;
if (!m_d->history.isEmpty()) {
const KisPaintInformation &prevPi = m_d->history.last();
prevPos = prevPi.pos();
prevTime = prevPi.currentTime();
} else {
prevPos = m_d->previousPaintInformation.pos();
prevTime = m_d->previousPaintInformation.currentTime();
}
int deltaTime = qMax(1, info.currentTime() - prevTime); // make sure deltaTime > 1
qreal currentVelocity = QVector2D(info.pos() - prevPos).length() / deltaTime;
m_d->velocityHistory.append(currentVelocity);
qreal currentDistance = QVector2D(info.pos() - prevPos).length();
m_d->distanceHistory.append(currentDistance);
}
m_d->history.append(info);
......@@ -273,24 +285,23 @@ 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
const qreal sigma = m_d->smoothingOptions.smoothnessDistance / 3.0; // '3.0' for (3 * sigma) range
qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
qreal gaussianWeight2 = sigma * sigma;
qreal velocitySum = 0.0;
qreal distanceSum = 0.0;
qreal scaleSum = 0.0;
qreal pressure = 0.0;
qreal baseRate = 0.0;
Q_ASSERT(m_d->history.size() == m_d->velocityHistory.size());
Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
for (int i = m_d->history.size() - 1; i >= 0; i--) {
qreal rate = 0.0;
const KisPaintInformation nextInfo = m_d->history.at(i);
double velocity = m_d->velocityHistory.at(i);
Q_ASSERT(velocity >= 0.0);
double distance = m_d->distanceHistory.at(i);
Q_ASSERT(distance >= 0.0);
qreal pressureGrad = 0.0;
if (i < m_d->history.size() - 1) {
......@@ -300,13 +311,13 @@ void KisToolFreehandHelper::paint(KoPointerEvent *event)
if (pressureGrad > 0.0 ) {
pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
velocity += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
}
}
if (gaussianWeight2 != 0.0) {
velocitySum += velocity;
rate = gaussianWeight * exp(-velocitySum * velocitySum / (2 * gaussianWeight2));
distanceSum += distance;
rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
}
if (m_d->history.size() - i == 1) {
......
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