kis_tool_freehand_helper.cpp 26.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 <math.h>
40

Dmitry Kazakov's avatar
Dmitry Kazakov committed
41
42
//#define DEBUG_BEZIER_CURVES

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

49
    KUndo2MagicString transactionText;
50

51
52
53
54
55
56
57
58
    bool haveTangent;
    QPointF previousTangent;

    bool hasPaintAtLeastOnce;

    QTime strokeTime;
    QTimer strokeTimeoutTimer;

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

    KisPaintInformation previousPaintInformation;
    KisPaintInformation olderPaintInformation;

66
    KisSmoothingOptionsSP smoothingOptions;
67
68

    QTimer airbrushingTimer;
69
70

    QList<KisPaintInformation> history;
71
    QList<qreal> distanceHistory;
72

73
    KisPaintOpUtils::PositionHistory lastOutlinePos;
74
75
76
77
78

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

80
    KisPaintInformation
81
82
    getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
                           const KisPaintInformation &lastPaintInfo);
83
84
85
};


Dmitry Kazakov's avatar
Dmitry Kazakov committed
86
KisToolFreehandHelper::KisToolFreehandHelper(KisPaintingInformationBuilder *infoBuilder,
87
                                             const KUndo2MagicString &transactionText,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
88
                                             KisRecordingAdapter *recordingAdapter)
89
90
91
    : m_d(new Private)
{
    m_d->infoBuilder = infoBuilder;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
92
    m_d->recordingAdapter = recordingAdapter;
93
    m_d->transactionText = transactionText;
94
    m_d->smoothingOptions = KisSmoothingOptionsSP(new KisSmoothingOptions());
95

96
97
98
    m_d->strokeTimeoutTimer.setSingleShot(true);
    connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
    connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
99
    connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
100
101
102
103
104
105
106
}

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

107
void KisToolFreehandHelper::setSmoothness(KisSmoothingOptionsSP smoothingOptions)
108
{
109
    m_d->smoothingOptions = smoothingOptions;
110
111
}

112
113
114
115
116
KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions() const
{
    return m_d->smoothingOptions;
}

117
QPainterPath KisToolFreehandHelper::paintOpOutline(const QPointF &savedCursorPos,
118
                                                   const KoPointerEvent *event,
119
120
121
122
                                                   const KisPaintOpSettings *globalSettings,
                                                   KisPaintOpSettings::OutlineMode mode) const
{
    const KisPaintOpSettings *settings = globalSettings;
123
    KisPaintInformation info = m_d->infoBuilder->hover(savedCursorPos, event);
124
    KisDistanceInformation distanceInfo(m_d->lastOutlinePos.pushThroughHistory(savedCursorPos), 0);
125
126
127
128
129
130
131

    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
132
    KisPaintInformation::DistanceInformationRegistrar registrar =
133
134
        info.registerDistanceInformation(&distanceInfo);

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
    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;
150
151
}

152
void KisToolFreehandHelper::initPaint(KoPointerEvent *event,
153
                                      KoCanvasResourceManager *resourceManager,
154
                                      KisImageWSP image, KisNodeSP currentNode,
155
156
                                      KisStrokesFacade *strokesFacade,
                                      KisPostExecutionUndoAdapter *undoAdapter,
157
158
                                      KisNodeSP overrideNode,
                                      KisDefaultBoundsBaseSP bounds)
159
160
161
162
163
164
165
{
    KisPaintInformation pi =
        m_d->infoBuilder->startStroke(event, elapsedStrokeTime());

    initPaintImpl(pi,
                  resourceManager,
                  image,
166
                  currentNode,
167
168
169
170
171
172
173
174
175
                  strokesFacade,
                  undoAdapter,
                  overrideNode,
                  bounds);
}

void KisToolFreehandHelper::initPaintImpl(const KisPaintInformation &previousPaintInformation,
                                          KoCanvasResourceManager *resourceManager,
                                          KisImageWSP image,
176
                                          KisNodeSP currentNode,
177
178
179
180
                                          KisStrokesFacade *strokesFacade,
                                          KisPostExecutionUndoAdapter *undoAdapter,
                                          KisNodeSP overrideNode,
                                          KisDefaultBoundsBaseSP bounds)
