Krita Source Code Documentation
Loading...
Searching...
No Matches
KisToolFreehandHelper Class Reference

#include <kis_tool_freehand_helper.h>

+ Inheritance diagram for KisToolFreehandHelper:

Classes

struct  Private
 

Signals

void requestExplicitUpdateOutline ()
 

Public Member Functions

void cursorMoved (const QPointF &cursorPos)
 
void endPaint ()
 
void initPaint (KoPointerEvent *event, const QPointF &pixelCoords, KisImageWSP image, KisNodeSP currentNode, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
 
bool isRunning () const
 
 KisToolFreehandHelper (KisPaintingInformationBuilder *infoBuilder, KoCanvasResourceProvider *resourceManager, const KUndo2MagicString &transactionText=KUndo2MagicString(), KisSmoothingOptions *smoothingOptions=0)
 
void paintEvent (KoPointerEvent *event)
 
KisOptimizedBrushOutline paintOpOutline (const QPointF &savedCursorPos, const KoPointerEvent *event, const KisPaintOpSettingsSP globalSettings, KisPaintOpSettings::OutlineMode mode) const
 
void setSmoothness (KisSmoothingOptionsSP smoothingOptions)
 
KisSmoothingOptionsSP smoothingOptions () const
 
 ~KisToolFreehandHelper () override
 

Protected Member Functions

void cancelPaint ()
 
virtual void createPainters (QVector< KisFreehandStrokeInfo * > &strokeInfos, const KisDistanceInformation &startDist)
 
int elapsedStrokeTime () const
 
void initPaintImpl (qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
 
virtual void paintAt (const KisPaintInformation &pi)
 
void paintAt (int strokeInfoId, const KisPaintInformation &pi)
 
virtual void paintBezierCurve (const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)
 
void paintBezierCurve (int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)
 
virtual void paintLine (const KisPaintInformation &pi1, const KisPaintInformation &pi2)
 
void paintLine (int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2)
 
KoCanvasResourceProvider * resourceManager () const
 

Private Slots

void doAirbrushing ()
 
void finishStroke ()
 
void slotSmoothingTypeChanged ()
 
void stabilizerPollAndPaint ()
 

Private Member Functions

int computeAirbrushTimerInterval () const
 
qreal currentPhysicalZoom () const
 
qreal currentZoom () const
 
KisPaintInformation getStabilizedPaintInfo (const QQueue< KisPaintInformation > &queue, const KisPaintInformation &lastPaintInfo)
 
void paint (KisPaintInformation &info)
 
void paintBezierSegment (KisPaintInformation pi1, KisPaintInformation pi2, QPointF tangent1, QPointF tangent2)
 
void stabilizerEnd ()
 
void stabilizerStart (KisPaintInformation firstPaintInfo)
 
void updateCurrentBrushPosition (const QPointF &pixelPos)
 

Private Attributes

Private *const m_d
 

Detailed Description

Definition at line 31 of file kis_tool_freehand_helper.h.

Constructor & Destructor Documentation

◆ KisToolFreehandHelper()

KisToolFreehandHelper::KisToolFreehandHelper ( KisPaintingInformationBuilder * infoBuilder,
KoCanvasResourceProvider * resourceManager,
const KUndo2MagicString & transactionText = KUndo2MagicString(),
KisSmoothingOptions * smoothingOptions = 0 )

Definition at line 136 of file kis_tool_freehand_helper.cpp.

140 : m_d(new Private())
141{
143 m_d->infoBuilder = infoBuilder;
144 m_d->transactionText = transactionText;
147
150
151 m_d->strokeTimeoutTimer.setSingleShot(true);
152 connect(&m_d->strokeTimeoutTimer, SIGNAL(timeout()), SLOT(finishStroke()));
153 connect(&m_d->airbrushingTimer, SIGNAL(timeout()), SLOT(doAirbrushing()));
154 connect(&m_d->stabilizerPollTimer, SIGNAL(timeout()), SLOT(stabilizerPollAndPaint()));
155 connect(m_d->smoothingOptions.data(), SIGNAL(sigSmoothingTypeChanged()), SLOT(slotSmoothingTypeChanged()));
156
158 [this](const KisPaintInformation &pi1, const KisPaintInformation &pi2) {
159 paintLine(pi1, pi2);
160 });
162 [this]() {
164 });
165}
void setUpdateOutlineCallback(std::function< void()> requestUpdateOutline)
void setPaintLineCallback(std::function< void(const KisPaintInformation &, const KisPaintInformation &)> paintLine)
KisSmoothingOptionsSP smoothingOptions() const
KoCanvasResourceProvider * resourceManager() const
void paintLine(int strokeInfoId, const KisPaintInformation &pi1, const KisPaintInformation &pi2)
void requestExplicitUpdateOutline()
QSharedPointer< KisSmoothingOptions > KisSmoothingOptionsSP
KisPerStrokeRandomSourceSP fakeStrokeRandomSource
KoCanvasResourceProvider * resourceManager
KisStabilizerDelayedPaintHelper stabilizerDelayedPaintHelper
KisPaintingInformationBuilder * infoBuilder

References KisToolFreehandHelper::Private::airbrushingTimer, doAirbrushing(), KisToolFreehandHelper::Private::fakeDabRandomSource, KisToolFreehandHelper::Private::fakeStrokeRandomSource, finishStroke(), KisToolFreehandHelper::Private::infoBuilder, m_d, paintLine(), requestExplicitUpdateOutline(), KisToolFreehandHelper::Private::resourceManager, resourceManager(), KisStabilizerDelayedPaintHelper::setPaintLineCallback(), KisStabilizerDelayedPaintHelper::setUpdateOutlineCallback(), slotSmoothingTypeChanged(), KisToolFreehandHelper::Private::smoothingOptions, smoothingOptions(), KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, stabilizerPollAndPaint(), KisToolFreehandHelper::Private::stabilizerPollTimer, KisToolFreehandHelper::Private::strokeTimeoutTimer, and KisToolFreehandHelper::Private::transactionText.

◆ ~KisToolFreehandHelper()

KisToolFreehandHelper::~KisToolFreehandHelper ( )
override

Definition at line 167 of file kis_tool_freehand_helper.cpp.

168{
169 delete m_d;
170}

References m_d.

Member Function Documentation

◆ cancelPaint()

void KisToolFreehandHelper::cancelPaint ( )
protected

