kis_tool_freehand_helper.cpp 27.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 *  Copyright (c) 2011 Dmitry Kazakov <dimula73@gmail.com>
 *
 *  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
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  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_tool_freehand_helper.h"

#include <QTimer>
22
#include <QQueue>
23

Halla Rempt's avatar
Halla Rempt committed
24
#include <klocale.h>
25

26
#include <KoPointerEvent.h>
27
#include <KoCanvasResourceManager.h>
28 29 30

#include "kis_distance_information.h"
#include "kis_painting_information_builder.h"
Dmitry Kazakov's avatar
Dmitry Kazakov committed
31
#include "kis_recording_adapter.h"
32 33
#include "kis_image.h"
#include "kis_painter.h"
34
#include "kis_smoothing_options.h"
35
#include "kis_paintop_preset.h"
36
#include "kis_paintop_utils.h"
37

38 39
#include "kis_update_time_monitor.h"

40

41
#include <math.h>
42

Dmitry Kazakov's avatar
Dmitry Kazakov committed
43 44
//#define DEBUG_BEZIER_CURVES

45 46 47
struct KisToolFreehandHelper::Private
{
    KisPaintingInformationBuilder *infoBuilder;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
48
    KisRecordingAdapter *recordingAdapter;
49 50
    KisStrokesFacade *strokesFacade;

51
    KUndo2MagicString transactionText;
52

53 54 55 56 57 58 59 60
    bool haveTangent;
    QPointF previousTangent;

    bool hasPaintAtLeastOnce;

    QTime strokeTime;
    QTimer strokeTimeoutTimer;

61
    QVector<PainterInfo*> painterInfos;
62 63 64 65 66 67
    KisResourcesSnapshotSP resources;
    KisStrokeId strokeId;

    KisPaintInformation previousPaintInformation;
    KisPaintInformation olderPaintInformation;

68
    KisSmoothingOptionsSP smoothingOptions;
69 70

    QTimer airbrushingTimer;
71 72

    QList<KisPaintInformation> history;
73
    QList<qreal> distanceHistory;
74

75
    KisPaintOpUtils::PositionHistory lastOutlinePos;
76 77 78 79 80

    // Stabilizer data
    QQueue<KisPaintInformation> stabilizerDeque;
    KisPaintInformation stabilizerLastPaintInfo;
    QTimer stabilizerPollTimer;
81

82 83
    int canvasRotation;
    bool canvasMirroredH;
84

85
    KisPaintInformation
86 87
    getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
                           const KisPaintInformation &lastPaintInfo);
88 89

    qreal effectiveSmoothnessDistance() const;
90 91 92
};


Dmitry Kazakov's avatar
Dmitry Kazakov committed
93
KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
94
                                             const KUndo2MagicString &transactionText,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
95
                                             KisRecordingAdapter *recordingAdapter)
Halla Rempt's avatar
Halla Rempt committed
96
    : m_d(new Private())
97 98
{
    m_d->infoBuilder = infoBuilder;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
99
    m_d->recordingAdapter = recordingAdapter;
100
    m_d->transactionText = transactionText;
101
    m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions());
Halla Rempt's avatar
Halla Rempt committed
102
    m_d->canvasRotation = 0;
103

104 105 106
    m_d->strokeTimeoutTimer.setSingleShot(true);
    connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
    connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
107
    connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
108 109 110 111 112 113 114
}

KisToolFreehandHelper::~KisToolFreehandHelper()
{
    delete m_d;
}

115
void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions)
116
{
117
    m_d->smoothingOptions = smoothingOptions;
118 119
}

120 121 122 123 124
KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const
{
    return m_d->smoothingOptions;
}

125
QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos,
126
                                                   const KoPointerEvent *event,
127 128 129 130
                                                   const KisPaintOpSettings *globalSettings,
                                                   KisPaintOpSettings::OutlineMode mode) const
{
    const KisPaintOpSettings *settings = globalSettings;
131
    KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event);