181
182
183
184
185
186
187
188
189
190
191
192
{
    Q_UNUSED(overrideNode);

    m_d->strokesFacade = strokesFacade;

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

    m_d->hasPaintAtLeastOnce = false;

    m_d->strokeTime.start();

193
    m_d->previousPaintInformation = previousPaintInformation;
194
195
196
197
198

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

199
    m_d->resources = new KisResourcesSnapshot(image,
200
                                              currentNode,
201
                                              undoAdapter,
202
203
                                              resourceManager,
                                              bounds);
204

Dmitry Kazakov's avatar
Dmitry Kazakov committed
205
206
207
208
    if(overrideNode) {
        m_d->resources->setCurrentNode(overrideNode);
    }

Dmitry Kazakov's avatar
Dmitry Kazakov committed
209
210
211
212
    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->startStroke(image, m_d->resources);
    }

213
    KisStrokeStrategy *stroke =
214
215
        new FreehandStrokeStrategy(m_d->resources->needsIndirectPainting(),
                                   m_d->resources->indirectPaintingCompositeOp(),
216
                                   m_d->resources, m_d->painterInfos, m_d->transactionText);
217
218
219

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

220
    m_d->history.clear();
221
    m_d->distanceHistory.clear();
222

223
224
225
226
    if(m_d->resources->needsAirbrushing()) {
        m_d->airbrushingTimer.setInterval(m_d->resources->airbrushingRate());
        m_d->airbrushingTimer.start();
    }
227

228
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
229
230
        stabilizerStart(m_d->previousPaintInformation);
    }
231
232
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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
272
273
//            qDebug() << "WARINING: there is no intersection point "
//                     << "in the basic smoothing algoriths";
Dmitry Kazakov's avatar
Dmitry Kazakov committed
274
275
276
277
278
279
280
281
282
283
284
285
        }

        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();

286
287
288
289
290
    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
291
292
293
294

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

    // the controls should not differ more than 50%
295
    similarity = qMax(similarity, qreal(0.5));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
296
297
298

    // when the controls are symmetric, their size should be smaller
    // to avoid corner-like curves
299
    coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316

    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;
    }

317
    paintBezierCurve(pi1,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
318
319
320
321
322
                     control1,
                     control2,
                     pi2);
}

323
324
325
void KisToolFreehandHelper::paint(KoPointerEvent *event)
{
    KisPaintInformation info =
326
            m_d->infoBuilder->continueStroke(event,
327
                                             elapsedStrokeTime());
328

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    /**
     * 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$
     */
348
349
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING
        && m_d->smoothingOptions->smoothnessDistance() > 0.0) {
350

351
        { // initialize current distance
352
353
354
355
356
357
358
359
360
            QPointF prevPos;

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

361
362
            qreal currentDistance = QVector2D(info.pos() - prevPos).length();
            m_d->distanceHistory.append(currentDistance);
363
364
        }

365
366
367
368
369
370
        m_d->history.append(info);

        qreal x = 0.0;
        qreal y = 0.0;

        if (m_d->history.size() > 3) {
371
            const qreal effectiveSmoothnessDistance =
372
373
374
                !m_d->smoothingOptions->useScalableDistance() ?
                m_d->smoothingOptions->smoothnessDistance() :
                m_d->smoothingOptions->smoothnessDistance() /
375
376
377
                m_d->resources->effectiveZoom();

            const qreal sigma = effectiveSmoothnessDistance / 3.0; // '3.0' for (3 * sigma) range
378

379
380
            qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
            qreal gaussianWeight2 = sigma * sigma;
381
            qreal distanceSum = 0.0;
382
            qreal scaleSum = 0.0;
383
            qreal pressure = 0.0;
384
            qreal baseRate = 0.0;
385

386
            Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
387

388
            for (int i = m_d->history.size() - 1; i >= 0; i--) {
389
390
391
                qreal rate = 0.0;

                const KisPaintInformation nextInfo = m_d->history.at(i);
392
393
                double distance = m_d->distanceHistory.at(i);
                Q_ASSERT(distance >= 0.0);
394

395
396
397
398
                qreal pressureGrad = 0.0;
                if (i < m_d->history.size() - 1) {
                    pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();

399
                    const qreal tailAgressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
400
401
402

                    if (pressureGrad > 0.0 ) {
                        pressureGrad *= tailAgressiveness * (1.0 - nextInfo.pressure());
403
                        distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
404
405
406
                    }
                }

407
                if (gaussianWeight2 != 0.0) {
408
409
                    distanceSum += distance;
                    rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
410
                }
411
412
413
414
415
416
417

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

418
419
420
                scaleSum += rate;
                x += rate * nextInfo.pos().x();
                y += rate * nextInfo.pos().y();
421

422
                if (m_d->smoothingOptions->smoothPressure()) {
423
424
                    pressure += rate * nextInfo.pressure();
                }
425
426
427
428
429
            }

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

431
                if (m_d->smoothingOptions->smoothPressure()) {
432
433
                    pressure /= scaleSum;
                }
434
            }
435

436
437
            if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
                info.setPos(QPointF(x, y));
438
                if (m_d->smoothingOptions->smoothPressure()) {
439
440
                    info.setPressure(pressure);
                }
441
                m_d->history.last() = info;
442
443
444
            }
        }
    }