Definition at line 751 of file kis_tool_freehand_helper.cpp.

752{
753 if (!m_d->strokeId) return;
754
755 m_d->strokeTimeoutTimer.stop();
756
757 if (m_d->airbrushingTimer.isActive()) {
758 m_d->airbrushingTimer.stop();
759 }
760
763 }
764
765 if (m_d->stabilizerPollTimer.isActive()) {
766 m_d->stabilizerPollTimer.stop();
767 }
768
771 }
772
773 // see a comment in endPaint()
774 m_d->strokeInfos.clear();
775
777 m_d->strokeId.clear();
778
779}
virtual bool cancelStroke(KisStrokeId id)=0
QVector< KisFreehandStrokeInfo * > strokeInfos
KisAsynchronousStrokeUpdateHelper asyncUpdateHelper

References KisToolFreehandHelper::Private::airbrushingTimer, KisToolFreehandHelper::Private::asyncUpdateHelper, KisStabilizerDelayedPaintHelper::cancel(), KisStrokesFacade::cancelStroke(), KisAsynchronousStrokeUpdateHelper::cancelUpdateStream(), KisAsynchronousStrokeUpdateHelper::isActive(), m_d, KisStabilizerDelayedPaintHelper::running(), KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::stabilizerPollTimer, KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokeInfos, KisToolFreehandHelper::Private::strokesFacade, and KisToolFreehandHelper::Private::strokeTimeoutTimer.

◆ computeAirbrushTimerInterval()

int KisToolFreehandHelper::computeAirbrushTimerInterval ( ) const
private

Definition at line 1001 of file kis_tool_freehand_helper.cpp.

1002{
1003 qreal realInterval = m_d->resources->airbrushingInterval() * AIRBRUSH_INTERVAL_FACTOR;
1004 return qMax(1, qFloor(realInterval));
1005}
const qreal AIRBRUSH_INTERVAL_FACTOR

References AIRBRUSH_INTERVAL_FACTOR, KisResourcesSnapshot::airbrushingInterval(), m_d, and KisToolFreehandHelper::Private::resources.

◆ createPainters()

void KisToolFreehandHelper::createPainters ( QVector< KisFreehandStrokeInfo * > & strokeInfos,
const KisDistanceInformation & startDist )
protectedvirtual

Reimplemented in KisToolMultihandHelper.

Definition at line 1077 of file kis_tool_freehand_helper.cpp.

1079{
1080 strokeInfos << new KisFreehandStrokeInfo(startDist);
1081}

◆ currentPhysicalZoom()

qreal KisToolFreehandHelper::currentPhysicalZoom ( ) const
private

◆ currentZoom()

qreal KisToolFreehandHelper::currentZoom ( ) const
private

Definition at line 1007 of file kis_tool_freehand_helper.cpp.

1008{
1010}
@ EffectiveZoom
-Used only by painting tools for non-displaying purposes

References KoCanvasResource::EffectiveZoom, m_d, KoCanvasResourceProvider::resource(), and KisToolFreehandHelper::Private::resourceManager.

◆ cursorMoved()

void KisToolFreehandHelper::cursorMoved ( const QPointF & cursorPos)

◆ doAirbrushing

void KisToolFreehandHelper::doAirbrushing ( )
privateslot

Definition at line 977 of file kis_tool_freehand_helper.cpp.

978{
979 // Check that the stroke hasn't ended.
980 if (!m_d->strokeInfos.isEmpty()) {
981
982 // Add a new painting update at a point identical to the previous one, except for the time
983 // and speed information.
985 KisPaintInformation nextPaint(prevPaint.pos(),
986 prevPaint.pressure(),
987 prevPaint.xTilt(),
988 prevPaint.yTilt(),
989 prevPaint.rotation(),
990 prevPaint.tangentialPressure(),
991 prevPaint.perspective(),
993 0.0);
994 nextPaint.setCanvasRotation(prevPaint.canvasRotation());
995 nextPaint.setCanvasMirroredH(prevPaint.canvasMirroredH());
996 nextPaint.setCanvasMirroredV(prevPaint.canvasMirroredV());
997 paint(nextPaint);
998 }
999}
qreal perspective() const
reciprocal of distance on the perspective grid
const QPointF & pos() const
qreal xTilt() const
The tilt of the pen on the horizontal axis (from 0.0 to 1.0)
qreal tangentialPressure() const
tangential pressure (i.e., rate for an airbrush device)
qreal yTilt() const
The tilt of the pen on the vertical axis (from 0.0 to 1.0)
qreal rotation() const
rotation as given by the tablet event
qreal pressure() const
The pressure of the value (from 0.0 to 1.0)
void paint(KisPaintInformation &info)

References KisPaintInformation::canvasMirroredH(), KisPaintInformation::canvasMirroredV(), KisPaintInformation::canvasRotation(), elapsedStrokeTime(), m_d, paint(), KisPaintInformation::perspective(), KisPaintInformation::pos(), KisPaintInformation::pressure(), KisToolFreehandHelper::Private::previousPaintInformation, KisPaintInformation::rotation(), KisPaintInformation::setCanvasMirroredH(), KisPaintInformation::setCanvasMirroredV(), KisPaintInformation::setCanvasRotation(), KisToolFreehandHelper::Private::strokeInfos, KisPaintInformation::tangentialPressure(), KisPaintInformation::xTilt(), and KisPaintInformation::yTilt().

◆ elapsedStrokeTime()

int KisToolFreehandHelper::elapsedStrokeTime ( ) const
protected

Definition at line 781 of file kis_tool_freehand_helper.cpp.

782{
783 return m_d->strokeTime.elapsed();
784}

References m_d, and KisToolFreehandHelper::Private::strokeTime.

◆ endPaint()

void KisToolFreehandHelper::endPaint ( )

There might be some timer events still pending, so we should cancel them. Use this flag for the purpose. Please note that we are not in MT here, so no mutex is needed

Definition at line 713 of file kis_tool_freehand_helper.cpp.

714{
715 if (!m_d->hasPaintAtLeastOnce) {
717 } else if (m_d->smoothingOptions->smoothingType() != KisSmoothingOptions::NO_SMOOTHING) {
718 finishStroke();
719 }
720 m_d->strokeTimeoutTimer.stop();
721
722 if(m_d->airbrushingTimer.isActive()) {
723 m_d->airbrushingTimer.stop();
724 }
725
726 if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
728 }
729
732 }
733
740 m_d->strokeInfos.clear();
741
742 // last update to complete rendering if there is still something pending
745
747 m_d->strokeId.clear();
749}
virtual void endStroke(KisStrokeId id)=0
virtual void addJob(KisStrokeId id, KisStrokeJobData *data)=0
void paintAt(int strokeInfoId, const KisPaintInformation &pi)