132 133
    info.setCanvasRotation(m_d->canvasRotation);
    info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
134
    KisDistanceInformation distanceInfo(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0);
135 136 137 138 139 140 141

    if (!m_d->painterInfos.isEmpty()) {
        settings = m_d->resources->currentPaintOpPreset()->settings();
        info = m_d->previousPaintInformation;
        distanceInfo = *m_d->painterInfos.first()->dragDistance;
    }

Halla Rempt's avatar
Halla Rempt committed
142
    KisPaintInformation::DistanceInformationRegistrar registrar =
143 144
        info.registerDistanceInformation(&distanceInfo);

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    QPainterPath outline = settings->brushOutline(info, mode);



    if (m_d->resources &&
        m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER &&
        m_d->smoothingOptions->useDelayDistance()) {

        const qreal R = m_d->smoothingOptions->delayDistance() /
            m_d->resources->effectiveZoom();

        outline.addEllipse(info.pos(), R, R);
    }

    return outline;
160 161
}

162
void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
163
                                      KoCanvasResourceManager *resourceManager,
164
                                      KisImageWSP image, KisNodeSP currentNode,
165 166
                                      KisStrokesFacade *strokesFacade,
                                      KisPostExecutionUndoAdapter *undoAdapter,
167 168
                                      KisNodeSP overrideNode,
                                      KisDefaultBoundsBaseSP bounds)
169 170 171 172 173 174 175
{
    KisPaintInformation pi =
        m_d->infoBuilder->startStroke(event, elapsedStrokeTime());

    initPaintImpl(pi,
                  resourceManager,
                  image,
176
                  currentNode,
177 178 179 180 181 182
                  strokesFacade,
                  undoAdapter,
                  overrideNode,
                  bounds);
}

183 184 185 186 187
bool KisToolFreehandHelper::isRunning() const
{
    return m_d->strokeId;
}

188 189 190
void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation,
                                          KoCanvasResourceManager *resourceManager,
                                          KisImageWSP image,
191
                                          KisNodeSP currentNode,
192 193 194 195
                                          KisStrokesFacade *strokesFacade,
                                          KisPostExecutionUndoAdapter *undoAdapter,
                                          KisNodeSP overrideNode,
                                          KisDefaultBoundsBaseSP bounds)
196 197 198 199 200 201 202 203 204 205 206 207
{
    Q_UNUSED(overrideNode);

    m_d->strokesFacade = strokesFacade;

    m_d->haveTangent = false;
    m_d->previousTangent = QPointF();

    m_d->hasPaintAtLeastOnce = false;

    m_d->strokeTime.start();

208
    m_d->previousPaintInformation = previousPaintInformation;
209 210 211 212 213

    createPainters(m_d->painterInfos,
                   m_d->previousPaintInformation.pos(),
                   m_d->previousPaintInformation.currentTime());

214
    m_d->resources = new KisResourcesSnapshot(image,
215
                                              currentNode,
216
                                              undoAdapter,
217 218
                                              resourceManager,
                                              bounds);
219

Dmitry Kazakov's avatar
Dmitry Kazakov committed
220 221 222 223
    if(overrideNode) {
        m_d->resources->setCurrentNode(overrideNode);
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
224 225 226 227
    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->startStroke(image, m_d->resources);
    }

228
    KisStrokeStrategy *stroke =
229 230
        new FreehandStrokeStrategy(m_d->resources->needsIndirectPainting(),
                                   m_d->resources->indirectPaintingCompositeOp(),
231
                                   m_d->resources, m_d->painterInfos, m_d->transactionText);
232 233 234

    m_d->strokeId = m_d->strokesFacade->startStroke(stroke);

235
    m_d->history.clear();
236
    m_d->distanceHistory.clear();
237

238 239 240 241
    if(m_d->resources->needsAirbrushing()) {
        m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate());
        m_d->airbrushingTimer.start();
    }
242

