Krita Source Code Documentation
Loading...
Searching...
No Matches
KisToolSelectMagnetic.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019 Kuntal Majumder <hellozee@disroot.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <QApplication>
10#include <QPainter>
11#include <QPainterPath>
12#include <QPushButton>
13#include <QVBoxLayout>
14
15#include <kis_debug.h>
16#include <klocalizedstring.h>
17
18#include <KoPointerEvent.h>
19#include <KoShapeController.h>
20#include <KoPathShape.h>
21#include <KoColorSpace.h>
22#include <KoCompositeOp.h>
24#include <KoViewConverter.h>
25
26#include <kis_layer.h>
28#include <kis_cursor.h>
29#include <kis_image.h>
30#include <kis_default_bounds.h>
31
32#include "canvas/kis_canvas2.h"
33#include "kis_painter.h"
34#include "kis_pixel_selection.h"
37#include <kis_command_utils.h>
40
41#include "kis_algebra_2d.h"
42
45#include <kis_slider_spin_box.h>
46
47#define FEEDBACK_LINE_WIDTH 2
48
50 : KisToolSelect(canvas,
51 KisCursor::loadWithSize("tool_magnetic_selection_cursor.svg", 32, 32, 6, 6),
52 i18n("Magnetic Selection"))
53 , m_worker(nullptr)
54 , m_mouseHoverCompressor(100, KisSignalCompressor::FIRST_ACTIVE)
55{ }
56
58{
59 if (isSelecting()) {
60 if (event->key() == Qt::Key_Control) {
61 m_continuedMode = true;
62 }
63 }
65}
66
67/*
68 * Calculates the checkpoints responsible to determining the last point from where
69 * the edge is calculated.
70 * Takes 3 point, min, median and max, searches for an edge point from median to max, if fails,
71 * searches for the same from median to min, if fails, median becomes that edge point.
72 */
74{
75 qreal totalDistance = 0.0;
76 int checkPoint = 0;
77 int finalPoint = 2;
78 int midPoint = 1;
79 int minPoint = 0;
80 qreal maxFactor = 2;
81
82 for (; finalPoint < points.count(); finalPoint++) {
83 totalDistance += kisDistance(points[finalPoint], points[finalPoint - 1]);
84
85 if (totalDistance <= m_anchorGap / 3.0) {
86 minPoint = finalPoint;
87 }
88
89 if (totalDistance <= m_anchorGap) {
90 midPoint = finalPoint;
91 }
92
93 if (totalDistance > maxFactor * m_anchorGap) {
94 break;
95 }
96 }
97
98 if (totalDistance > maxFactor * m_anchorGap) {
99 bool foundSomething = false;
100
101 for (int i = midPoint; i < finalPoint; i++) {
102 if (m_worker->intensity(points.at(i).toPoint()) >= m_threshold) {
103 m_lastAnchor = points.at(i).toPoint();
104 m_anchorPoints.push_back(m_lastAnchor);
105
106 vQPointF temp;
107 for (int j = 0; j <= i; j++) {
108 temp.push_back(points[j]);
109 }
110
111 m_pointCollection.push_back(temp);
112 foundSomething = true;
113 checkPoint = i;
114 break;
115 }
116 }
117
118 if (!foundSomething) {
119 for (int i = midPoint - 1; i >= minPoint; i--) {
120 if (m_worker->intensity(points.at(i).toPoint()) >= m_threshold) {
121 m_lastAnchor = points.at(i).toPoint();
122 m_anchorPoints.push_back(m_lastAnchor);
123 vQPointF temp;
124 for (int j = midPoint - 1; j >= i; j--) {
125 temp.push_front(points[j]);
126 }
127
128 m_pointCollection.push_back(temp);
129 foundSomething = true;
130 checkPoint = i;
131 break;
132 }
133 }
134 }
135
136 if (!foundSomething) {
137 m_lastAnchor = points[midPoint].toPoint();
138 m_anchorPoints.push_back(m_lastAnchor);
139 vQPointF temp;
140
141 for (int j = 0; j <= midPoint; j++) {
142 temp.push_back(points[j]);
143 }
144
145 m_pointCollection.push_back(temp);
146 checkPoint = midPoint;
147 foundSomething = true;
148 }
149 }
150
151 totalDistance = 0.0;
153
154 for (; finalPoint < points.count(); finalPoint++) {
155 totalDistance += kisDistance(points[finalPoint], points[checkPoint]);
156 if (totalDistance > maxFactor * m_anchorGap) {
157 points.remove(0, checkPoint + 1);
158 calculateCheckPoints(points);
159 break;
160 }
161 }
162} // KisToolSelectMagnetic::calculateCheckPoints
163
165{
166 if (isSelecting()) {
167 if (event->key() == Qt::Key_Control ||
168 !(event->modifiers() & Qt::ControlModifier)) {
169
170 m_continuedMode = false;
171 if (mode() != PAINT_MODE) {
172 // Prevent finishing the selection if there is only one point, since
173 // finishSelectionAction will deselect the current selection. That
174 // is fine if the user just clicks, but not if we are in continued
175 // mode
176 if (m_points.count() > 1) {
178 }
179 m_points.clear(); // ensure points are always cleared
180 }
181 }
182 }
183
185}
186
188{
189 return m_worker->computeEdge(m_searchRadius, a, b, m_filterRadius);
190}
191
192// the cursor is still tracked even when no mousebutton is pressed
194{
195 if (isMovingSelection()) {
197 return;
198 }
199
200 m_lastCursorPos = convertToPixelCoord(event);
201 if (isSelecting()) {
203 }
205} // KisToolSelectMagnetic::mouseMoveEvent
206
207// press primary mouse button
209{
211 if (isMovingSelection()) {
212 return;
213 }
214
215 setMode(KisTool::PAINT_MODE);
216 QPointF temp(convertToPixelCoord(event));
217
218 if (!image()->bounds().contains(temp.toPoint())) {
219 return;
220 }
221
222 m_cursorOnPress = temp;
223
225
226 if (m_complete || m_selected) {
227 return;
228 }
229
230 if (m_anchorPoints.count() != 0) {
231 vQPointF edge = computeEdgeWrapper(m_anchorPoints.last(), temp.toPoint());
232 m_points.append(edge);
233 m_pointCollection.push_back(edge);
234 } else {
236 updateInitialAnchorBounds(temp.toPoint());
237 Q_EMIT setButtonsEnabled(true);
238 }
239
240 m_lastAnchor = temp.toPoint();
241 m_anchorPoints.push_back(m_lastAnchor);
242 m_lastCursorPos = temp;
244 updateCanvasPixelRect(image()->bounds());
245} // KisToolSelectMagnetic::beginPrimaryAction
246
248{
249 Q_FOREACH (const QPoint pt, m_anchorPoints) {
250 qreal zoomLevel = canvas()->viewConverter()->zoom();
251 int sides = (int) std::ceil(10.0 / zoomLevel);
252 QRect r = QRect(QPoint(0, 0), QSize(sides, sides));
253 r.moveCenter(pt);
254 if (r.contains(temp.toPoint())) {
255 m_selected = true;
256 m_selectedAnchor = m_anchorPoints.lastIndexOf(pt);
257 return;
258 }
259 }
260}
261
262/*
263
264~~TODO ALERT~~
265The double click adds a bit more functionality to the tool but also the reason
266of multiple problems, so disabling it for now, if someone can find some alternate
267ways for mimicking what the double clicks intended to do, please drop a patch
268
269void KisToolSelectMagnetic::beginPrimaryDoubleClickAction(KoPointerEvent *event)
270{
271 QPointF temp = convertToPixelCoord(event);
272
273 if (!image()->bounds().contains(temp.toPoint())) {
274 return;
275 }
276
277 checkIfAnchorIsSelected(temp);
278
279 if (m_selected) {
280 deleteSelectedAnchor();
281 return;
282 }
283
284 if (m_complete) {
285 int pointA = 0, pointB = 1;
286 double dist = std::numeric_limits<double>::max();
287 int total = m_anchorPoints.count();
288 for (int i = 0; i < total; i++) {
289 double distToCompare = kisDistance(m_anchorPoints[i], temp) +
290 kisDistance(temp, m_anchorPoints[(i + 1) % total]);
291 if (distToCompare < dist) {
292 pointA = i;
293 pointB = (i + 1) % total;
294 dist = distToCompare;
295 }
296 }
297
298 vQPointF path1 = computeEdgeWrapper(m_anchorPoints[pointA], temp.toPoint());
299 vQPointF path2 = computeEdgeWrapper(temp.toPoint(), m_anchorPoints[pointB]);
300
301 m_pointCollection[pointA] = path1;
302 m_pointCollection.insert(pointB, path2);
303 m_anchorPoints.insert(pointB, temp.toPoint());
304
305 reEvaluatePoints();
306 }
307} // KisToolSelectMagnetic::beginPrimaryDoubleClickAction
308*/
309
310// drag while primary mouse button is pressed
312{
313 if (isMovingSelection()) {
315 return;
316 }
317
318 if (m_selected) {
319 m_anchorPoints[m_selectedAnchor] = convertToPixelCoord(event).toPoint();
320 } else if (!m_complete) {
321 m_lastCursorPos = convertToPixelCoord(event);
324 }
325}
326
328{
329 QPoint current = m_lastCursorPos.toPoint();
330 if (!image()->bounds().contains(current))
331 return;
332
333 if(kisDistance(m_lastAnchor, current) < m_anchorGap)
334 return;
335
336 vQPointF pointSet = computeEdgeWrapper(m_lastAnchor, current);
337 calculateCheckPoints(pointSet);
338}
339
340// release primary mouse button
342{
343 if (isMovingSelection()) {
345 return;
346 }
347
348 if (m_selected && convertToPixelCoord(event) != m_cursorOnPress) {
349 if (!image()->bounds().contains(m_anchorPoints[m_selectedAnchor])) {
351 } else {
353 }
354 } else if (m_selected) {
355 QPointF temp(convertToPixelCoord(event));
356
357 if (!image()->bounds().contains(temp.toPoint())) {
358 return;
359 }
360
361 if (m_snapBound.contains(temp) && m_anchorPoints.count() > 1) {
362 if(m_complete){
364 return;
365 }
366 vQPointF edge = computeEdgeWrapper(m_anchorPoints.last(), temp.toPoint());
367 m_points.append(edge);
368 m_pointCollection.push_back(edge);
369 m_complete = true;
370 }
371 }
375 }
376 m_selected = false;
377} // KisToolSelectMagnetic::endPrimaryAction
378
380{
381 if (m_anchorPoints.isEmpty())
382 return;
383
384 if (m_anchorPoints.size() <= 1) {
386
387 } else if (m_selectedAnchor == 0) { // if it is the initial anchor
388 m_anchorPoints.pop_front();
389 m_pointCollection.pop_front();
390
391 if (m_complete) {
393 }
394
395 } else if (m_selectedAnchor == m_anchorPoints.count() - 1) { // if it is the last anchor
396 m_anchorPoints.pop_back();
397 m_pointCollection.pop_back();
398
399 if (m_complete) {
401 }
402
403 } else { // it is in the middle
409 }
410
411 if (m_complete && m_anchorPoints.size() < 3) {
412 m_complete = false;
413 m_pointCollection.pop_back();
414 }
415
417
418} // KisToolSelectMagnetic::deleteSelectedAnchor
419
454
456{
457 qreal zoomLevel = canvas()->viewConverter()->zoom();
458 int sides = (int) std::ceil(10.0 / zoomLevel);
459 m_snapBound = QRectF(QPoint(0, 0), QSize(sides, sides));
460 m_snapBound.moveCenter(pt);
461 return sides;
462}
463
465{
466 m_points.clear();
467 Q_FOREACH (const vQPointF vec, m_pointCollection) {
468 m_points.append(vec);
469 }
470
472}
473
475{
476 KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2 *>(canvas());
477 KIS_ASSERT_RECOVER_RETURN(kisCanvas);
478 kisCanvas->updateCanvas();
479 setMode(KisTool::HOVER_MODE);
480 m_complete = false;
481 m_finished = true;
482
483 // just for testing out
484 // m_worker.saveTheImage(m_points);
485
486 QRectF boundingViewRect =
488
489 KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Magnetic Selection"));
490
491 if (m_points.count() > 2 &&
492 !helper.tryDeselectCurrentSelection(boundingViewRect, selectionAction()))
493 {
494 KisCursorOverrideLock cursorLock(KisCursor::waitCursor());
495
496 const SelectionMode mode =
497 helper.tryOverrideSelectionMode(kisCanvas->viewManager()->selection(),
500 if (mode == PIXEL_SELECTION) {
501 KisProcessingApplicator applicator(
502 currentImage(),
503 currentNode(),
506 kundo2_i18n("Magnetic Selection"));
507
508 KisPixelSelectionSP tmpSel =
509 new KisPixelSelection(new KisDefaultBounds(currentImage()));
510
511 const bool antiAlias = antiAliasSelection();
512 const int grow = growSelection();
513 const int feather = featherSelection();
514
515 QPainterPath path;
516 path.addPolygon(m_points);
517 path.closeSubpath();
518
520 [tmpSel, antiAlias, grow, feather, path]() mutable
521 -> KUndo2Command * {
522 KisPainter painter(tmpSel);
523 painter.setPaintColor(
524 KoColor(Qt::black, tmpSel->colorSpace()));
525 // Since the feathering already smooths the selection, the
526 // antiAlias is not applied if we must feather
527 painter.setAntiAliasPolygonFill(antiAlias && feather == 0);
530
531 painter.paintPainterPath(path);
532
533 if (grow > 0) {
534 KisGrowSelectionFilter biggy(grow, grow);
535 biggy.process(tmpSel,
536 tmpSel->selectedRect().adjusted(-grow,
537 -grow,
538 grow,
539 grow));
540 } else if (grow < 0) {
541 KisShrinkSelectionFilter tiny(-grow, -grow, false);
542 tiny.process(tmpSel, tmpSel->selectedRect());
543 }
544 if (feather > 0) {
545 KisFeatherSelectionFilter feathery(feather);
546 feathery.process(
547 tmpSel,
548 tmpSel->selectedRect().adjusted(-feather,
549 -feather,
550 feather,
551 feather));
552 }
553
554 if (grow == 0 && feather == 0) {
555 tmpSel->setOutlineCache(path);
556 } else {
557 tmpSel->invalidateOutlineCache();
558 }
559
560 return 0;
561 });
562
564 helper.selectPixelSelection(applicator, tmpSel, selectionAction());
565 applicator.end();
566
567 } else {
568 KoPathShape *path = new KoPathShape();
569 path->setShapeId(KoPathShapeId);
570
571 QTransform resolutionMatrix;
572 resolutionMatrix.scale(1 / currentImage()->xRes(), 1 / currentImage()->yRes());
573 path->moveTo(resolutionMatrix.map(m_points[0]));
574 for (int i = 1; i < m_points.count(); i++)
575 path->lineTo(resolutionMatrix.map(m_points[i]));
576 path->close();
577 path->normalize();
578 helper.addSelectionShape(path, selectionAction());
579 }
580 }
581
583
585} // KisToolSelectMagnetic::finishSelectionAction
586
588{
589 m_points.clear();
590 m_anchorPoints.clear();
591 m_pointCollection.clear();
592 m_paintPath = QPainterPath();
593 m_complete = false;
594}
595
597{
598 m_paintPath = QPainterPath();
599 if (m_points.size() > 0) {
600 m_paintPath.moveTo(pixelToView(m_points[0]));
601 }
602 for (int i = 1; i < m_points.count(); i++) {
603 m_paintPath.lineTo(pixelToView(m_points[i]));
604 }
605
607
608 if (m_continuedMode && mode() != PAINT_MODE) {
610 }
611
612 updateCanvasPixelRect(image()->bounds());
613}
614
615void KisToolSelectMagnetic::paint(QPainter& gc, const KoViewConverter &converter)
616{
617 Q_UNUSED(converter);
619 if ((mode() == KisTool::PAINT_MODE || m_continuedMode) &&
620 !m_anchorPoints.isEmpty())
621 {
622 QPainterPath outline = m_paintPath;
623 if (m_continuedMode && mode() != KisTool::PAINT_MODE) {
624 outline.lineTo(pixelToView(m_lastCursorPos));
625 }
626 paintToolOutline(&gc, outline);
627 drawAnchors(gc);
628 }
629}
630
632{
633 const KisHandlePalette palette = canvas()->displayRendererInterface()->handlePaletteForDisplayColorSpace();
634 int sides = updateInitialAnchorBounds(m_anchorPoints.first());
635 Q_FOREACH (const QPoint pt, m_anchorPoints) {
636 KisHandlePainterHelper helper(&gc, handleRadius(), decorationThickness());
637 QRect r(QPoint(0, 0), QSize(sides, sides));
638 r.moveCenter(pt);
639 if (r.contains(m_lastCursorPos.toPoint())) {
641 } else {
643 }
644 helper.drawHandleRect(pixelToView(pt), 4, QPoint(0, 0));
645 }
646}
647
649{
650 if (m_points.count() > 1) {
651 qint32 lastPointIndex = m_points.count() - 1;
652
653 QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_points[lastPointIndex]).normalized();
654 updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH);
655
656 updateCanvasPixelRect(updateRect);
657 }
658}
659
661{
662 if (!m_points.isEmpty()) {
663 qint32 lastPointIndex = m_points.count() - 1;
664
665 QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_lastCursorPos).normalized();
666 updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH);
667
668 updateCanvasPixelRect(updateRect);
669 }
670}
671
672void KisToolSelectMagnetic::activate(const QSet<KoShape *> &shapes)
673{
674 m_worker.reset(new KisMagneticWorker(image()->projection()));
675 m_configGroup = KSharedConfig::openConfig()->group(toolId());
676 connect(action("undo_polygon_selection"), SIGNAL(triggered()), SLOT(undoPoints()), Qt::UniqueConnection);
677 connect(&m_mouseHoverCompressor, SIGNAL(timeout()), this, SLOT(slotCalculateEdge()));
679}
680
682{
683 KisCanvas2 *kisCanvas = dynamic_cast<KisCanvas2 *>(canvas());
684 KIS_ASSERT_RECOVER_RETURN(kisCanvas);
685 kisCanvas->updateCanvas();
687 m_continuedMode = false;
688 disconnect(action("undo_polygon_selection"), nullptr, this, nullptr);
689
691}
692
694{
695 if (m_complete) return;
696
697 if(m_anchorPoints.count() <= 1){
699 return;
700 }
701
702 m_anchorPoints.pop_back();
703 m_pointCollection.pop_back();
705}
706
708{
709 if (m_finished || m_anchorPoints.count() < 2) return;
710
711 setButtonsEnabled(false);
713 m_finished = false;
714}
715
723
725{
727 KisSelectionOptions *selectionWidget = selectionOptionWidget();
728
729 // Create widgets
731 sliderRadius->setObjectName("radius");
732 sliderRadius->setRange(2.5, 100.0, 2);
733 sliderRadius->setSingleStep(0.5);
734 sliderRadius->setPrefix(
735 i18nc("Filter radius in Magnetic Select Tool settings",
736 "Filter Radius: "));
737
738 KisSliderSpinBox *sliderThreshold = new KisSliderSpinBox;
739 sliderThreshold->setObjectName("threshold");
740 sliderThreshold->setRange(1, 255);
741 sliderThreshold->setSingleStep(10);
742 sliderThreshold->setPrefix(
743 i18nc("Threshold in Magnetic Selection's Tool options", "Threshold: "));
744
745 KisSliderSpinBox *sliderSearchRadius = new KisSliderSpinBox;
746 sliderSearchRadius->setObjectName("frequency");
747 sliderSearchRadius->setRange(20, 200);
748 sliderSearchRadius->setSingleStep(10);
749 sliderSearchRadius->setPrefix(
750 i18nc("Search Radius in Magnetic Selection's Tool options",
751 "Search Radius: "));
752 sliderSearchRadius->setSuffix(" px");
753
754 KisSliderSpinBox *sliderAnchorGap = new KisSliderSpinBox;
755 sliderAnchorGap->setObjectName("anchorgap");
756 sliderAnchorGap->setRange(20, 200);
757 sliderAnchorGap->setSingleStep(10);
758 sliderAnchorGap->setPrefix(
759 i18nc("Anchor Gap in Magnetic Selection's Tool options",
760 "Anchor Gap: "));
761 sliderAnchorGap->setSuffix(" px");
762
763 QPushButton *buttonCompleteSelection =
764 new QPushButton(i18nc("Complete the selection", "Complete"),
765 selectionWidget);
766 buttonCompleteSelection->setEnabled(false);
767
768 QPushButton *buttonDiscardSelection =
769 new QPushButton(i18nc("Discard the selection", "Discard"),
770 selectionWidget);
771 buttonDiscardSelection->setEnabled(false);
772
773 // Set the tooltips
774 sliderRadius->setToolTip(i18nc("@info:tooltip",
775 "Radius of the filter for the detecting "
776 "edges, might take some time to calculate"));
777 sliderThreshold->setToolTip(
778 i18nc("@info:tooltip",
779 "Threshold for determining the minimum intensity of the edges"));
780 sliderSearchRadius->setToolTip(
781 i18nc("@info:tooltip", "Extra area to be searched"));
782 sliderAnchorGap->setToolTip(
783 i18nc("@info:tooltip", "Gap between 2 anchors in interactive mode"));
784 buttonCompleteSelection->setToolTip(
785 i18nc("@info:tooltip", "Complete Selection"));
786 buttonDiscardSelection->setToolTip(
787 i18nc("@info:tooltip", "Discard Selection"));
788
789 // Construct the option widget
790 KisOptionCollectionWidgetWithHeader *sectionPathOptions =
792 i18nc("The 'path options' section label in magnetic selection's "
793 "tool options",
794 "Path options"));
795 sectionPathOptions->appendWidget("sliderRadius", sliderRadius);
796 sectionPathOptions->appendWidget("sliderThreshold", sliderThreshold);
797 sectionPathOptions->appendWidget("sliderSearchRadius", sliderSearchRadius);
798 sectionPathOptions->appendWidget("sliderAnchorGap", sliderAnchorGap);
799 sectionPathOptions->appendWidget("buttonCompleteSelection",
800 buttonCompleteSelection);
801 sectionPathOptions->appendWidget("buttonDiscardSelection",
802 buttonDiscardSelection);
803 selectionWidget->appendWidget("sectionPathOptions", sectionPathOptions);
804
805 // Load configuration settings into tool options
806 m_filterRadius = m_configGroup.readEntry("filterradius", 3.0);
807 m_threshold = m_configGroup.readEntry("threshold", 100);
808 m_searchRadius = m_configGroup.readEntry("searchradius", 30);
809 m_anchorGap = m_configGroup.readEntry("anchorgap", 20);
810
811 sliderRadius->setValue(m_filterRadius);
812 sliderThreshold->setValue(m_threshold);
813 sliderSearchRadius->setValue(m_searchRadius);
814 sliderAnchorGap->setValue(m_anchorGap);
815
816 // Make connections
817 connect(sliderRadius,
818 SIGNAL(valueChanged(qreal)),
819 this,
820 SLOT(slotSetFilterRadius(qreal)));
821 connect(sliderThreshold,
822 SIGNAL(valueChanged(int)),
823 this,
824 SLOT(slotSetThreshold(int)));
825 connect(sliderSearchRadius,
826 SIGNAL(valueChanged(int)),
827 this,
828 SLOT(slotSetSearchRadius(int)));
829 connect(sliderAnchorGap,
830 SIGNAL(valueChanged(int)),
831 this,
832 SLOT(slotSetAnchorGap(int)));
833 connect(buttonCompleteSelection,
834 SIGNAL(clicked()),
835 this,
836 SLOT(requestStrokeEnd()));
837 connect(this,
838 SIGNAL(setButtonsEnabled(bool)),
839 buttonCompleteSelection,
840 SLOT(setEnabled(bool)));
841 connect(buttonDiscardSelection,
842 SIGNAL(clicked()),
843 this,
845 connect(this,
846 SIGNAL(setButtonsEnabled(bool)),
847 buttonDiscardSelection,
848 SLOT(setEnabled(bool)));
849
850 return selectionWidget;
851
852} // KisToolSelectMagnetic::createOptionWidget
853
855{
856 m_filterRadius = r;
857 m_configGroup.writeEntry("filterradius", r);
858}
859
861{
862 m_threshold = t;
863 m_configGroup.writeEntry("threshold", t);
864}
865
867{
868 m_searchRadius = r;
869 m_configGroup.writeEntry("searchradius", r);
870}
871
873{
874 m_anchorGap = g;
875 m_configGroup.writeEntry("anchorgap", g);
876}
877
879{
881 useCursor(KisCursor::loadWithSize("tool_magnetic_selection_cursor_add.svg", 32, 32, 6, 6));
882 } else if (selectionAction() == SELECTION_SUBTRACT) {
883 useCursor(KisCursor::loadWithSize("tool_magnetic_selection_cursor_sub.svg", 32, 32, 6, 6));
884 } else if (selectionAction() == SELECTION_INTERSECT) {
885 useCursor(KisCursor::loadWithSize("tool_magnetic_selection_cursor_inter.svg", 32, 32, 6, 6));
887 useCursor(KisCursor::loadWithSize("tool_magnetic_selection_cursor_symdiff.svg", 32, 32, 6, 6));
888 } else {
889 KisToolSelect::resetCursorStyle();
890 }
891}
QVector< KisImageSignalType > KisImageSignalVector
SelectionMode
@ PIXEL_SELECTION
@ SELECTION_INTERSECT
@ SELECTION_SYMMETRICDIFFERENCE
@ SELECTION_SUBTRACT
@ SELECTION_ADD
#define FEEDBACK_LINE_WIDTH
#define KoPathShapeId
Definition KoPathShape.h:20
void updateCanvas(const QRectF &rc) override
KisViewManager * viewManager() const
static QCursor loadWithSize(const QString &cursorName, int width, int height, int hotspotX=-1, int hotspotY=-1)
static QCursor waitCursor()
Definition kis_cursor.cc:54
This class is a spinbox in which you can click and drag to set the value. A slider like bar is displa...
void setValue(qreal newValue)
void setRange(qreal newMinimum, qreal newMaximum, int newNumberOfDecimals=0, bool computeNewFastSliderStep=true)
Set the minimum and the maximum values of the range.
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) override
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) override
The KisHandlePainterHelper class is a special helper for painting handles around objects....
void drawHandleRect(const QPointF &center, qreal radius)
void setHandleStyle(const KisHandleStyle &style)
static KisHandleStyle & primarySelection(KisHandlePalette palette=KisHandlePalette())
static KisHandleStyle & highlightedPrimaryHandles(KisHandlePalette palette=KisHandlePalette())
Wrapper class around a KisOptionCollectionWidget that also provide a header with a title label and an...
void appendWidget(const QString &id, QWidget *widget)
Insert the given widget with the given id at the end of the list. The list widget takes ownership of ...
void appendWidget(const QString &id, QWidget *widget)
Insert the given widget with the given id at the end of the list. The list widget takes ownership of ...
const KoColorSpace * colorSpace() const
@ FillStyleForegroundColor
void paintPainterPath(const QPainterPath &path)
void setStrokeStyle(StrokeStyle strokeStyle)
Set the current brush stroke style.
void setFillStyle(FillStyle fillStyle)
Set the current style with which to fill.
void setPaintColor(const KoColor &color)
void setAntiAliasPolygonFill(bool antiAliasPolygonFill)
Set whether a polygon's filled area should be anti-aliased or not. The default is true.
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
void addSelectionShape(KoShape *shape, SelectionAction action=SELECTION_DEFAULT)
void selectPixelSelection(KisProcessingApplicator &applicator, KisPixelSelectionSP selection, SelectionAction action)
SelectionMode tryOverrideSelectionMode(KisSelectionSP activeSelection, SelectionMode currentMode, SelectionAction currentAction) const
bool tryDeselectCurrentSelection(const QRectF selectionViewRect, SelectionAction action)
void process(KisPixelSelectionSP pixelSelection, const QRect &rect) override
This class is a spinbox in which you can click and drag to set the value. A slider like bar is displa...
void setValue(int newValue)
void setRange(int newMinimum, int newMaximum, bool computeNewFastSliderStep=true)
Set the minimum and the maximum values of the range, computing a new "fast slider step" based on the ...
KisSelectionOptions * selectionOptionWidget()
void keyReleaseEvent(QKeyEvent *event) override
void mouseMoveEvent(KoPointerEvent *event) override
SelectionAction selectionAction() const
void beginPrimaryAction(KoPointerEvent *event) override
void activate(const QSet< KoShape * > &shapes) override
SelectionMode selectionMode() const
void keyPressEvent(QKeyEvent *event) override
void continuePrimaryAction(KoPointerEvent *event) override
void endPrimaryAction(KoPointerEvent *event) override
QWidget * createOptionWidget() override
bool antiAliasSelection() const
QVector< QPoint > m_anchorPoints
void continuePrimaryAction(KoPointerEvent *event) override
int updateInitialAnchorBounds(QPoint pt)
void keyReleaseEvent(QKeyEvent *event) override
QWidget * createOptionWidget() override
QVector< QPointF > m_points
QScopedPointer< KisMagneticWorker > m_worker
void paint(QPainter &gc, const KoViewConverter &converter) override
void calculateCheckPoints(vQPointF points)
void activate(const QSet< KoShape * > &shapes) override
KisSignalCompressor m_mouseHoverCompressor
void checkIfAnchorIsSelected(QPointF pt)
void keyPressEvent(QKeyEvent *event) override
void setButtonsEnabled(bool)
QVector< vQPointF > m_pointCollection
vQPointF computeEdgeWrapper(QPoint a, QPoint b)
void mouseMoveEvent(KoPointerEvent *event) override
void endPrimaryAction(KoPointerEvent *event) override
KisToolSelectMagnetic(KoCanvasBase *canvas)
void requestStrokeCancellation() override
void beginPrimaryAction(KoPointerEvent *event) override
KisSelectionSP selection()
The position of a path point within a path shape.
Definition KoPathShape.h:63
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define bounds(x, a, b)
qreal kisDistance(const QPointF &pt1, const QPointF &pt2)
Definition kis_global.h:190
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
KUndo2MagicString kundo2_i18n(const char *text)
void accumulateBounds(const Point &pt, Rect *bounds)
rgba palette[MAX_PALETTE]
Definition palette.c:35
The LambdaCommand struct is a shorthand for creation of AggregateCommand commands using C++ lambda fe...
void setOutlineCache(const QPainterPath &cache)
void deactivate() override
Definition kis_tool.cc:131
@ PAINT_MODE
Definition kis_tool.h:300
@ HOVER_MODE
Definition kis_tool.h:299