Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tool_multihand.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2011 Lukáš Tvrdý <lukast.dev@gmail.com>
3 * SPDX-FileCopyrightText: 2011 Dmitry Kazakov <dimula73@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
8
9#include <QTransform>
10
11#include <QPushButton>
12#include <QFormLayout>
13#include <QStackedWidget>
14#include <kis_slider_spin_box.h>
16#include "kis_canvas2.h"
17#include "kis_cursor.h"
18#include "KisViewManager.h"
19#include "kis_selection.h"
20
22
23#include <QtGlobal>
24
25
26static const int MAXIMUM_BRUSHES = 50;
27
29 : KisToolBrush(canvas),
30 m_transformMode(SYMMETRY),
31 m_angle(0),
32 m_handsCount(6),
33 m_mirrorVertically(false),
34 m_mirrorHorizontally(false),
35 m_showAxes(false),
36 m_translateRadius(100),
37 m_setupAxesFlag(false),
38 m_addSubbrushesMode(false),
39 m_intervalX(0),
40 m_intervalY(0)
41 , m_randomGenerator(QRandomGenerator::global()->generate())
42 , customUI(0)
43{
44
45
46 m_helper =
49 kundo2_i18n("Multibrush Stroke"));
51 if (image()) {
52 m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height());
53 }
54
55}
56
60
85
98
100{
101 if(mode() == KisTool::OTHER) {
103 requestUpdateOutline(event->point, 0);
105 }
106 else {
108 }
109}
110
124
139
152
160
161void KisToolMultihand::paint(QPainter& gc, const KoViewConverter &converter)
162{
163 QPainterPath path;
164
165 if (m_showAxes) {
166 const int axisLength = currentImage()->height() + currentImage()->width();
167
168 // add division guide lines if using multiple brushes
169 if ((m_handsCount > 1 && m_transformMode == SYMMETRY) ||
171 int axesCount;
173 axesCount = m_handsCount;
174 }
175 else {
176 axesCount = m_handsCount*2;
177 }
178 const qreal axesAngle = 360.0 / float(axesCount);
179 float currentAngle = 0.0;
180 const float startingInsetLength = 20; // don't start each line at the origin so we can see better when all points converge
181
182 // draw lines radiating from the origin
183 for( int i=0; i < axesCount; i++) {
184
185 currentAngle = i*axesAngle;
186
187 // convert angles to radians since cos and sin need that
188 currentAngle = currentAngle * 0.017453 + m_angle; // m_angle is current rotation set on UI
189
190 const QPoint startingSpot = QPoint(m_axesPoint.x()+ (sin(currentAngle)*startingInsetLength), m_axesPoint.y()- (cos(currentAngle))*startingInsetLength );
191 path.moveTo(startingSpot.x(), startingSpot.y());
192 QPointF symmetryLinePoint(m_axesPoint.x()+ (sin(currentAngle)*axisLength), m_axesPoint.y()- (cos(currentAngle))*axisLength );
193 path.lineTo(symmetryLinePoint);
194 }
195
196 }
197 else if(m_transformMode == MIRROR) {
198
200 path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()-axisLength*sin(m_angle+M_PI_2));
201 path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()+axisLength*sin(m_angle+M_PI_2));
202 }
203
205 path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle), m_axesPoint.y()-axisLength*sin(m_angle));
206 path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle), m_axesPoint.y()+axisLength*sin(m_angle));
207 }
208 }
209 else if (m_transformMode == COPYTRANSLATE) {
210
211 const int ellipsePreviewSize = 10;
212 // draw ellipse at origin to emphasize this is a drawing point
213 path.addEllipse(m_axesPoint.x()-(ellipsePreviewSize),
214 m_axesPoint.y()-(ellipsePreviewSize),
215 ellipsePreviewSize*2,
216 ellipsePreviewSize*2);
217
218 Q_FOREACH (QPointF dPos, m_subbrOriginalLocations) {
219 path.addEllipse(dPos, ellipsePreviewSize, ellipsePreviewSize); // Show subbrush reference locations while in add mode
220 }
221
222 // draw the horiz/vertical line for axis origin
223 path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle), m_axesPoint.y()-axisLength*sin(m_angle));
224 path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle), m_axesPoint.y()+axisLength*sin(m_angle));
225 path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()-axisLength*sin(m_angle+M_PI_2));
226 path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()+axisLength*sin(m_angle+M_PI_2));
227
228 }
230 const int ellipsePreviewSize = 10;
231
232 Q_FOREACH (QPointF dPos, intervalLocations()) {
233 path.addEllipse(dPos, ellipsePreviewSize, ellipsePreviewSize);
234 }
235 }
236 else {
237
238 // draw the horiz/vertical line for axis origin
239 path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle), m_axesPoint.y()-axisLength*sin(m_angle));
240 path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle), m_axesPoint.y()+axisLength*sin(m_angle));
241 path.moveTo(m_axesPoint.x()-axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()-axisLength*sin(m_angle+M_PI_2));
242 path.lineTo(m_axesPoint.x()+axisLength*cos(m_angle+M_PI_2), m_axesPoint.y()+axisLength*sin(m_angle+M_PI_2));
243 }
244
245 } else {
246
247 // not showing axis
249
250 Q_FOREACH (QPointF dPos, m_subbrOriginalLocations) {
251 // Show subbrush reference locations while in add mode
253 path.addEllipse(dPos, 10, 10);
254 }
255 }
256 }
257 }
258
259 KisToolFreehand::paint(gc, converter);
260
261 // origin point preview line/s
262 gc.save();
263 QPen outlinePen;
264 outlinePen.setColor(QColor(100,100,100,150));
265 outlinePen.setStyle(Qt::PenStyle::SolidLine);
266 gc.setPen(outlinePen);
267 paintToolOutline(&gc, pixelToView(path));
268 gc.restore();
269
270
271 // fill in a dot for the origin if showing axis
273 // draw a dot at the origin point to help with precisely moving
274 QPainterPath dotPath;
275 const int dotRadius = 4;
276 dotPath.moveTo(m_axesPoint.x(), m_axesPoint.y());
277 dotPath.addEllipse(m_axesPoint.x()- dotRadius*0.25, m_axesPoint.y()- dotRadius*0.25, dotRadius, dotRadius); // last 2 parameters are dot's size
278
279 QBrush fillBrush;
280 fillBrush.setColor(QColor(255, 255, 255, 255));
281 fillBrush.setStyle(Qt::SolidPattern);
282 gc.fillPath(pixelToView(dotPath), fillBrush);
283
284
285 // add slight offset circle for contrast to help show it on
286 dotPath = QPainterPath(); // resets path
287 dotPath.addEllipse(m_axesPoint.x() - dotRadius*0.75, m_axesPoint.y()- dotRadius*0.75, dotRadius, dotRadius); // last 2 parameters are dot's size
288 fillBrush.setColor(QColor(120, 120, 120, 255));
289 gc.fillPath(pixelToView(dotPath), fillBrush);
290 }
291
292}
293
295{
296 QVector<QTransform> transformations;
297 QTransform m;
298
300 qreal angle = 0;
301 const qreal angleStep = (2 * M_PI) / m_handsCount;
302
303 for(int i = 0; i < m_handsCount; i++) {
304 m.translate(m_axesPoint.x(), m_axesPoint.y());
305 m.rotateRadians(angle);
306 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
307
308 transformations << m;
309 m.reset();
310 angle += angleStep;
311 }
312 }
313 else if(m_transformMode == MIRROR) {
314 transformations << m;
315
317 m.translate(m_axesPoint.x(),m_axesPoint.y());
318 m.rotateRadians(m_angle);
319 m.scale(-1,1);
320 m.rotateRadians(-m_angle);
321 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
322 transformations << m;
323 m.reset();
324 }
325
326 if (m_mirrorVertically) {
327 m.translate(m_axesPoint.x(),m_axesPoint.y());
328 m.rotateRadians(m_angle);
329 m.scale(1,-1);
330 m.rotateRadians(-m_angle);
331 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
332 transformations << m;
333 m.reset();
334 }
335
337 m.translate(m_axesPoint.x(),m_axesPoint.y());
338 m.rotateRadians(m_angle);
339 m.scale(-1,-1);
340 m.rotateRadians(-m_angle);
341 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
342 transformations << m;
343 m.reset();
344 }
345
346 }
347 else if(m_transformMode == SNOWFLAKE) {
348 qreal angle = 0;
349 const qreal angleStep = (2 * M_PI) / m_handsCount/4;
350
351 for(int i = 0; i < m_handsCount*4; i++) {
352 if ((i%2)==1) {
353
354 m.translate(m_axesPoint.x(), m_axesPoint.y());
355 m.rotateRadians(m_angle-angleStep);
356 m.rotateRadians(angle);
357 m.scale(-1,1);
358 m.rotateRadians(-m_angle+angleStep);
359 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
360
361 transformations << m;
362 m.reset();
363 angle += angleStep*2;
364 } else {
365 m.translate(m_axesPoint.x(), m_axesPoint.y());
366 m.rotateRadians(m_angle-angleStep);
367 m.rotateRadians(angle);
368 m.rotateRadians(-m_angle+angleStep);
369 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
370
371 transformations << m;
372 m.reset();
373 angle += angleStep*2;
374 }
375 }
376 }
377 else if(m_transformMode == TRANSLATE) {
382 for (int i = 0; i < m_handsCount; i++){
383 const qreal angle = m_randomGenerator.bounded(2.0 * M_PI);
384 const qreal length = m_randomGenerator.bounded(1.0);
385
386 // convert the Polar coordinates to Cartesian coordinates
387 qreal nx = (m_translateRadius * cos(angle) * length);
388 qreal ny = (m_translateRadius * sin(angle) * length);
389
390 m.translate(m_axesPoint.x(),m_axesPoint.y());
391 m.rotateRadians(m_angle);
392 m.translate(nx,ny);
393 m.rotateRadians(-m_angle);
394 m.translate(-m_axesPoint.x(), -m_axesPoint.y());
395 transformations << m;
396 m.reset();
397 }
398 } else if (m_transformMode == COPYTRANSLATE) {
399 transformations << m;
400 Q_FOREACH (QPointF dPos, m_subbrOriginalLocations) {
401 const QPointF resPos = dPos-m_axesPoint; // Calculate the difference between subbrush reference position and "origin" reference
402 m.translate(resPos.x(), resPos.y());
403 transformations << m;
404 m.reset();
405 }
407 KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
408 Q_ASSERT(kisCanvas);
409 const QRect bounds = kisCanvas->viewManager()->selection() ?
410 kisCanvas->viewManager()->selection()->selectedExactRect() :
411 kisCanvas->currentImage()->bounds();
412 const QPoint dPos = bounds.topLeft() +
413 QPoint(m_intervalX ? m_intervalX * floor((m_axesPoint.x() - bounds.left()) / m_intervalX) : 0,
414 m_intervalY ? m_intervalY * floor((m_axesPoint.y() - bounds.top()) / m_intervalY) : 0);
415
416 Q_FOREACH (QPoint pos, intervalLocations()) {
417 const QPointF resPos = pos - dPos;
418 m.translate(resPos.x(), resPos.y());
419 transformations << m;
420 m.reset();
421 }
422 }
423
424 m_helper->setupTransformations(transformations);
425}
426
428{
429 QWidget *widget = KisToolBrush::createOptionWidget();
430
432
433 // brush smoothing option.
434 //customUI->layout()->addWidget(widget);
435 customUI->smoothingOptionsLayout->addWidget(widget);
436
437
438 // setup common parameters that all of the modes will see
439 connect(customUI->showAxesCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetAxesVisible(bool)));
440 customUI->showAxesCheckbox->setChecked((bool)m_configGroup.readEntry("showAxes", false));
441
442 connect(image(), SIGNAL(sigSizeChanged(QPointF,QPointF)), this, SLOT(resetAxes()));
443
444 customUI->moveOriginButton->setCheckable(true);
445 connect(customUI->moveOriginButton, SIGNAL(clicked(bool)),this, SLOT(activateAxesPointModeSetup()));
446
447 connect(customUI->resetOriginButton, SIGNAL(released()), this, SLOT(resetAxes()));
448
449 customUI->multihandTypeCombobox->addItem(i18n("Symmetry"),int(SYMMETRY)); // axis mode
450 customUI->multihandTypeCombobox->addItem(i18nc("Label of Mirror in Multihand brush tool options", "Mirror"),int(MIRROR));
451 customUI->multihandTypeCombobox->addItem(i18n("Translate"),int(TRANSLATE));
452 customUI->multihandTypeCombobox->addItem(i18n("Snowflake"),int(SNOWFLAKE));
453 customUI->multihandTypeCombobox->addItem(i18n("Copy Translate"),int(COPYTRANSLATE));
454 customUI->multihandTypeCombobox->addItem(i18n("Copy Translate at Intervals"),int(COPYTRANSLATEINTERVALS));
455 connect(customUI->multihandTypeCombobox,SIGNAL(currentIndexChanged(int)),this, SLOT(slotSetTransformMode(int)));
456 customUI->multihandTypeCombobox->setCurrentIndex(m_configGroup.readEntry("transformMode", 0));
457
458
459 customUI->axisRotationAngleSelector->setRange(0.0, 90.0);
460 customUI->axisRotationAngleSelector->setDecimals(1);
461 customUI->axisRotationAngleSelector->setWrapping(false);
462 customUI->axisRotationAngleSelector->setFlipOptionsMode(KisAngleSelector::FlipOptionsMode_NoFlipOptions);
463 connect(customUI->axisRotationAngleSelector, SIGNAL(angleChanged(qreal)), this, SLOT(slotSetAxesAngle(qreal)));
464 customUI->axisRotationAngleSelector->setAngle(m_configGroup.readEntry("axesAngle", 0.0));
465
466
467 // symmetry mode options
468 customUI->brushCountSpinBox->setRange(1, MAXIMUM_BRUSHES);
469 connect(customUI->brushCountSpinBox, SIGNAL(valueChanged(int)),this, SLOT(slotSetHandsCount(int)));
470 customUI->brushCountSpinBox->setValue(m_configGroup.readEntry("handsCount", 4));
471
472 // mirror mode specific options
473 connect(customUI->horizontalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorHorizontally(bool)));
474 customUI->horizontalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorHorizontally", false));
475
476 connect(customUI->verticalCheckbox, SIGNAL(toggled(bool)), this, SLOT(slotSetMirrorVertically(bool)));
477 customUI->verticalCheckbox->setChecked((bool)m_configGroup.readEntry("mirrorVertically", false));
478
479 // translate mode options
480 customUI->translationRadiusSpinbox->setRange(0, 200);
481 customUI->translationRadiusSpinbox->setSuffix(i18n(" px"));
482
483 connect(customUI->translationRadiusSpinbox,SIGNAL(valueChanged(int)),this,SLOT(slotSetTranslateRadius(int)));
484 customUI->translationRadiusSpinbox->setValue(m_configGroup.readEntry("translateRadius", 0));
485
486 // Copy translate mode options and actions
487 connect(customUI->addSubbrushButton, &QPushButton::clicked, this, &KisToolMultihand::slotAddSubbrushesMode);
488 connect(customUI->removeSubbrushButton, &QPushButton::clicked, this, &KisToolMultihand::slotRemoveAllSubbrushes);
489
490 // Copy translate at intervals mode options and actions
491 customUI->intervalXSpinBox->setRange(0, 2000);
492 customUI->intervalXSpinBox->setSuffix(i18n(" px"));
493 customUI->intervalYSpinBox->setRange(0, 2000);
494 customUI->intervalYSpinBox->setSuffix(i18n(" px"));
495
496 KisAspectRatioLocker *intervalAspectLocker = new KisAspectRatioLocker(this);
497 intervalAspectLocker->connectSpinBoxes(customUI->intervalXSpinBox, customUI->intervalYSpinBox, customUI->intervalAspectButton);
498
499 customUI->intervalXSpinBox->setValue(m_configGroup.readEntry("intervalX", 0));
500 customUI->intervalYSpinBox->setValue(m_configGroup.readEntry("intervalY", 0));
501 connect(intervalAspectLocker, SIGNAL(sliderValueChanged()), this, SLOT(slotSetIntervals()));
502 slotSetIntervals(); // X and Y need to be set at the same time.
503 connect(intervalAspectLocker, SIGNAL(aspectButtonChanged()), this, SLOT(slotSetKeepAspect()));
504 customUI->intervalAspectButton->setKeepAspectRatio(m_configGroup.readEntry("intervalKeepAspect", false));
505
506 // snowflake re-uses the existing options, so there is no special parameters for that...
507
508
509 return static_cast<QWidget*>(customUI); // keeping it in the native class until the end allows us to access the UI components
510}
511
513{
514 if (customUI->moveOriginButton->isChecked()){
515 m_setupAxesFlag = true;
517 updateCanvas();
518 } else {
520 }
521}
522
524{
525 m_axesPoint = QPointF(0.5 * image()->width(), 0.5 * image()->height());
527}
528
529
531{
532 m_setupAxesFlag = false;
533 customUI->moveOriginButton->setChecked(false);
535 updateCanvas();
536}
537
539{
540 KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
541 Q_ASSERT(kisCanvas);
542 kisCanvas->updateCanvas();
543 if(customUI->moveOriginButton->isChecked())
544 {
545 kisCanvas->viewManager()->showFloatingMessage(i18n("X: %1 px\nY: %2 px"
546 , QString::number(this->m_axesPoint.x(),'f',1),QString::number(this->m_axesPoint.y(),'f',1))
547 , QIcon(), 1000, KisFloatingMessage::High, Qt::AlignLeft | Qt::TextWordWrap | Qt::AlignVCenter);
548 }
549}
550
552{
554
555 KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2*>(canvas());
556 Q_ASSERT(kisCanvas);
557 const QRect bounds = kisCanvas->viewManager()->selection() ?
558 kisCanvas->viewManager()->selection()->selectedExactRect() :
559 kisCanvas->currentImage()->bounds();
560
561 const int intervals = m_intervalX ? (bounds.width() / m_intervalX) : 0 +
562 m_intervalY ? (bounds.height() / m_intervalY) : 0;
563 if (intervals > MAXIMUM_BRUSHES) {
564 kisCanvas->viewManager()->showFloatingMessage(
565 i18n("Multibrush Tool does not support more than %1 brushes; use a larger interval.",
566 QString::number(MAXIMUM_BRUSHES)), QIcon());
567 return intervalLocations;
568 }
569
570 for (int x = bounds.left(); x <= bounds.right(); x += m_intervalX) {
571 for (int y = bounds.top(); y <= bounds.bottom(); y += m_intervalY) {
572 intervalLocations << QPoint(x,y);
573
574 if (m_intervalY == 0) { break; }
575 }
576 if (m_intervalX == 0) { break; }
577 }
578
579 return intervalLocations;
580}
581
583{
584 m_handsCount = count;
585 m_configGroup.writeEntry("handsCount", count);
586 updateCanvas();
587}
588
590{
591 //negative so axes rotates counter clockwise
592 m_angle = -angle*M_PI/180;
593 updateCanvas();
594 m_configGroup.writeEntry("axesAngle", angle);
595}
596
598{
599 m_transformMode = enumTransformModes(customUI->multihandTypeCombobox->itemData(index).toInt());
600 m_configGroup.writeEntry("transformMode", index);
601
602 // turn on or off what we need
603
604 bool vis = index == MIRROR;
605 customUI->horizontalCheckbox->setVisible(vis);
606 customUI->verticalCheckbox->setVisible(vis);
607
608 vis = index == TRANSLATE;
609 customUI->translationRadiusSpinbox->setVisible(vis);
610 customUI->radiusLabel->setVisible(vis);
611 customUI->brushCountSpinBox->setVisible(vis);
612 customUI->brushesLabel->setVisible(vis);
613
614 vis = index == SYMMETRY || index == SNOWFLAKE || index == TRANSLATE;
615 customUI->brushCountSpinBox->setVisible(vis);
616 customUI->brushesLabel->setVisible(vis);
617
618 vis = index == COPYTRANSLATE;
619 customUI->subbrushLabel->setVisible(vis);
620 customUI->addSubbrushButton->setVisible(vis);
621 customUI->addSubbrushButton->setChecked(m_addSubbrushesMode);
622 customUI->removeSubbrushButton->setVisible(vis);
623
624 vis = index == COPYTRANSLATEINTERVALS;
625 customUI->intervalXLabel->setVisible(vis);
626 customUI->intervalYLabel->setVisible(vis);
627 customUI->intervalXSpinBox->setVisible(vis);
628 customUI->intervalYSpinBox->setVisible(vis);
629 customUI->intervalAspectButton->setVisible(vis);
630
631 vis = index != COPYTRANSLATEINTERVALS;
632 customUI->label->setVisible(vis); // the origin label
633 customUI->moveOriginButton->setVisible(vis);
634 customUI->resetOriginButton->setVisible(vis);
635 customUI->axisRotationLabel->setVisible(vis);
636 customUI->axisRotationAngleSelector->setVisible(vis);
637
638}
639
641{
642 m_showAxes = vis;
643 updateCanvas();
644 m_configGroup.writeEntry("showAxes", vis);
645}
646
647
649{
650 m_mirrorVertically = mirror;
651 updateCanvas();
652 m_configGroup.writeEntry("mirrorVertically", mirror);
653}
654
656{
657 m_mirrorHorizontally = mirror;
658 updateCanvas();
659 m_configGroup.writeEntry("mirrorHorizontally", mirror);
660}
661
663{
664 m_translateRadius = radius;
665 m_configGroup.writeEntry("translateRadius", radius);
666}
667
669{
670 m_addSubbrushesMode = checked;
671 updateCanvas();
672}
673
679
681{
682 m_intervalX = customUI->intervalXSpinBox->value();
683 m_configGroup.writeEntry("intervalX", m_intervalX);
684
685 m_intervalY = customUI->intervalYSpinBox->value();
686 m_configGroup.writeEntry("intervalY", m_intervalY);
687
688 updateCanvas();
689}
690
692{
693 m_configGroup.writeEntry("intervalKeepAspect", customUI->intervalAspectButton->keepAspectRatio());
694}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
@ FlipOptionsMode_NoFlipOptions
There is no flip options available.
void connectSpinBoxes(SpinBoxType *spinOne, SpinBoxType *spinTwo, KoAspectButton *aspectButton)
KisImageWSP currentImage() const
void updateCanvas(const QRectF &rc) override
KisViewManager * viewManager() const
static QCursor crossCursor()
Definition kis_cursor.cc:34
qint32 width() const
qint32 height() const
QRect bounds() const override
void resetCursorStyle() override
KConfigGroup m_configGroup
QWidget * createOptionWidget() override
void continuePrimaryAction(KoPointerEvent *event) override
KisPaintingInformationBuilder * paintingInformationBuilder() const
void beginPrimaryAction(KoPointerEvent *event) override
void resetHelper(KisToolFreehandHelper *helper)
void endPrimaryAction(KoPointerEvent *event) override
void setupTransformations(const QVector< QTransform > &transformations)
void slotSetMirrorVertically(bool mirror)
void beginAlternateAction(KoPointerEvent *event, AlternateAction action) override
void slotSetAxesVisible(bool vis)
QVector< QPoint > intervalLocations()
void continueAlternateAction(KoPointerEvent *event, AlternateAction action) override
void slotSetTranslateRadius(int radius)
void slotSetHandsCount(int count)
void slotAddSubbrushesMode(bool checked)
void paint(QPainter &gc, const KoViewConverter &converter) override
QRandomGenerator m_randomGenerator
KisToolMultiHandConfigWidget * customUI
enumTransformModes m_transformMode
QWidget * createOptionWidget() override
void slotSetTransformMode(int qcomboboxIndex)
void endAlternateAction(KoPointerEvent *event, AlternateAction action) override
void beginPrimaryAction(KoPointerEvent *event) override
KisToolMultihandHelper * m_helper
void continuePrimaryAction(KoPointerEvent *event) override
void slotSetAxesAngle(qreal angle)
void slotSetMirrorHorizontally(bool mirror)
void mouseMoveEvent(KoPointerEvent *event) override
QVector< QPointF > m_subbrOriginalLocations
KisToolMultihand(KoCanvasBase *canvas)
void endPrimaryAction(KoPointerEvent *event) override
virtual void requestUpdateOutline(const QPointF &outlineDocPoint, const KoPointerEvent *event)
void setMode(ToolMode mode) override
KisSelectionSP selection()
void showFloatingMessage(const QString &message, const QIcon &icon, int timeout=4500, KisFloatingMessage::Priority priority=KisFloatingMessage::Medium, int alignment=Qt::AlignCenter|Qt::TextWordWrap)
shows a floating message in the top right corner of the canvas
QPointer< KoCanvasResourceProvider > resourceManager
QPointF point
The point in document coordinates.
void useCursor(const QCursor &cursor)
virtual void mouseMoveEvent(KoPointerEvent *event)=0
QAction * action(const QString &name) const
virtual void paint(QPainter &painter, const KoViewConverter &converter)=0
#define bounds(x, a, b)
#define M_PI
Definition kis_global.h:111
static const int MAXIMUM_BRUSHES
KUndo2MagicString kundo2_i18n(const char *text)
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.
QPointF convertToPixelCoord(KoPointerEvent *e)
Definition kis_tool.cc:189
virtual ToolMode mode() const
Definition kis_tool.cc:407
KisImageWSP currentImage()
Definition kis_tool.cc:393
virtual void continueAlternateAction(KoPointerEvent *event, AlternateAction action)
Definition kis_tool.cc:477
QPointF pixelToView(const QPoint &pixelCoord) const
Definition kis_tool.cc:269
virtual void endAlternateAction(KoPointerEvent *event, AlternateAction action)
Definition kis_tool.cc:483
void paintToolOutline(QPainter *painter, const KisOptimizedBrushOutline &path)
Definition kis_tool.cc:589
KisImageWSP image() const
Definition kis_tool.cc:332
@ OTHER_1
Definition kis_tool.h:306
@ HOVER_MODE
Definition kis_tool.h:299
virtual void beginAlternateAction(KoPointerEvent *event, AlternateAction action)
Definition kis_tool.cc:466
AlternateAction
Definition kis_tool.h:134
@ ChangeSizeSnap
Definition kis_tool.h:136
@ ChangeSize
Definition kis_tool.h:135
KisCanvas2 * canvas