References KisStrokesFacade::addJob(), KisToolFreehandHelper::Private::airbrushingTimer, KisToolFreehandHelper::Private::asyncUpdateHelper, KisStrokesFacade::endStroke(), KisAsynchronousStrokeUpdateHelper::endUpdateStream(), finishStroke(), KisToolFreehandHelper::Private::hasPaintAtLeastOnce, KisToolFreehandHelper::Private::infoBuilder, KisAsynchronousStrokeUpdateHelper::isActive(), m_d, KisSmoothingOptions::NO_SMOOTHING, paintAt(), KisToolFreehandHelper::Private::previousPaintInformation, KisPaintingInformationBuilder::reset(), KisToolFreehandHelper::Private::smoothingOptions, KisSmoothingOptions::STABILIZER, stabilizerEnd(), KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokeInfos, KisToolFreehandHelper::Private::strokesFacade, and KisToolFreehandHelper::Private::strokeTimeoutTimer.

◆ finishStroke

void KisToolFreehandHelper::finishStroke ( )
privateslot

◆ getStabilizedPaintInfo()

KisPaintInformation KisToolFreehandHelper::getStabilizedPaintInfo ( const QQueue< KisPaintInformation > & queue,
const KisPaintInformation & lastPaintInfo )
private

The first point is going to be overridden by lastPaintInfo, skip it.

Definition at line 815 of file kis_tool_freehand_helper.cpp.

817{
818 KisPaintInformation result(lastPaintInfo.pos(),
819 lastPaintInfo.pressure(),
820 lastPaintInfo.xTilt(),
821 lastPaintInfo.yTilt(),
822 lastPaintInfo.rotation(),
823 lastPaintInfo.tangentialPressure(),
824 lastPaintInfo.perspective(),
826 lastPaintInfo.drawingSpeed());
827
828 result.setCanvasRotation(lastPaintInfo.canvasRotation());
829 result.setCanvasMirroredH(lastPaintInfo.canvasMirroredH());
830 result.setCanvasMirroredV(lastPaintInfo.canvasMirroredV());
831
832 if (queue.size() > 1) {
833 QQueue<KisPaintInformation>::const_iterator it = queue.constBegin();
834 QQueue<KisPaintInformation>::const_iterator end = queue.constEnd();
835
839 it++;
840 int i = 2;
841
842 if (m_d->smoothingOptions->stabilizeSensors()) {
843 while (it != end) {
844 qreal k = qreal(i - 1) / i; // coeff for uniform averaging
845 result.KisPaintInformation::mixOtherWithoutTime(k, *it);
846 it++;
847 i++;
848 }
849 } else{
850 while (it != end) {
851 qreal k = qreal(i - 1) / i; // coeff for uniform averaging
852 result.KisPaintInformation::mixOtherOnlyPosition(k, *it);
853 it++;
854 i++;
855 }
856 }
857 }
858
859 return result;
860}

References KisPaintInformation::canvasMirroredH(), KisPaintInformation::canvasMirroredV(), KisPaintInformation::canvasRotation(), KisPaintInformation::drawingSpeed(), elapsedStrokeTime(), m_d, KisPaintInformation::perspective(), KisPaintInformation::pos(), KisPaintInformation::pressure(), KisPaintInformation::rotation(), KisPaintInformation::setCanvasMirroredH(), KisPaintInformation::setCanvasMirroredV(), KisPaintInformation::setCanvasRotation(), KisToolFreehandHelper::Private::smoothingOptions, KisPaintInformation::tangentialPressure(), KisPaintInformation::xTilt(), and KisPaintInformation::yTilt().

◆ initPaint()

void KisToolFreehandHelper::initPaint ( KoPointerEvent * event,
const QPointF & pixelCoords,
KisImageWSP image,
KisNodeSP currentNode,
KisStrokesFacade * strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0 )
Parameters
eventThe event
pixelCoordsThe position of the KoPointerEvent, in pixel coordinates.
resourceManagerThe canvas resource manager
imageThe image
currentNodeThe current node
strokesFacadeThe strokes facade
overrideNodeThe override node
boundsThe bounds

Definition at line 258 of file kis_tool_freehand_helper.cpp.

264{
265 // When using touch drawing, we only get coordinates when the finger is
266 // actually pressed down, never when the finger is in the air. That means we
267 // have to clear the distance and speed information in this case, otherwise
268 // strokes start in a state depending on where the last stroke left off,
269 // which is useless. Resetting these at least makes it predictable. This
270 // can't sensibly be done in endPaint since there can be stylus or mouse
271 // events in the meantime, whose distance and speed is also unrelated.
272 if (event->isTouchEvent()) {
273 m_d->lastCursorPos.reset(pixelCoords);
275 }
276
277 QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(pixelCoords, currentZoom());
278 m_d->strokeTime.start();
281 qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, pixelCoords, 0.0);
282
283 initPaintImpl(startAngle,
284 pi,
286 image,
287 currentNode,
288 strokesFacade,
289 overrideNode,
290 bounds);
291}
KisPaintInformation startStroke(KoPointerEvent *event, int timeElapsed, const KoCanvasResourceProvider *manager)
void initPaintImpl(qreal startAngle, const KisPaintInformation &pi, KoCanvasResourceProvider *resourceManager, KisImageWSP image, KisNodeSP node, KisStrokesFacade *strokesFacade, KisNodeSP overrideNode=0, KisDefaultBoundsBaseSP bounds=0)
bool isTouchEvent() const
#define bounds(x, a, b)
qreal directionBetweenPoints(const QPointF &p1, const QPointF &p2, qreal defaultAngle)

References bounds, currentZoom(), KisAlgebra2D::directionBetweenPoints(), elapsedStrokeTime(), KisToolFreehandHelper::Private::infoBuilder, initPaintImpl(), KoPointerEvent::isTouchEvent(), KisToolFreehandHelper::Private::lastCursorPos, m_d, KisPaintOpUtils::PositionHistory::pushThroughHistory(), KisPaintingInformationBuilder::reset(), KisPaintOpUtils::PositionHistory::reset(), KisToolFreehandHelper::Private::resourceManager, KisPaintingInformationBuilder::startStroke(), and KisToolFreehandHelper::Private::strokeTime.