243
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
244 245
        stabilizerStart(m_d->previousPaintInformation);
    }
246 247
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
void KisToolFreehandHelper::paintBezierSegment(KisPaintInformation pi1, KisPaintInformation pi2,
                                               QPointF tangent1, QPointF tangent2)
{
    if (tangent1.isNull() || tangent2.isNull()) return;

    const qreal maxSanePoint = 1e6;

    QPointF controlTarget1;
    QPointF controlTarget2;

    // Shows the direction in which control points go
    QPointF controlDirection1 = pi1.pos() + tangent1;
    QPointF controlDirection2 = pi2.pos() - tangent2;

    // Lines in the direction of the control points
    QLineF line1(pi1.pos(), controlDirection1);
    QLineF line2(pi2.pos(), controlDirection2);

    // Lines to check whether the control points lay on the opposite
    // side of the line
    QLineF line3(controlDirection1, controlDirection2);
    QLineF line4(pi1.pos(), pi2.pos());

    QPointF intersection;
    if (line3.intersect(line4, &intersection) == QLineF::BoundedIntersection) {
        qreal controlLength = line4.length() / 2;

        line1.setLength(controlLength);
        line2.setLength(controlLength);

        controlTarget1 = line1.p2();
        controlTarget2 = line2.p2();
    } else {
        QLineF::IntersectType type = line1.intersect(line2, &intersection);

        if (type == QLineF::NoIntersection ||
            intersection.manhattanLength() > maxSanePoint) {

            intersection = 0.5 * (pi1.pos() + pi2.pos());
Halla Rempt's avatar
Halla Rempt committed
287 288
//            qDebug() << "WARINING: there is no intersection point "
//                     << "in the basic smoothing algoriths";
Dmitry Kazakov's avatar
Dmitry Kazakov committed
289 290 291 292 293 294 295 296 297 298 299 300
        }

        controlTarget1 = intersection;
        controlTarget2 = intersection;
    }

    // shows how near to the controlTarget the value raises
    qreal coeff = 0.8;

    qreal velocity1 = QLineF(QPointF(), tangent1).length();
    qreal velocity2 = QLineF(QPointF(), tangent2).length();

301 302 303 304 305
    if (velocity1 == 0.0 || velocity2 == 0.0) {
        velocity1 = 1e-6;
        velocity2 = 1e-6;
        qWarning() << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
    }
Dmitry Kazakov's avatar
Dmitry Kazakov committed
306 307 308 309

    qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);

    // the controls should not differ more than 50%
310
    similarity = qMax(similarity, qreal(0.5));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
311 312 313

    // when the controls are symmetric, their size should be smaller
    // to avoid corner-like curves
314
    coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331

    Q_ASSERT(coeff > 0);


    QPointF control1;
    QPointF control2;

    if (velocity1 > velocity2) {
        control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
        coeff *= similarity;
        control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
    } else {
        control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
        coeff *= similarity;
        control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
    }

332
    paintBezierCurve(pi1,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
333 334 335 336 337
                     control1,
                     control2,
                     pi2);
}

338 339 340 341 342 343 344 345 346 347
qreal KisToolFreehandHelper::Private::effectiveSmoothnessDistance() const
{
    const qreal effectiveSmoothnessDistance =
        !smoothingOptions->useScalableDistance() ?
        smoothingOptions->smoothnessDistance() :
        smoothingOptions->smoothnessDistance() / resources->effectiveZoom();

    return effectiveSmoothnessDistance;
}