445

446
447
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::SIMPLE_SMOOTHING
        || m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::WEIGHTED_SMOOTHING)
448
449
    {
        // Now paint between the coordinates, using the bezier curve interpolation
450
451
452
        if (!m_d->haveTangent) {
            m_d->haveTangent = true;
            m_d->previousTangent =
453
                (info.pos() - m_d->previousPaintInformation.pos()) /
454
                qMax(1.0, info.currentTime() - m_d->previousPaintInformation.currentTime());
455
        } else {
456
            QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
457
                qMax(1.0, info.currentTime() - m_d->olderPaintInformation.currentTime());
Dmitry Kazakov's avatar
Dmitry Kazakov committed
458
459
460
461

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

462
463
464
465
            m_d->previousTangent = newTangent;
        }
        m_d->olderPaintInformation = m_d->previousPaintInformation;
        m_d->strokeTimeoutTimer.start(100);
466
    }
467
    else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
468
        paintLine(m_d->previousPaintInformation, info);
469
470
    }

471
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
472
473
474
475
        m_d->stabilizerLastPaintInfo = info;
    } else {
        m_d->previousPaintInformation = info;
    }
476
477
478
479
480
481
482
483
484

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

void KisToolFreehandHelper::endPaint()
{
    if (!m_d->hasPaintAtLeastOnce) {
485
        paintAt(m_d->previousPaintInformation);
486
    } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
487
488
489
490
491
492
493
494
        finishStroke();
    }
    m_d->strokeTimeoutTimer.stop();

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

495
    if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
496
497
498
        stabilizerEnd();
    }

499
500
501
    /**
     * 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
502
     * Please note that we are not in MT here, so no mutex
503
504
     * is needed
     */
505
    m_d->painterInfos.clear();
506
507

    m_d->strokesFacade->endStroke(m_d->strokeId);
508
    m_d->strokeId.clear();
Dmitry Kazakov's avatar
Dmitry Kazakov committed
509
510
511
512

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->endStroke();
    }
513
514
}

515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
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();
}

546
547
548
void KisToolFreehandHelper::stabilizerStart(KisPaintInformation firstPaintInfo)
{
    // FIXME: Ugly hack, this is no a "distance" in any way
549
    int sampleSize = m_d->smoothingOptions->smoothnessDistance();
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    assert(sampleSize > 0);

    // 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();
}

564
565
566
KisPaintInformation
KisToolFreehandHelper::Private::getStabilizedPaintInfo(const QQueue<KisPaintInformation> &queue,
                                                       const KisPaintInformation &lastPaintInfo)
567
{
568
    KisPaintInformation result(lastPaintInfo);
569

570
571
572
    if (queue.size() > 1) {
        QQueue<KisPaintInformation>::const_iterator it = queue.constBegin();
        QQueue<KisPaintInformation>::const_iterator end = queue.constEnd();
573

574
575
576
577
578
        /**
         * The first point is going to be overridden by lastPaintInfo, skip it.
         */
        it++;
        int i = 2;
579

580
581
582
583
584
585
586
587
588
589
590
591
592
593
        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++;
            }
594
595
        }
    }
596

597
    return result;
598
599
600
601
}

void KisToolFreehandHelper::stabilizerPollAndPaint()
{
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
    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) {
618
        paintLine(m_d->previousPaintInformation, newInfo);
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
        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;
        }
    }
637
638
639
640
641
}

void KisToolFreehandHelper::stabilizerEnd()
{
    // FIXME: Ugly hack, this is no a "distance" in any way
642
    int sampleSize = m_d->smoothingOptions->smoothnessDistance();
643
644
645
646
647
648
649
650
651
652
    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.
653
654
655
        if (m_d->smoothingOptions->finishStabilizedCurve()) {
            stabilizerPollAndPaint();
        }
656
657
658
    }
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
659
660
const KisPaintOp* KisToolFreehandHelper::currentPaintOp() const
{
661
    return !m_d->painterInfos.isEmpty() ? m_d->painterInfos.first()->painter->paintOp() : 0;
Dmitry Kazakov's avatar
Dmitry Kazakov committed
662
663
}