◆ initPaintImpl()

void KisToolFreehandHelper::initPaintImpl ( qreal startAngle,
const KisPaintInformation & pi,
KoCanvasResourceProvider * resourceManager,
KisImageWSP image,
KisNodeSP node,
KisStrokesFacade * strokesFacade,
KisNodeSP overrideNode = 0,
KisDefaultBoundsBaseSP bounds = 0 )
protected

Definition at line 298 of file kis_tool_freehand_helper.cpp.

306{
307 m_d->strokesFacade = strokesFacade;
308
309 m_d->haveTangent = false;
310 m_d->previousTangent = QPointF();
311
312 m_d->hasPaintAtLeastOnce = false;
313
315
317 currentNode,
319 bounds);
320 if(overrideNode) {
321 m_d->resources->setCurrentNode(overrideNode);
322 }
323
324 const bool airbrushing = m_d->resources->needsAirbrushing();
325 const bool useSpacingUpdates = m_d->resources->needsSpacingUpdates();
326
328 startAngle,
329 useSpacingUpdates ? SPACING_UPDATE_INTERVAL : LONG_TIME,
330 airbrushing ? TIMING_UPDATE_INTERVAL : LONG_TIME,
331 0);
332 KisDistanceInformation startDist = startDistInfo.makeDistInfo();
333
335 startDist);
336
337 KisStrokeStrategy *stroke =
343
345
346 m_d->history.clear();
347 m_d->distanceHistory.clear();
348 m_d->hasLastDrawnPixel = false;
349 m_d->hasTentativePixel = false;
350
351 if (airbrushing) {
353 m_d->airbrushingTimer.start();
356 }
357
358 if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
360 }
361
362 // If airbrushing, paint an initial dab immediately. This is a workaround for an issue where
363 // some paintops (Dyna, Particle, Sketch) might never initialize their spacing/timing
364 // information until paintAt is called.
365 if (airbrushing) {
366 paintAt(pi);
367 }
368}
void startUpdateStream(KisStrokesFacade *strokesFacade, KisStrokeId strokeId)
The KisResourcesSnapshot class takes a snapshot of the various resources like colors and settings use...
void setCurrentNode(KisNodeSP node)
virtual KisStrokeId startStroke(KisStrokeStrategy *strokeStrategy)=0
void stabilizerStart(KisPaintInformation firstPaintInfo)
virtual void createPainters(QVector< KisFreehandStrokeInfo * > &strokeInfos, const KisDistanceInformation &startDist)
const qreal LONG_TIME
const qreal TIMING_UPDATE_INTERVAL
const qreal SPACING_UPDATE_INTERVAL

References KisToolFreehandHelper::Private::airbrushingTimer, KisToolFreehandHelper::Private::asyncUpdateHelper, bounds, computeAirbrushTimerInterval(), createPainters(), KisToolFreehandHelper::Private::distanceHistory, KisToolFreehandHelper::Private::hasLastDrawnPixel, KisToolFreehandHelper::Private::hasPaintAtLeastOnce, KisToolFreehandHelper::Private::hasTentativePixel, KisToolFreehandHelper::Private::haveTangent, KisToolFreehandHelper::Private::history, LONG_TIME, m_d, KisResourcesSnapshot::needsAirbrushing(), KisResourcesSnapshot::needsSpacingUpdates(), paintAt(), KisPaintInformation::pos(), KisResourcesSnapshot::presetNeedsAsynchronousUpdates(), KisToolFreehandHelper::Private::previousPaintInformation, KisToolFreehandHelper::Private::previousTangent, resourceManager(), KisToolFreehandHelper::Private::resources, KisResourcesSnapshot::setCurrentNode(), KisToolFreehandHelper::Private::smoothingOptions, SPACING_UPDATE_INTERVAL, KisSmoothingOptions::STABILIZER, stabilizerStart(), KisStrokesFacade::startStroke(), KisAsynchronousStrokeUpdateHelper::startUpdateStream(), KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokeInfos, KisToolFreehandHelper::Private::strokesFacade, FreehandStrokeStrategy::SupportsContinuedInterstrokeData, FreehandStrokeStrategy::SupportsTimedMergeId, TIMING_UPDATE_INTERVAL, and KisToolFreehandHelper::Private::transactionText.

◆ isRunning()

bool KisToolFreehandHelper::isRunning ( ) const

Definition at line 293 of file kis_tool_freehand_helper.cpp.

294{
295 return bool(m_d->strokeId);
296}

References m_d, and KisToolFreehandHelper::Private::strokeId.

◆ paint()

void KisToolFreehandHelper::paint ( KisPaintInformation & info)
private

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 https://w.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 controlling the end of the stroke

4) The formula is a little bit different: 'Distance' parameter stands for $3 \Sigma$

Definition at line 502 of file kis_tool_freehand_helper.cpp.