348 349 350
void KisToolFreehandHelper::paint(KoPointerEvent *event)
{
    KisPaintInformation info =
351
            m_d->infoBuilder->continueStroke(event,
352
                                             elapsedStrokeTime());
353 354
    info.setCanvasRotation( m_d->canvasRotation );
    info.setCanvasHorizontalMirrorState( m_d->canvasMirroredH );
355

356 357
    KisUpdateTimeMonitor::instance()->reportMouseMove(info.pos());

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376
    /**
     * 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$
     */
377 378
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING
        && m_d->smoothingOptions->smoothnessDistance() > 0.0) {
379

380
        { // initialize current distance
381 382 383 384 385 386 387 388 389
            QPointF prevPos;

            if (!m_d->history.isEmpty()) {
                const KisPaintInformation &prevPi = m_d->history.last();
                prevPos = prevPi.pos();
            } else {
                prevPos = m_d->previousPaintInformation.pos();
            }

390 391
            qreal currentDistance = QVector2D(info.pos() - prevPos).length();
            m_d->distanceHistory.append(currentDistance);
392 393
        }

394 395 396 397 398 399
        m_d->history.append(info);

        qreal x = 0.0;
        qreal y = 0.0;

        if (m_d->history.size() > 3) {
400
            const qreal sigma = m_d->effectiveSmoothnessDistance() / 3.0; // '3.0' for (3 * sigma) range
401

402 403
            qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
            qreal gaussianWeight2 = sigma * sigma;
404
            qreal distanceSum = 0.0;
405
            qreal scaleSum = 0.0;
406
            qreal pressure = 0.0;
407
            qreal baseRate = 0.0;
408

409
            Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
410

411
            for (int i = m_d->history.size() - 1; i >= 0; i--) {
412 413 414
                qreal rate = 0.0;

                const KisPaintInformation nextInfo = m_d->history.at(i);
415 416
                double distance = m_d->distanceHistory.at(i);
                Q_ASSERT(distance >= 0.0);
417

418 419 420 421
                qreal pressureGrad = 0.0;
                if (i < m_d->history.size() - 1) {
                    pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();

422
                    const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
423 424 425

                    if (pressureGrad > 0.0 ) {
                        pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
426
                        distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
427 428 429
                    }
                }

430
                if (gaussianWeight2 != 0.0) {
431 432
                    distanceSum += distance;
                    rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
433
                }
434 435 436 437 438 439 440

                if (m_d->history.size() - i == 1) {
                    baseRate = rate;
                } else if (baseRate / rate > 100) {
                    break;
                }

441 442 443
                scaleSum += rate;
                x += rate * nextInfo.pos().x();
                y += rate * nextInfo.pos().y();
444

445
                if (m_d->smoothingOptions->smoothPressure()) {
446 447
                    pressure += rate * nextInfo.pressure();
                }
448 449 450 451 452
            }

            if (scaleSum != 0.0) {
                x /= scaleSum;
                y /= scaleSum;
453

454
                if (m_d->smoothingOptions->smoothPressure()) {
455 456
                    pressure /= scaleSum;
                }
457
            }
458

459 460
            if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
                info.setPos(QPointF(x, y));
461
                if (m_d->smoothingOptions->smoothPressure()) {
462 463
                    info.setPressure(pressure);
                }
464
                m_d->history.last() = info;
465 466 467
            }
        }
    }
468

469 470
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING
        || m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING)
471 472
    {
        // Now paint between the coordinates, using the bezier curve interpolation
473 474 475
        if (!m_d->haveTangent) {
            m_d->haveTangent = true;
            m_d->previousTangent =
476
                (info.pos() - m_d->previousPaintInformation.pos()) /
477
                qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
478
        } else {
479
            QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
480
                qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
Dmitry Kazakov's avatar
Dmitry Kazakov committed
481 482 483 484

            paintBezierSegment(m_d->olderPaintInformation, m_d->previousPaintInformation,
                               m_d->previousTangent, newTangent);

485 486 487 488
            m_d->previousTangent = newTangent;
        }
        m_d->olderPaintInformation = m_d->previousPaintInformation;
        m_d->strokeTimeoutTimer.start(100);
489
    }
490
    else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
491
        paintLine(m_d->previousPaintInformation, info);
492 493
    }

494
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
495 496 497 498
        m_d->stabilizerLastPaintInfo = info;
    } else {
        m_d->previousPaintInformation = info;
    }