664
665
void KisToolFreehandHelper::finishStroke()
{
666
    if (m_d->haveTangent) {
667
668
        m_d->haveTangent = false;

Dmitry Kazakov's avatar
Dmitry Kazakov committed
669
670
671
672
673
674
675
        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);
676
677
678
679
680
    }
}

void KisToolFreehandHelper::doAirbrushing()
{
681
    if(!m_d->painterInfos.isEmpty()) {
682
        paintAt(m_d->previousPaintInformation);
683
684
685
    }
}

686
void KisToolFreehandHelper::paintAt(PainterInfo *painterInfo,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
687
                                    const KisPaintInformation &pi)
688
689
690
{
    m_d->hasPaintAtLeastOnce = true;
    m_d->strokesFacade->addJob(m_d->strokeId,
691
692
                               new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
                                                                painterInfo, pi));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
693
694
695
696

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->addPoint(pi);
    }
697
698
}

699
void KisToolFreehandHelper::paintLine(PainterInfo *painterInfo,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
700
                                      const KisPaintInformation &pi1,
701
702
703
704
                                      const KisPaintInformation &pi2)
{
    m_d->hasPaintAtLeastOnce = true;
    m_d->strokesFacade->addJob(m_d->strokeId,
705
706
                               new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
                                                                painterInfo, pi1, pi2));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
707
708
709
710

    if(m_d->recordingAdapter) {
        m_d->recordingAdapter->addLine(pi1, pi2);
    }
711
712
}

713
void KisToolFreehandHelper::paintBezierCurve(PainterInfo *painterInfo,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
714
                                             const KisPaintInformation &pi1,
715
716
717
718
                                             const QPointF &control1,
                                             const QPointF &control2,
                                             const KisPaintInformation &pi2)
{
Dmitry Kazakov's avatar
Dmitry Kazakov committed
719
720
721
722
723
724
725
726
727
728
#ifdef DEBUG_BEZIER_CURVES
    KisPaintInformation tpi1;
    KisPaintInformation tpi2;

    tpi1 = pi1;
    tpi2 = pi2;

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

729
    paintLine(tpi1, tpi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
730
731
732
733
734
735

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

    tpi1.setPos(pi1.pos());
    tpi2.setPos(control1);
736
    paintLine(tpi1, tpi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
737
738
739

    tpi1.setPos(pi2.pos());
    tpi2.setPos(control2);
740
    paintLine(tpi1, tpi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
741
742
#endif

743
744
    m_d->hasPaintAtLeastOnce = true;
    m_d->strokesFacade->addJob(m_d->strokeId,
745
746
747
                               new FreehandStrokeStrategy::Data(m_d->resources->currentNode(),
                                                                painterInfo,
                                                                pi1, control1, control2, pi2));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
748
749
750
751

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

754
755
756
void KisToolFreehandHelper::createPainters(QVector<PainterInfo*> &painterInfos,
                                           const QPointF &lastPosition,
                                           int lastTime)
Dmitry Kazakov's avatar
Dmitry Kazakov committed
757
{
758
759
760
    painterInfos <<
        new PainterInfo(new KisPainter(),
                        new KisDistanceInformation(lastPosition, lastTime));
Dmitry Kazakov's avatar
Dmitry Kazakov committed
761
762
}

763
void KisToolFreehandHelper::paintAt(const QVector<PainterInfo*> &painterInfos,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
764
765
                                    const KisPaintInformation &pi)
{
766
    paintAt(painterInfos.first(), pi);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
767
768
}

769
void KisToolFreehandHelper::paintLine(const QVector<PainterInfo*> &painterInfos,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
770
771
772
                                      const KisPaintInformation &pi1,
                                      const KisPaintInformation &pi2)
{
773
    paintLine(painterInfos.first(), pi1, pi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
774
775
}

776
void KisToolFreehandHelper::paintBezierCurve(const QVector<PainterInfo*> &painterInfos,
Dmitry Kazakov's avatar
Dmitry Kazakov committed
777
778
779
780
781
                                             const KisPaintInformation &pi1,
                                             const QPointF &control1,
                                             const QPointF &control2,
                                             const KisPaintInformation &pi2)
{
782
    paintBezierCurve(painterInfos.first(), pi1, control1, control2, pi2);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
783
784
}

785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
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);
}