503{
524 KisPaintInformation lastUsedPaintInformation;
525
527 && (m_d->smoothingOptions->smoothnessDistanceMin() > 0.0
528 || m_d->smoothingOptions->smoothnessDistanceMax() > 0.0)) {
529
530 { // initialize current distance
531 QPointF prevPos;
532
533 if (!m_d->history.isEmpty()) {
534 const KisPaintInformation &prevPi = m_d->history.last();
535 prevPos = prevPi.pos();
536 } else {
537 prevPos = m_d->previousPaintInformation.pos();
538 }
539
540 qreal currentDistance = QVector2D(info.pos() - prevPos).length();
541 m_d->distanceHistory.append(currentDistance);
542 }
543
544 m_d->history.append(info);
545
546 qreal x = 0.0;
547 qreal y = 0.0;
548
549 if (m_d->history.size() > 3) {
550 const qreal sigma = m_d->effectiveSmoothnessDistance(m_d->previousPaintInformation.drawingSpeed()) / 3.0; // '3.0' for (3 * sigma) range
551
552 qreal gaussianWeight = 1 / (sqrt(2 * M_PI) * sigma);
553 qreal gaussianWeight2 = sigma * sigma;
554 qreal distanceSum = 0.0;
555 qreal scaleSum = 0.0;
556 qreal pressure = 0.0;
557 qreal baseRate = 0.0;
558
559 Q_ASSERT(m_d->history.size() == m_d->distanceHistory.size());
560
561 for (int i = m_d->history.size() - 1; i >= 0; i--) {
562 qreal rate = 0.0;
563
564 const KisPaintInformation nextInfo = m_d->history.at(i);
565 double distance = m_d->distanceHistory.at(i);
566 Q_ASSERT(distance >= 0.0);
567
568 qreal pressureGrad = 0.0;
569 if (i < m_d->history.size() - 1) {
570 pressureGrad = nextInfo.pressure() - m_d->history.at(i + 1).pressure();
571
572 const qreal tailAggressiveness = 40.0 * m_d->smoothingOptions->tailAggressiveness();
573
574 if (pressureGrad > 0.0 ) {
575 pressureGrad *= tailAggressiveness * (1.0 - nextInfo.pressure());
576 distance += pressureGrad * 3.0 * sigma; // (3 * sigma) --- holds > 90% of the region
577 }
578 }
579
580 if (gaussianWeight2 != 0.0) {
581 distanceSum += distance;
582 rate = gaussianWeight * exp(-distanceSum * distanceSum / (2 * gaussianWeight2));
583 }
584
585 if (m_d->history.size() - i == 1) {
586 baseRate = rate;
587 } else if (baseRate / rate > 100) {
588 break;
589 }
590
591 scaleSum += rate;
592 x += rate * nextInfo.pos().x();
593 y += rate * nextInfo.pos().y();
594
595 if (m_d->smoothingOptions->smoothPressure()) {
596 pressure += rate * nextInfo.pressure();
597 }
598 }
599
600 if (scaleSum != 0.0) {
601 x /= scaleSum;
602 y /= scaleSum;
603
604 if (m_d->smoothingOptions->smoothPressure()) {
605 pressure /= scaleSum;
606 }
607 }
608
609 if ((x != 0.0 && y != 0.0) || (x == info.pos().x() && y == info.pos().y())) {
610 info.setPos(QPointF(x, y));
611 if (m_d->smoothingOptions->smoothPressure()) {
612 info.setPressure(pressure);
613 }
614 m_d->history.last() = info;
615 }
616 }
617 }
618
621 {
622 // Now paint between the coordinates, using the bezier curve interpolation
623 if (!m_d->haveTangent) {
624 m_d->haveTangent = true;
626 (info.pos() - m_d->previousPaintInformation.pos()) /
627 qMax(qreal(1.0), info.currentTime() - m_d->previousPaintInformation.currentTime());
628 } else {
629 QPointF newTangent = (info.pos() - m_d->olderPaintInformation.pos()) /
630 qMax(qreal(1.0), info.currentTime() - m_d->olderPaintInformation.currentTime());
631
632 if (newTangent.isNull() || m_d->previousTangent.isNull())
633 {
635 } else {
637 m_d->previousTangent, newTangent);
638 }
639
640 m_d->previousTangent = newTangent;
641 }
643
644 // Enable stroke timeout only when not airbrushing.
645 if (!m_d->airbrushingTimer.isActive()) {
646 m_d->strokeTimeoutTimer.start(100);
647 }
648 }
649
650 else if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::NO_SMOOTHING){
652 }
653
655 QPoint currentPixelPos = Private::toPixelPos(info.pos());
656
657 // First pixel is always perfect.
658 if (!m_d->hasLastDrawnPixel) {
659 Private::setPaintInfoPixelPos(info, currentPixelPos);
660 paintLine(info, info);
661 m_d->lastDrawnPixel = currentPixelPos;
662 m_d->hasLastDrawnPixel = true;
663 m_d->hasTentativePixel = false;
665
666 } else {
667 // Check if we need to flush a currently stashed tentative pixel.
668 bool lastTentative = m_d->isTentativePixel(currentPixelPos);
669 if (m_d->hasTentativePixel && !lastTentative) {
672 m_d->hasTentativePixel = false;
675 lastTentative = m_d->isTentativePixel(currentPixelPos);
676 }
677
678 // If this pixel is too close to the previously drawn one, we mark
679 // it as tentative and wait for further movements to make sure we
680 // don't put it down in a place that generates a jag. If there is
681 // already a tentative pixel, it will be clobbered.
682 if (lastTentative) {
683 m_d->hasTentativePixel = true;
684 m_d->tentativePixel = currentPixelPos;
686 } else {
687 Private::setPaintInfoPixelPos(info, currentPixelPos);
689 m_d->lastDrawnPixel = currentPixelPos;
691 // Don't need to fiddle with hasLastTentativePixel here, it can
692 // only ever be false here due to the previous condition.
693 }
694 }
695 }
696
697 if (m_d->smoothingOptions->smoothingType() == KisSmoothingOptions::STABILIZER) {
700 // Paint here so we don't have to rely on the timer
701 // This is just a tricky source for a relatively stable 7ms "timer"
703 }
704 } else {
706 }
707
708 if(m_d->airbrushingTimer.isActive()) {
709 m_d->airbrushingTimer.start();
710 }
711}
qreal distance(const QPointF &p1, const QPointF &p2)
void setPos(const QPointF &p)
void setPressure(qreal p)
Set the pressure.
void addEvent(const KisPaintInformation &pi)
#define M_PI
Definition kis_global.h:111
KisStabilizedEventsSampler stabilizedSampler
static void setPaintInfoPixelPos(KisPaintInformation &info, const QPoint &pixelPos)
bool isTentativePixel(const QPoint &currentPixelPos) const
static QPoint toPixelPos(const QPointF &pos)
qreal effectiveSmoothnessDistance(qreal speed) const