499 500 501 502 503 504 505 506 507

    if(m_d->airbrushingTimer.isActive()) {
        m_d->airbrushingTimer.start();
    }
}

void KisToolFreehandHelper::endPaint()
{
    if (!m_d->hasPaintAtLeastOnce) {
508
        paintAt(m_d->previousPaintInformation);
509
    } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
510 511 512 513 514 515 516 517
        finishStroke();
    }
    m_d->strokeTimeoutTimer.stop();

    if(m_d->airbrushingTimer.isActive()) {
        m_d->airbrushingTimer.stop();
    }

518
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
519 520 521
        stabilizerEnd();
    }

522 523 524
    /**
     * There might be some timer events still pending, so
     * we should cancel them. Use this flag for the purpose.
Dmitry Kazakov's avatar
Dmitry Kazakov committed
525
     * Please note that we are not in MT here, so no mutex
526 527
     * is needed
     */
528
    m_d->painterInfos.clear();
529 530

    m_d->strokesFacade->endStroke(m_d->strokeId);
531
    m_d->strokeId.clear();
Dmitry Kazakov's avatar
Dmitry Kazakov committed
532 533 534 535

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->endStroke();
    }
536 537
}

538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
void KisToolFreehandHelper::cancelPaint()
{
    if (!m_d->strokeId) return;

    m_d->strokeTimeoutTimer.stop();

    if (m_d->airbrushingTimer.isActive()) {
        m_d->airbrushingTimer.stop();
    }

    if (m_d->stabilizerPollTimer.isActive()) {
        m_d->stabilizerPollTimer.stop();
    }

    // see a comment in endPaint()
    m_d->painterInfos.clear();

    m_d->strokesFacade->cancelStroke(m_d->strokeId);
    m_d->strokeId.clear();

    if(m_d->recordingAdapter) {
        //FIXME: not implemented
        //m_d->recordingAdapter->cancelStroke();
    }
}

int KisToolFreehandHelper::elapsedStrokeTime() const
{
    return m_d->strokeTime.elapsed();
}

569 570 571
void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
{
    // FIXME: Ugly hack, this is no a "distance" in any way
572 573
    int sampleSize = qRound(m_d->effectiveSmoothnessDistance());
    sampleSize = qMax(3, sampleSize);
574 575 576 577 578 579 580 581 582 583 584 585 586

    // Fill the deque with the current value repeated until filling the sample
    m_d->stabilizerDeque.clear();
    for (int i = sampleSize; i > 0; i--) {
        m_d->stabilizerDeque.enqueue(firstPaintInfo);
    }
    m_d->stabilizerLastPaintInfo = firstPaintInfo;

    // Poll and draw each millisecond
    m_d->stabilizerPollTimer.setInterval(1);
    m_d->stabilizerPollTimer.start();
}

587 588 589
KisPaintInformation
KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
                                                       const KisPaintInformation &lastPaintInfo)
590
{
591
    KisPaintInformation result(lastPaintInfo);
592

593 594 595
    if (queue.size() > 1) {
        QQueue<KisPaintInformation>::const_iterator it = queue.constBegin();
        QQueue<KisPaintInformation>::const_iterator end = queue.constEnd();
596

597 598 599 600 601
        /**
         * The first point is going to be overridden by lastPaintInfo, skip it.
         */
        it++;
        int i = 2;
602

603 604 605 606 607 608 609 610 611 612 613 614 615 616
        if (smoothingOptions->stabilizeSensors()) {
            while (it != end) {
                qreal k = qreal(i - 1) / i; // coeff for uniform averaging
                result = KisPaintInformation::mix(k, *it, result);
                it++;
                i++;
            }
        } else{
            while (it != end) {
                qreal k = qreal(i - 1) / i; // coeff for uniform averaging
                result = KisPaintInformation::mixOnlyPosition(k, *it, result);
                it++;
                i++;
            }
617 618
        }
    }
619

620
    return result;
621 622 623 624
}

