diff --git a/plugins/tools/selectiontools/KisMagneticGraph.cc b/plugins/tools/selectiontools/KisMagneticGraph.cc deleted file mode 100644 index 3f5bf5e3fb..0000000000 --- a/plugins/tools/selectiontools/KisMagneticGraph.cc +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2019 Kuntal Majumder - * - * This library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "KisMagneticGraph.h" diff --git a/plugins/tools/selectiontools/KisMagneticGraph.h b/plugins/tools/selectiontools/KisMagneticGraph.h index 2e1d156482..46b2b86cf8 100644 --- a/plugins/tools/selectiontools/KisMagneticGraph.h +++ b/plugins/tools/selectiontools/KisMagneticGraph.h @@ -1,256 +1,263 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISMAGNETICGRAPH_H #define KISMAGNETICGRAPH_H #include #include #include #include #include #include #include struct VertexDescriptor { long x,y; enum Direction { MIN = 0, N = MIN, S, E, W, NW, NE, SW, SE, NONE }; VertexDescriptor(long _x, long _y): x(_x), y(_y) { } VertexDescriptor(QPoint pt): x(pt.x()), y(pt.y()) { } VertexDescriptor(): x(0), y(0) { } bool operator==(VertexDescriptor const &rhs) const { return rhs.x == x && rhs.y == y; } bool operator==(QPoint const &rhs) const { return rhs.x() == x && rhs.y() == y; } bool operator !=(VertexDescriptor const &rhs) const { return rhs.x != x || rhs.y != y; } bool operator<(VertexDescriptor const &rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); } // returns one of the 8 neighboring pixel based on the direction // it gives out multiple warnings, but I am lazy, sorry VertexDescriptor neighbor(Direction direction) const { int dx = 0, dy = 0; switch (direction){ case W: case SW: case NW: dx = -1; break; case E: case SE: case NE: dx = 1; } switch(direction){ case N: case NW: case NE: dy = -1; break; case S: case SW: case SE: dy = 1; } VertexDescriptor const neighbor(x + dx, y + dy); return neighbor; } }; QDebug operator<<(QDebug dbg, const VertexDescriptor &v) { dbg.nospace() << "(" << v.x << ", " << v.y << ")"; return dbg.space(); } struct neighbour_iterator; struct KisMagneticGraph{ typedef KisMagneticGraph type; KisMagneticGraph() { } + + KisMagneticGraph(KisPaintDeviceSP dev): + m_dev(dev) + { + m_randAccess = m_dev->createRandomAccessorNG(m_dev->exactBounds().x(), m_dev->exactBounds().y()); + } + KisMagneticGraph(KisPaintDeviceSP dev, QRect graphRect): m_rect(graphRect), m_dev(dev) { m_randAccess = m_dev->createRandomAccessorNG(m_dev->exactBounds().x(), m_dev->exactBounds().y()); } typedef VertexDescriptor vertex_descriptor; typedef std::pair edge_descriptor; typedef boost::undirected_tag directed_category; typedef boost::disallow_parallel_edge_tag edge_parallel_category; typedef boost::incidence_graph_tag traversal_category; typedef neighbour_iterator out_edge_iterator; typedef unsigned degree_size_type; quint8 getIntensity(VertexDescriptor pt) { m_randAccess->moveTo(pt.x, pt.y); quint8 val = *(m_randAccess->rawData()); return val; } unsigned outDegree(VertexDescriptor pt){ //corners if(pt == m_rect.topLeft() || pt == m_rect.topRight() || pt == m_rect.bottomLeft() || pt == m_rect.bottomRight()){ if(m_rect.width() == 1 || m_rect.height() == 1) return 1; return 3; } //edges if(pt.x == m_rect.topLeft().x() || pt.y == m_rect.topLeft().y() || pt.x == m_rect.bottomRight().x() || pt.y == m_rect.bottomRight().y()){ if(m_rect.width() == 1 || m_rect.height() == 1) return 2; return 5; } return 8; } QRect m_rect; private: KisPaintDeviceSP m_dev; KisRandomAccessorSP m_randAccess; }; struct neighbour_iterator : public boost::iterator_facade, boost::forward_traversal_tag, std::pair> { neighbour_iterator(VertexDescriptor v, KisMagneticGraph g, VertexDescriptor::Direction d): m_point(v), m_direction(d), m_graph(g) { } neighbour_iterator() { } std::pair operator*() const { std::pair const result = std::make_pair(m_point, m_point.neighbor(m_direction)); return result; } void operator++() { m_direction = static_cast(int(m_direction)+1); VertexDescriptor next = m_point.neighbor(m_direction); if(m_direction == VertexDescriptor::NONE){ return; } if(!m_graph.m_rect.contains(next.x, next.y)){ operator++(); } } bool operator==(neighbour_iterator const& that) const { return m_point == that.m_point && m_direction == that.m_direction; } bool equal(neighbour_iterator const& that) const { return operator==(that); } void increment() { operator++(); } private: VertexDescriptor m_point; VertexDescriptor::Direction m_direction; KisMagneticGraph m_graph; }; // Requirements for an Incidence Graph, // https://www.boost.org/doc/libs/1_70_0/libs/graph/doc/IncidenceGraph.html namespace boost{ template<> struct graph_traits { typedef typename KisMagneticGraph::vertex_descriptor vertex_descriptor; typedef typename KisMagneticGraph::edge_descriptor edge_descriptor; typedef typename KisMagneticGraph::out_edge_iterator out_edge_iterator; typedef typename KisMagneticGraph::directed_category directed_category; typedef typename KisMagneticGraph::edge_parallel_category edge_parallel_category; typedef typename KisMagneticGraph::traversal_category traversal_category; typedef typename KisMagneticGraph::degree_size_type degree_size_type; typedef void in_edge_iterator; typedef void vertex_iterator; typedef void vertices_size_type; typedef void edge_iterator; typedef void edges_size_type; }; } typename KisMagneticGraph::vertex_descriptor source(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g) { Q_UNUSED(g) return e.first; } typename KisMagneticGraph::vertex_descriptor target(typename KisMagneticGraph::edge_descriptor e, KisMagneticGraph g) { Q_UNUSED(g) return e.second; } std::pair out_edges(typename KisMagneticGraph::vertex_descriptor v, KisMagneticGraph g) { return std::make_pair( KisMagneticGraph::out_edge_iterator(v, g, VertexDescriptor::Direction::MIN), KisMagneticGraph::out_edge_iterator(v, g, VertexDescriptor::Direction::NONE) ); } typename KisMagneticGraph::degree_size_type out_degree(typename KisMagneticGraph::vertex_descriptor v, KisMagneticGraph g) { return g.outDegree(v); } #endif diff --git a/plugins/tools/selectiontools/KisMagneticWorker.cc b/plugins/tools/selectiontools/KisMagneticWorker.cc index 12c4ae44c7..a960b73438 100644 --- a/plugins/tools/selectiontools/KisMagneticWorker.cc +++ b/plugins/tools/selectiontools/KisMagneticWorker.cc @@ -1,191 +1,197 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisMagneticWorker.h" #include #include #include #include #include #include #include #include "KisMagneticGraph.h" struct DistanceMap { typedef VertexDescriptor key_type; typedef double data_type; typedef std::pair value_type; explicit DistanceMap(double const& dval) : m_default(dval) { } data_type &operator[](key_type const& k) { if (m.find(k) == m.end()) m[k] = m_default; return m[k]; } private: std::map m; data_type const m_default; }; struct PredecessorMap{ PredecessorMap() = default; PredecessorMap(PredecessorMap const& that) = default; typedef VertexDescriptor key_type; typedef VertexDescriptor value_type; typedef boost::read_write_property_map_tag category; VertexDescriptor &operator[](VertexDescriptor v){ return m_map[v]; } std::map m_map; }; VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v){ auto found = m.m_map.find(v); return found != m.m_map.end() ? found->second : v; } void put(PredecessorMap &m, VertexDescriptor key, VertexDescriptor value){ m.m_map[key] = value; } double EuclideanDistance(VertexDescriptor p1, VertexDescriptor p2){ return std::sqrt(std::pow(p1.y-p2.y, 2) + std::pow(p1.x-p2.x, 2)); } class AStarHeuristic : public boost::astar_heuristic { private: VertexDescriptor m_goal; public: explicit AStarHeuristic(VertexDescriptor goal): m_goal(goal) { } double operator()(VertexDescriptor v) { return EuclideanDistance(v,m_goal); } }; struct GoalFound {}; class AStarGoalVisitor : public boost::default_astar_visitor { public: explicit AStarGoalVisitor(VertexDescriptor goal) : m_goal(goal) { } void examine_vertex(VertexDescriptor u, KisMagneticGraph const &g) { Q_UNUSED(g) if(u == m_goal){ throw GoalFound(); } } private: VertexDescriptor m_goal; }; struct WeightMap{ typedef std::pair key_type; typedef double data_type; typedef std::pair value_type; WeightMap() = default; explicit WeightMap(const KisMagneticGraph &g): m_graph(g) { } data_type& operator[](key_type const& k) { if (m_map.find(k) == m_map.end()) { double edge_gradient = (m_graph.getIntensity(k.first) + m_graph.getIntensity(k.second))/2; m_map[k] = EuclideanDistance(k.first, k.second) * (edge_gradient + 1); } return m_map[k]; } private: std::map m_map; KisMagneticGraph m_graph; }; KisMagneticWorker::KisMagneticWorker(const KisPaintDeviceSP& dev) { KisPaintDevice *tempDevice = new KisPaintDevice(dev->colorSpace()); - m_dev = KisPaintDeviceSP(tempDevice); + KisPaintDeviceSP m_dev = KisPaintDeviceSP(tempDevice); KisPainter::copyAreaOptimized(dev->exactBounds().topLeft(), dev, m_dev, dev->exactBounds()); KisGaussianKernel::applyLoG(m_dev, m_dev->exactBounds(), 2, 1.0, QBitArray(), nullptr); KisLazyFillTools::normalizeAndInvertAlpha8Device(m_dev, m_dev->exactBounds()); + m_graph = new KisMagneticGraph(m_dev); } QVector KisMagneticWorker::computeEdge(int radius, QPoint begin, QPoint end) { QRect rect(QPoint(0,0), QSize(radius*2, radius*2)); rect.moveCenter(begin); KisAlgebra2D::accumulateBounds(end, &rect); VertexDescriptor goal(end); VertexDescriptor start(begin); - KisMagneticGraph g(m_dev, rect); + m_graph->m_rect = rect; // How many maps does it require? // Take a look here, if it doesn't make sense, https://www.boost.org/doc/libs/1_70_0/libs/graph/doc/astar_search.html PredecessorMap pmap; DistanceMap dmap(std::numeric_limits::max()); dmap[start] = 0; std::map rmap; std::map cmap; std::map imap; - WeightMap wmap(g); + WeightMap wmap(*m_graph); AStarHeuristic heuristic(goal); QVector result; try{ boost::astar_search_no_init( - g, start, heuristic + *m_graph, start, heuristic ,boost::visitor(AStarGoalVisitor(goal)) .distance_map(boost::associative_property_map(dmap)) .predecessor_map(boost::ref(pmap)) .weight_map(boost::associative_property_map(wmap)) .vertex_index_map(boost::associative_property_map>(imap)) .rank_map(boost::associative_property_map>(rmap)) .color_map(boost::associative_property_map>(cmap)) .distance_combine(std::plus()) .distance_compare(std::less()) ); }catch(GoalFound const&){ for(VertexDescriptor u=goal; u!=start; u = pmap[u]){ result.push_front(QPointF(u.x,u.y)); } } result.push_front(QPoint(start.x,start.y)); return result; } + +quint8 KisMagneticWorker::intensity(QPoint pt) +{ + return m_graph->getIntensity(VertexDescriptor(pt)); +} diff --git a/plugins/tools/selectiontools/KisMagneticWorker.h b/plugins/tools/selectiontools/KisMagneticWorker.h index 39db34e5fc..17497b37d4 100644 --- a/plugins/tools/selectiontools/KisMagneticWorker.h +++ b/plugins/tools/selectiontools/KisMagneticWorker.h @@ -1,36 +1,41 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISMAGNETICWORKER_H #define KISMAGNETICWORKER_H #include #include +class KisMagneticGraph; + class KRITASELECTIONTOOLS_EXPORT KisMagneticWorker{ public: KisMagneticWorker() { //Do not use this, just for making the compiler happy } KisMagneticWorker(const KisPaintDeviceSP &dev); + QVector computeEdge(int radius, QPoint start, QPoint end); + quint8 intensity(QPoint pt); + private: - KisPaintDeviceSP m_dev; + KisMagneticGraph *m_graph; }; #endif diff --git a/plugins/tools/selectiontools/KisToolSelectMagnetic.cc b/plugins/tools/selectiontools/KisToolSelectMagnetic.cc index d027047679..4dfc1a85c5 100644 --- a/plugins/tools/selectiontools/KisToolSelectMagnetic.cc +++ b/plugins/tools/selectiontools/KisToolSelectMagnetic.cc @@ -1,292 +1,301 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisToolSelectMagnetic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_painter.h" #include #include "canvas/kis_canvas2.h" #include "kis_pixel_selection.h" #include "kis_selection_tool_helper.h" #include "kis_algebra_2d.h" #define FEEDBACK_LINE_WIDTH 2 KisToolSelectMagnetic::KisToolSelectMagnetic(KoCanvasBase *canvas) : KisToolSelect(canvas, KisCursor::load("tool_magnetic_selection_cursor.svg", 16, 16), i18n("Magnetic Selection")), - m_continuedMode(false), m_complete(true), m_checkPoint(0), m_radius(20) + m_continuedMode(false), m_complete(true), m_radius(20), m_threshold(220), m_checkPoint(-1) { } void KisToolSelectMagnetic::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control) { m_continuedMode = true; } KisToolSelect::keyPressEvent(event); } void KisToolSelectMagnetic::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Control || !(event->modifiers() & Qt::ControlModifier)) { m_continuedMode = false; if (mode() != PAINT_MODE && !m_points.isEmpty()) { finishSelectionAction(); } } KisToolSelect::keyReleaseEvent(event); } //the cursor is still tracked even when no mousebutton is pressed void KisToolSelectMagnetic::mouseMoveEvent(KoPointerEvent *event) { KisToolSelect::mouseMoveEvent(event); if(m_complete) return; - m_lastCursorPos = convertToPixelCoord(event); QPoint current((int)m_lastCursorPos.x(), (int)m_lastCursorPos.y()); - QVector point_set = m_worker.computeEdge(m_radius, m_lastAnchor, current); - int num = m_points.count(); - m_points.resize(m_radius * (num/m_radius)); - m_points.append(point_set); - num = m_points.count(); - - if(num/m_radius > 0) - m_checkPoint = -1; - - while(num/m_radius > 0){ - m_checkPoint += m_radius; - m_lastAnchor = QPoint((int)m_points[m_checkPoint].x(), (int)m_points[m_checkPoint].y()); - m_anchorPoints.push_back(m_lastAnchor); - num -= m_radius; + vQPointF pointSet = m_worker.computeEdge(m_radius, m_lastAnchor, current); + m_points.resize(m_checkPoint+1); + m_points.append(pointSet); + for(int i=m_points.count()-1; i>= m_checkPoint; i--){ + QPoint pointInQuestion(m_points[i].x(), m_points[i].y()); + if(m_worker.intensity(pointInQuestion) >= m_threshold){ + m_checkPoint = i; + m_lastAnchor = pointInQuestion; + break; + } } m_paintPath = QPainterPath(); m_paintPath.moveTo(pixelToView(m_points[0])); for(int i=1; i 0 && m_snapBound.contains(m_lastAnchor)){ m_complete = true; finishSelectionAction(); return; } - m_complete = false; m_anchorPoints.push_back(m_lastAnchor); + m_complete = false; } //drag while primary mouse button is pressed void KisToolSelectMagnetic::continuePrimaryAction(KoPointerEvent *event) { KisToolSelectBase::continuePrimaryAction(event); } //release primary mouse button void KisToolSelectMagnetic::endPrimaryAction(KoPointerEvent *event) { KisToolSelectBase::endPrimaryAction(event); } void KisToolSelectMagnetic::finishSelectionAction() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); kisCanvas->updateCanvas(); setMode(KisTool::HOVER_MODE); QRectF boundingViewRect = pixelToView(KisAlgebra2D::accumulateBounds(m_points)); KisSelectionToolHelper helper(kisCanvas, kundo2_i18n("Select by Outline")); if (m_points.count() > 2 && !helper.tryDeselectCurrentSelection(boundingViewRect, selectionAction())) { QApplication::setOverrideCursor(KisCursor::waitCursor()); const SelectionMode mode = helper.tryOverrideSelectionMode(kisCanvas->viewManager()->selection(), selectionMode(), selectionAction()); if (mode == PIXEL_SELECTION) { KisPixelSelectionSP tmpSel = KisPixelSelectionSP(new KisPixelSelection()); KisPainter painter(tmpSel); painter.setPaintColor(KoColor(Qt::black, tmpSel->colorSpace())); painter.setAntiAliasPolygonFill(antiAliasSelection()); painter.setFillStyle(KisPainter::FillStyleForegroundColor); painter.setStrokeStyle(KisPainter::StrokeStyleNone); painter.paintPolygon(m_points); QPainterPath cache; cache.addPolygon(m_points); cache.closeSubpath(); tmpSel->setOutlineCache(cache); helper.selectPixelSelection(tmpSel, selectionAction()); } else { KoPathShape* path = new KoPathShape(); path->setShapeId(KoPathShapeId); QTransform resolutionMatrix; resolutionMatrix.scale(1 / currentImage()->xRes(), 1 / currentImage()->yRes()); path->moveTo(resolutionMatrix.map(m_points[0])); for (int i = 1; i < m_points.count(); i++) path->lineTo(resolutionMatrix.map(m_points[i])); path->close(); path->normalize(); helper.addSelectionShape(path, selectionAction()); } QApplication::restoreOverrideCursor(); } m_points.clear(); m_anchorPoints.clear(); m_paintPath = QPainterPath(); } void KisToolSelectMagnetic::paint(QPainter& gc, const KoViewConverter &converter) { Q_UNUSED(converter); if ((mode() == KisTool::PAINT_MODE || m_continuedMode) && !m_points.isEmpty()) { QPainterPath outline = m_paintPath; if (m_continuedMode && mode() != KisTool::PAINT_MODE) { outline.lineTo(pixelToView(m_lastCursorPos)); } paintToolOutline(&gc, outline); Q_FOREACH(const QPoint pt, m_anchorPoints){ - QRect tempRect(QPoint(0,0),QSize(2, 2)); + QRect tempRect(QPoint(0,0),QSize(1, 1)); tempRect.moveTo(pt); gc.drawRect(pixelToView(tempRect)); } } } void KisToolSelectMagnetic::updateFeedback() { if (m_points.count() > 1) { qint32 lastPointIndex = m_points.count() - 1; QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_points[lastPointIndex]).normalized(); updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH); updateCanvasPixelRect(updateRect); } } void KisToolSelectMagnetic::updateContinuedMode() { if (!m_points.isEmpty()) { qint32 lastPointIndex = m_points.count() - 1; QRectF updateRect = QRectF(m_points[lastPointIndex - 1], m_lastCursorPos).normalized(); updateRect = kisGrowRect(updateRect, FEEDBACK_LINE_WIDTH); updateCanvasPixelRect(updateRect); } } void KisToolSelectMagnetic::activate(KoToolBase::ToolActivation activation, const QSet &shapes) { m_worker = KisMagneticWorker(image()->projection()); KisToolSelect::activate(activation, shapes); } void KisToolSelectMagnetic::deactivate() { KisCanvas2 * kisCanvas = dynamic_cast(canvas()); KIS_ASSERT_RECOVER_RETURN(kisCanvas); kisCanvas->updateCanvas(); m_continuedMode = false; m_complete = true; KisTool::deactivate(); } +void KisToolSelectMagnetic::requestUndoDuringStroke() +{ + if(m_complete) return; + m_anchorPoints.pop_back(); + m_lastAnchor = m_anchorPoints.last(); + for(int i=m_checkPoint; i>=0; i--){ + if(m_points[i] == m_lastAnchor){ + m_checkPoint = i; + break; + } + } +} + void KisToolSelectMagnetic::resetCursorStyle() { if (selectionAction() == SELECTION_ADD) { useCursor(KisCursor::load("tool_outline_selection_cursor_add.png", 6, 6)); } else if (selectionAction() == SELECTION_SUBTRACT) { useCursor(KisCursor::load("tool_outline_selection_cursor_sub.png", 6, 6)); } else { KisToolSelect::resetCursorStyle(); } } diff --git a/plugins/tools/selectiontools/KisToolSelectMagnetic.h b/plugins/tools/selectiontools/KisToolSelectMagnetic.h index e937404a58..3307df3ac2 100644 --- a/plugins/tools/selectiontools/KisToolSelectMagnetic.h +++ b/plugins/tools/selectiontools/KisToolSelectMagnetic.h @@ -1,97 +1,98 @@ /* * Copyright (c) 2019 Kuntal Majumder * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KIS_TOOL_SELECT_MAGNETIC_H_ #define KIS_TOOL_SELECT_MAGNETIC_H_ #include #include "KisSelectionToolFactoryBase.h" #include #include #include "KisMagneticWorker.h" class QPainterPath; class KisToolSelectMagnetic : public KisToolSelect { Q_OBJECT public: KisToolSelectMagnetic(KoCanvasBase *canvas); ~KisToolSelectMagnetic() override = default; void beginPrimaryAction(KoPointerEvent *event) override; void continuePrimaryAction(KoPointerEvent *event) override; void endPrimaryAction(KoPointerEvent *event) override; void paint(QPainter& gc, const KoViewConverter &converter) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; void mouseMoveEvent(KoPointerEvent *event) override; void resetCursorStyle(); public Q_SLOTS: void deactivate() override; void activate(KoToolBase::ToolActivation activation, const QSet &shapes) override; + void requestUndoDuringStroke() override; protected: using KisToolSelectBase::m_widgetHelper; private: void finishSelectionAction(); void updateFeedback(); void updateContinuedMode(); void updateCanvas(); QPainterPath m_paintPath; QVector m_points; QVector m_anchorPoints; bool m_continuedMode; QPointF m_lastCursorPos; QPoint m_lastAnchor; bool m_complete; KisMagneticWorker m_worker; - uint m_checkPoint; - uint m_radius; + uint m_radius, m_threshold; + int m_checkPoint; QRectF m_snapBound; }; class KisToolSelectMagneticFactory : public KisSelectionToolFactoryBase { public: KisToolSelectMagneticFactory() : KisSelectionToolFactoryBase("KisToolSelectMagnetic") { setToolTip(i18n("Magnetic Selection Tool")); setSection(TOOL_TYPE_SELECTION); setIconName(koIconNameCStr("tool_magnetic_selection")); setPriority(8); setActivationShapeId(KRITA_TOOL_ACTIVATION_ID); } ~KisToolSelectMagneticFactory() override {} KoToolBase * createTool(KoCanvasBase *canvas) override { return new KisToolSelectMagnetic(canvas); } }; #endif //__selecttoolmagnetic_h__