References KisStabilizedEventsSampler::addEvent(), KisToolFreehandHelper::Private::airbrushingTimer, KisPaintInformation::currentTime(), distance(), KisToolFreehandHelper::Private::distanceHistory, KisPaintInformation::drawingSpeed(), KisToolFreehandHelper::Private::effectiveSmoothnessDistance(), KisToolFreehandHelper::Private::hasLastDrawnPixel, KisToolFreehandHelper::Private::hasTentativePixel, KisToolFreehandHelper::Private::haveTangent, KisToolFreehandHelper::Private::history, KisToolFreehandHelper::Private::isTentativePixel(), KisToolFreehandHelper::Private::lastDrawnPixel, m_d, M_PI, KisSmoothingOptions::NO_SMOOTHING, KisToolFreehandHelper::Private::olderPaintInformation, paintBezierSegment(), paintLine(), KisStabilizerDelayedPaintHelper::paintSome(), KisSmoothingOptions::PIXEL_PERFECT, KisPaintInformation::pos(), KisPaintInformation::pressure(), KisToolFreehandHelper::Private::previousPaintInformation, KisToolFreehandHelper::Private::previousTangent, KisStabilizerDelayedPaintHelper::running(), KisToolFreehandHelper::Private::setPaintInfoPixelPos(), KisPaintInformation::setPos(), KisPaintInformation::setPressure(), KisSmoothingOptions::SIMPLE_SMOOTHING, KisToolFreehandHelper::Private::smoothingOptions, KisToolFreehandHelper::Private::stabilizedSampler, KisSmoothingOptions::STABILIZER, KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::strokeTimeoutTimer, KisToolFreehandHelper::Private::tentativePaintInformation, KisToolFreehandHelper::Private::tentativePixel, KisToolFreehandHelper::Private::toPixelPos(), and KisSmoothingOptions::WEIGHTED_SMOOTHING.

◆ paintAt() [1/2]

void KisToolFreehandHelper::paintAt ( const KisPaintInformation & pi)
protectedvirtual

Reimplemented in KisToolMultihandHelper, and KisToolMultihandHelper.

Definition at line 1083 of file kis_tool_freehand_helper.cpp.

1084{
1085 paintAt(0, pi);
1086}

References paintAt().

◆ paintAt() [2/2]

◆ paintBezierCurve() [1/2]

void KisToolFreehandHelper::paintBezierCurve ( const KisPaintInformation & pi1,
const QPointF & control1,
const QPointF & control2,
const KisPaintInformation & pi2 )
protectedvirtual

Reimplemented in KisToolMultihandHelper, and KisToolMultihandHelper.

Definition at line 1094 of file kis_tool_freehand_helper.cpp.

1098{
1099 paintBezierCurve(0, pi1, control1, control2, pi2);
1100}
void paintBezierCurve(int strokeInfoId, const KisPaintInformation &pi1, const QPointF &control1, const QPointF &control2, const KisPaintInformation &pi2)

References paintBezierCurve().

◆ paintBezierCurve() [2/2]

void KisToolFreehandHelper::paintBezierCurve ( int strokeInfoId,
const KisPaintInformation & pi1,
const QPointF & control1,
const QPointF & control2,
const KisPaintInformation & pi2 )
protected

Definition at line 1038 of file kis_tool_freehand_helper.cpp.

1043{
1045
1046#ifdef DEBUG_BEZIER_CURVES
1049
1050 tpi1 = pi1;
1051 tpi2 = pi2;
1052
1053 tpi1.setPressure(0.3);
1054 tpi2.setPressure(0.3);
1055
1056 paintLine(tpi1, tpi2);
1057
1058 tpi1.setPressure(0.6);
1059 tpi2.setPressure(0.3);
1060
1061 tpi1.setPos(pi1.pos());
1062 tpi2.setPos(control1);
1063 paintLine(tpi1, tpi2);
1064
1065 tpi1.setPos(pi2.pos());
1066 tpi2.setPos(control2);
1067 paintLine(tpi1, tpi2);
1068#endif
1069
1070 m_d->hasPaintAtLeastOnce = true;
1072 new FreehandStrokeStrategy::Data(strokeInfoId,
1073 pi1, control1, control2, pi2));
1074
1075}

References KisStrokesFacade::addJob(), KisToolFreehandHelper::Private::hasPaintAtLeastOnce, m_d, paintLine(), KisPaintInformation::pos(), KisPaintInformation::setPos(), KisPaintInformation::setPressure(), KisToolFreehandHelper::Private::strokeId, KisToolFreehandHelper::Private::strokesFacade, and updateCurrentBrushPosition().

◆ paintBezierSegment()

void KisToolFreehandHelper::paintBezierSegment ( KisPaintInformation pi1,
KisPaintInformation pi2,
QPointF tangent1,
QPointF tangent2 )
private

Definition at line 385 of file kis_tool_freehand_helper.cpp.

387{
388 if (tangent1.isNull() || tangent2.isNull()) return;
389
390 const qreal maxSanePoint = 1e6;
391
392 QPointF controlTarget1;
393 QPointF controlTarget2;
394
395 // Shows the direction in which control points go
396 QPointF controlDirection1 = pi1.pos() + tangent1;
397 QPointF controlDirection2 = pi2.pos() - tangent2;
398
399 // Lines in the direction of the control points
400 QLineF line1(pi1.pos(), controlDirection1);
401 QLineF line2(pi2.pos(), controlDirection2);
402
403 // Lines to check whether the control points lay on the opposite
404 // side of the line
405 QLineF line3(controlDirection1, controlDirection2);
406 QLineF line4(pi1.pos(), pi2.pos());
407
408 QPointF intersection;
409 if (line3.intersects(line4, &intersection) == QLineF::BoundedIntersection) {
410 qreal controlLength = line4.length() / 2;
411
412 line1.setLength(controlLength);
413 line2.setLength(controlLength);
414
415 controlTarget1 = line1.p2();
416 controlTarget2 = line2.p2();
417 } else {
418 QLineF::IntersectType type = line1.intersects(line2, &intersection);
419
420 if (type == QLineF::NoIntersection ||
421 intersection.manhattanLength() > maxSanePoint) {
422
423 intersection = 0.5 * (pi1.pos() + pi2.pos());
424// dbgKrita << "WARNING: there is no intersection point "
425// << "in the basic smoothing algorithms";
426 }
427
428 controlTarget1 = intersection;
429 controlTarget2 = intersection;
430 }
431
432 // shows how near to the controlTarget the value raises
433 qreal coeff = 0.8;
434
435 qreal velocity1 = QLineF(QPointF(), tangent1).length();
436 qreal velocity2 = QLineF(QPointF(), tangent2).length();
437
438 if (velocity1 == 0.0 || velocity2 == 0.0) {
439 velocity1 = 1e-6;
440 velocity2 = 1e-6;
441 warnKrita << "WARNING: Basic Smoothing: Velocity is Zero! Please report a bug:" << ppVar(velocity1) << ppVar(velocity2);
442 }
443
444 qreal similarity = qMin(velocity1/velocity2, velocity2/velocity1);
445
446 // the controls should not differ more than 50%
447 similarity = qMax(similarity, qreal(0.5));
448
449 // when the controls are symmetric, their size should be smaller
450 // to avoid corner-like curves
451 coeff *= 1 - qMax(qreal(0.0), similarity - qreal(0.8));
452
453 Q_ASSERT(coeff > 0);
454
455
456 QPointF control1;
457 QPointF control2;
458
459 if (velocity1 > velocity2) {
460 control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
461 coeff *= similarity;
462 control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
463 } else {
464 control2 = pi2.pos() * (1.0 - coeff) + coeff * controlTarget2;
465 coeff *= similarity;
466 control1 = pi1.pos() * (1.0 - coeff) + coeff * controlTarget1;
467 }
468
470 control1,
471 control2,
472 pi2);
473}
#define warnKrita
Definition kis_debug.h:94
#define ppVar(var)
Definition kis_debug.h:163