void KisToolFreehandHelper::stabilizerPollAndPaint()
{
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
    KisPaintInformation newInfo =
        m_d->getStabilizedPaintInfo(m_d->stabilizerDeque, m_d->stabilizerLastPaintInfo);

    bool canPaint = true;

    if (m_d->smoothingOptions->useDelayDistance()) {
        const qreal R = m_d->smoothingOptions->delayDistance() /
            m_d->resources->effectiveZoom();

        QPointF diff = m_d->stabilizerLastPaintInfo.pos() - m_d->previousPaintInformation.pos();
        qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));

        canPaint = dx > R;
    }

    if (canPaint) {
641
        paintLine(m_d->previousPaintInformation, newInfo);
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
        m_d->previousPaintInformation = newInfo;

        // Push the new entry through the queue
        m_d->stabilizerDeque.dequeue();
        m_d->stabilizerDeque.enqueue(m_d->stabilizerLastPaintInfo);

        emit requestExplicitUpdateOutline();

    } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {

        QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
        QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();

        while (it != end) {
            *it = m_d->previousPaintInformation;
            ++it;
        }
    }
660 661 662 663 664
}

void KisToolFreehandHelper::stabilizerEnd()
{
    // FIXME: Ugly hack, this is no a "distance" in any way
665
    int sampleSize = m_d->smoothingOptions->smoothnessDistance();
666 667 668 669 670 671 672 673 674 675
    assert(sampleSize > 0);

    // Stop the timer
    m_d->stabilizerPollTimer.stop();

    // Finish the line
    for (int i = sampleSize; i > 0; i--) {
        // In each iteration we add the latest paint info and delete the oldest
        // After `sampleSize` iterations the deque will be filled with the latest
        // value and we will have reached the end point.
676 677 678
        if (m_d->smoothingOptions->finishStabilizedCurve()) {
            stabilizerPollAndPaint();
        }
679 680 681
    }
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
682 683
const KisPaintOp* KisToolFreehandHelper::currentPaintOp() const
{
684
    return !m_d->painterInfos.isEmpty() ? m_d->painterInfos.first()->painter->paintOp() : 0;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
685 686
}

687 688
void KisToolFreehandHelper::finishStroke()
{
689
    if (m_d->haveTangent) {
690 691
        m_d->haveTangent = false;

Dmitry Kazakov's avatar
Dmitry Kazakov committed
692 693 694 695 696 697 698
        QPointF newTangent = (m_d->previousPaintInformation.pos() - m_d->olderPaintInformation.pos()) /
            (m_d->previousPaintInformation.currentTime() - m_d->olderPaintInformation.currentTime());

        paintBezierSegment(m_d->olderPaintInformation,
                           m_d->previousPaintInformation,
                           m_d->previousTangent,
                           newTangent);
699 700 701 702 703
    }
}

void KisToolFreehandHelper::doAirbrushing()
{
704
    if(!m_d->painterInfos.isEmpty()) {
705
        paintAt(m_d->previousPaintInformation);
706 707 708
    }
}

709
void KisToolFreehandHelper::paintAt(PainterInfo *painterInfo,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
710
                                    const KisPaintInformation &pi)
711 712 713
{
    m_d->hasPaintAtLeastOnce = true;
    m_d->strokesFacade->addJob(m_d->strokeId,
714 715
                               new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
                                                                painterInfo, pi));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
716 717 718 719

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->addPoint(pi);
    }
720 721
}

722
void KisToolFreehandHelper::paintLine(PainterInfo *painterInfo,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
723
                                      const KisPaintInformation &pi1,
724 725 726 727
                                      const KisPaintInformation &pi2)
{
    m_d->hasPaintAtLeastOnce = true;
    m_d->strokesFacade->addJob(m_d->strokeId,
728 729
                               new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
                                                                painterInfo, pi1, pi2));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
730 731 732 733

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->addLine(pi1, pi2);
    }
734 735
}

736
void KisToolFreehandHelper::paintBezierCurve(PainterInfo *painterInfo,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
737
                                             const KisPaintInformation &pi1,
738 739 740 741
                                             const QPointF &control1,
                                             const QPointF &control2,
                                             const KisPaintInformation &pi2)
{
Dmitry Kazakov's avatar
Dmitry Kazakov committed
742 743 744 745 746 747 748 749 750 751
#ifdef DEBUG_BEZIER_CURVES
    KisPaintInformation tpi1;
    KisPaintInformation tpi2;

    tpi1 = pi1;
    tpi2 = pi2;

    tpi1.setPressure(0.3);
    tpi2.setPressure(0.3);

752
    paintLine(tpi1, tpi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
753 754 755 756 757 758

    tpi1.setPressure(0.6);
    tpi2.setPressure(0.3);

    tpi1.setPos(pi1.pos());
    tpi2.setPos(control1);
759
    paintLine(tpi1, tpi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
760 761 762

    tpi1.setPos(pi2.pos());
    tpi2.setPos(control2);
763
    paintLine(tpi1, tpi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
764 765
#endif

766 767
    m_d->hasPaintAtLeastOnce = true;
    m_d->strokesFacade->addJob(m_d->strokeId,
768 769 770
                               new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
                                                                painterInfo,
                                                                pi1, control1, control2, pi2));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
771 772 773 774

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->addCurve(pi1, control1, control2, pi2);
    }
775 776
}

777 778 779
void KisToolFreehandHelper::createPainters(QVector<PainterInfo*> &painterInfos,
                                           const QPointF &lastPosition,
                                           int lastTime)
Dmitry Kazakov's avatar
Dmitry Kazakov committed
780
{
781 782 783
    painterInfos <<
        new PainterInfo(new KisPainter(),
                        new KisDistanceInformation(lastPosition, lastTime));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
784 785
}

786
void KisToolFreehandHelper::paintAt(const QVector<PainterInfo*> &painterInfos,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
787 788
                                    const KisPaintInformation &pi)
{
789
    paintAt(painterInfos.first(), pi);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
790 791
}

792
void KisToolFreehandHelper::paintLine(const QVector<PainterInfo*> &painterInfos,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
793 794 795
                                      const KisPaintInformation &pi1,
                                      const KisPaintInformation &pi2)
{
796
    paintLine(painterInfos.first(), pi1, pi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
797 798
}

799
void KisToolFreehandHelper::paintBezierCurve(const QVector<PainterInfo*> &painterInfos,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
800 801 802 803 804
                                             const KisPaintInformation &pi1,
                                             const QPointF &control1,
                                             const QPointF &control2,
                                             const KisPaintInformation &pi2)
{
805
    paintBezierCurve(painterInfos.first(), pi1, control1, control2, pi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
806 807
}

808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825
void KisToolFreehandHelper::paintAt(const KisPaintInformation &pi)
{
    paintAt(m_d->painterInfos, pi);
}

void KisToolFreehandHelper::paintLine(const KisPaintInformation &pi1,
                                      const KisPaintInformation &pi2)
{
    paintLine(m_d->painterInfos, pi1, pi2);
}

void KisToolFreehandHelper::paintBezierCurve(const KisPaintInformation &pi1,
                                             const QPointF &control1,
                                             const QPointF &control2,
                                             const KisPaintInformation &pi2)
{
    paintBezierCurve(m_d->painterInfos, pi1, control1, control2, pi2);
}
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845

int KisToolFreehandHelper::canvasRotation()
{
    return m_d->canvasRotation;
}

void KisToolFreehandHelper::setCanvasRotation(int rotation)
{
   m_d->canvasRotation = rotation; 
}
bool KisToolFreehandHelper::canvasMirroredH()
{
    return m_d->canvasMirroredH;
}

void KisToolFreehandHelper::setCanvasHorizontalMirrorState(bool mirrored)
{
   m_d->canvasMirroredH = mirrored; 
}