References paintBezierCurve(), KisPaintInformation::pos(), ppVar, and warnKrita.

◆ paintEvent()

◆ paintLine() [1/2]

void KisToolFreehandHelper::paintLine ( const KisPaintInformation & pi1,
const KisPaintInformation & pi2 )
protectedvirtual

Reimplemented in KisToolMultihandHelper, and KisToolMultihandHelper.

Definition at line 1088 of file kis_tool_freehand_helper.cpp.

1090{
1091 paintLine(0, pi1, pi2);
1092}

References paintLine().

◆ paintLine() [2/2]

◆ paintOpOutline()

KisOptimizedBrushOutline KisToolFreehandHelper::paintOpOutline ( const QPointF & savedCursorPos,
const KoPointerEvent * event,
const KisPaintOpSettingsSP globalSettings,
KisPaintOpSettings::OutlineMode mode ) const

When LoD mode is active it may happen that the helper has already started a stroke, but it painted noting, because all the work is being calculated by the scaled-down LodN stroke. So at first we try to fetch the data from the lodN stroke ("buddy") and then check if there is at least something has been painted with this distance information object.

Tiny hack alert: here we fetch the distance information directly from the LodN stroke. Ideally, we should upscale its data, but here we just override it with our local copy of the coordinates.

Definition at line 182 of file kis_tool_freehand_helper.cpp.

186{
187 KisPaintOpSettingsSP settings = globalSettings;
188 QPointF prevPoint = m_d->lastCursorPos.pushThroughHistory(savedCursorPos, currentZoom());
189 qreal startAngle = KisAlgebra2D::directionBetweenPoints(prevPoint, savedCursorPos, 0);
190 KisDistanceInformation distanceInfo(prevPoint, startAngle);
191
193
194 if (!m_d->strokeInfos.isEmpty()) {
195 settings = m_d->resources->currentPaintOpPreset()->settings();
199 } else {
201 }
202
212 KisDistanceInformation *buddyDistance =
213 m_d->strokeInfos.first()->buddyDragDistance();
214
215 if (buddyDistance) {
222 distanceInfo = *buddyDistance;
223 distanceInfo.overrideLastValues(prevPoint, startAngle);
224
225 } else if (m_d->strokeInfos.first()->dragDistance->isStarted()) {
226 distanceInfo = *m_d->strokeInfos.first()->dragDistance;
227 }
228 } else {
229 info = m_d->infoBuilder->hover(savedCursorPos, event, !m_d->strokeInfos.isEmpty());
230 }
231
233 info.registerDistanceInformation(&distanceInfo);
234
237
238 KisOptimizedBrushOutline outline = settings->brushOutline(info, mode, currentPhysicalZoom());
239
240 if (m_d->resources &&
242 m_d->smoothingOptions->useDelayDistance()) {
243
244 const qreal R = m_d->smoothingOptions->delayDistance() /
246
247 outline.addEllipse(info.pos(), R, R);
248 }
249
250 return outline;
251}
Eigen::Matrix< double, 4, 2 > R
void addEllipse(const QPointF &center, qreal rx, qreal ry)
void setRandomSource(KisRandomSourceSP value)
DistanceInformationRegistrar registerDistanceInformation(KisDistanceInformation *distance)
void setPerStrokeRandomSource(KisPerStrokeRandomSourceSP value)
KisPaintInformation hover(const QPointF &imagePoint, const KoPointerEvent *event, bool isStrokeStarted)
KisPaintOpPresetSP currentPaintOpPreset() const
void overrideLastValues(const QPointF &lastPosition, qreal lastAngle)

References KisOptimizedBrushOutline::addEllipse(), KisResourcesSnapshot::currentPaintOpPreset(), currentPhysicalZoom(), currentZoom(), KisAlgebra2D::directionBetweenPoints(), KisResourcesSnapshot::effectiveZoom(), KisToolFreehandHelper::Private::fakeDabRandomSource, KisToolFreehandHelper::Private::fakeStrokeRandomSource, KisStabilizerDelayedPaintHelper::hasLastPaintInformation(), KisPaintingInformationBuilder::hover(), KisToolFreehandHelper::Private::infoBuilder, KisToolFreehandHelper::Private::lastCursorPos, KisStabilizerDelayedPaintHelper::lastPaintInformation(), m_d, KisDistanceInformation::overrideLastValues(), KisPaintInformation::pos(), KisToolFreehandHelper::Private::previousPaintInformation, KisPaintOpUtils::PositionHistory::pushThroughHistory(), R, KisPaintInformation::registerDistanceInformation(), KisToolFreehandHelper::Private::resources, KisStabilizerDelayedPaintHelper::running(), KisPaintInformation::setPerStrokeRandomSource(), KisPaintInformation::setRandomSource(), KisToolFreehandHelper::Private::smoothingOptions, KisSmoothingOptions::STABILIZER, KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, and KisToolFreehandHelper::Private::strokeInfos.

◆ requestExplicitUpdateOutline

void KisToolFreehandHelper::requestExplicitUpdateOutline ( )
signal

The signal is emitted when the outline should be updated explicitly by the tool. Used by Stabilizer option, because it paints on internal timer events instead of the on every paint() event

◆ resourceManager()

KoCanvasResourceProvider * KisToolFreehandHelper::resourceManager ( ) const
protected

Definition at line 370 of file kis_tool_freehand_helper.cpp.

371{
372 return m_d->resourceManager;
373}

References m_d, and KisToolFreehandHelper::Private::resourceManager.

◆ setSmoothness()

void KisToolFreehandHelper::setSmoothness ( KisSmoothingOptionsSP smoothingOptions)

◆ slotSmoothingTypeChanged

◆ smoothingOptions()

KisSmoothingOptionsSP KisToolFreehandHelper::smoothingOptions ( ) const

Definition at line 177 of file kis_tool_freehand_helper.cpp.

178{
179 return m_d->smoothingOptions;
180}

References m_d, and KisToolFreehandHelper::Private::smoothingOptions.

◆ stabilizerEnd()

void KisToolFreehandHelper::stabilizerEnd ( )
private

◆ stabilizerPollAndPaint

void KisToolFreehandHelper::stabilizerPollAndPaint ( )
privateslot

Definition at line 862 of file kis_tool_freehand_helper.cpp.

863{
866 std::tie(it, end) = m_d->stabilizedSampler.range();
867 QVector<KisPaintInformation> delayedPaintTodoItems;
868
869 for (; it != end; ++it) {
870 KisPaintInformation sampledInfo = *it;
871
872 bool canPaint = true;
873
874 if (m_d->smoothingOptions->useDelayDistance()) {
875 const qreal R = m_d->smoothingOptions->delayDistance() /
877
878 QPointF diff = sampledInfo.pos() - m_d->previousPaintInformation.pos();
879 qreal dx = sqrt(pow2(diff.x()) + pow2(diff.y()));
880
881 if (!(dx > R)) {
883 sampledInfo.setPos(m_d->previousPaintInformation.pos());
884 }
885 else {
886 canPaint = false;
887 }
888 }
889 }
890
891 if (canPaint) {
893
895 delayedPaintTodoItems.append(newInfo);
896 } else {
898 }
899 m_d->previousPaintInformation = newInfo;
900
901 // Push the new entry through the queue
902 m_d->stabilizerDeque.dequeue();
903 m_d->stabilizerDeque.enqueue(sampledInfo);
904 } else if (m_d->stabilizerDeque.head().pos() != m_d->previousPaintInformation.pos()) {
905 QQueue<KisPaintInformation>::iterator it = m_d->stabilizerDeque.begin();
906 QQueue<KisPaintInformation>::iterator end = m_d->stabilizerDeque.end();
907
908 while (it != end) {
910 ++it;
911 }
912 }
913 }
914
916
918 m_d->stabilizerDelayedPaintHelper.update(delayedPaintTodoItems);
919 } else {
921 }
922}
std::pair< iterator, iterator > range() const
void update(const QVector< KisPaintInformation > &newPaintInfos)
KisPaintInformation getStabilizedPaintInfo(const QQueue< KisPaintInformation > &queue, const KisPaintInformation &lastPaintInfo)
T pow2(const T &x)
Definition kis_global.h:166

References KisStabilizedEventsSampler::clear(), KisResourcesSnapshot::effectiveZoom(), getStabilizedPaintInfo(), m_d, KisResourcesSnapshot::needsAirbrushing(), paintLine(), KisPaintInformation::pos(), pow2(), KisToolFreehandHelper::Private::previousPaintInformation, R, KisStabilizedEventsSampler::range(), requestExplicitUpdateOutline(), KisToolFreehandHelper::Private::resources, KisStabilizerDelayedPaintHelper::running(), KisPaintInformation::setPos(), KisToolFreehandHelper::Private::smoothingOptions, KisToolFreehandHelper::Private::stabilizedSampler, KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::stabilizerDeque, and KisStabilizerDelayedPaintHelper::update().

◆ stabilizerStart()

void KisToolFreehandHelper::stabilizerStart ( KisPaintInformation firstPaintInfo)
private

Definition at line 786 of file kis_tool_freehand_helper.cpp.

787{
788 m_d->usingStabilizer = true;
789 // FIXME: Ugly hack, this is no a "distance" in any way
790 int sampleSize = qRound(m_d->effectiveSmoothnessDistance(firstPaintInfo.drawingSpeed()));
791 sampleSize = qMax(3, sampleSize);
792
793 // Fill the deque with the current value repeated until filling the sample
794 m_d->stabilizerDeque.clear();
795 for (int i = sampleSize; i > 0; i--) {
796 m_d->stabilizerDeque.enqueue(firstPaintInfo);
797 }
798
799 // Poll and draw regularly
800 KisConfig cfg(true);
801 int stabilizerSampleSize = cfg.stabilizerSampleSize();
802 m_d->stabilizerPollTimer.setInterval(stabilizerSampleSize);
803 m_d->stabilizerPollTimer.start();
804
805 bool delayedPaintEnabled = cfg.stabilizerDelayedPaint();
806 if (delayedPaintEnabled) {
807 m_d->stabilizerDelayedPaintHelper.start(firstPaintInfo);
808 }
809
811 m_d->stabilizedSampler.addEvent(firstPaintInfo);
812}
void start(const KisPaintInformation &firstPaintInfo)

References KisStabilizedEventsSampler::addEvent(), KisStabilizedEventsSampler::clear(), KisPaintInformation::drawingSpeed(), KisToolFreehandHelper::Private::effectiveSmoothnessDistance(), m_d, KisToolFreehandHelper::Private::stabilizedSampler, KisConfig::stabilizerDelayedPaint(), KisToolFreehandHelper::Private::stabilizerDelayedPaintHelper, KisToolFreehandHelper::Private::stabilizerDeque, KisToolFreehandHelper::Private::stabilizerPollTimer, KisConfig::stabilizerSampleSize(), KisStabilizerDelayedPaintHelper::start(), and KisToolFreehandHelper::Private::usingStabilizer.

◆ updateCurrentBrushPosition()

void KisToolFreehandHelper::updateCurrentBrushPosition ( const QPointF & pixelPos)
private

Publishes the current brush position (in image pixel coordinates) to the canvas resource provider, so scripts can follow where the brush actually is during a stroke (including stabilization delay).

Definition at line 375 of file kis_tool_freehand_helper.cpp.

376{
377 // Store on the flake-level resource provider (the only provider this
378 // helper holds), keyed by a Krita-specific resource id.
379 if (KoCanvasResourceProvider *manager = resourceManager()) {
380 manager->setResource(KoCanvasResource::CurrentBrushPosition,
381 QVariant::fromValue(pixelPos));
382 }
383}

References KoCanvasResource::CurrentBrushPosition, resourceManager(), and KoCanvasResourceProvider::setResource().

Member Data Documentation

◆ m_d

Private* const KisToolFreehandHelper::m_d
private

Definition at line 160 of file kis_tool_freehand_helper.h.


The documentation for this class was generated from the following files: