diff --git a/plugins/tools/selectiontools/KisMagneticWorker.cc b/plugins/tools/selectiontools/KisMagneticWorker.cc index e0159aea10..ed4c987052 100644 --- a/plugins/tools/selectiontools/KisMagneticWorker.cc +++ b/plugins/tools/selectiontools/KisMagneticWorker.cc @@ -1,276 +1,279 @@ /* * 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 #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) + 255.0 - edge_gradient; } return m_map[k]; } private: std::map m_map; KisMagneticGraph m_graph; }; KisMagneticLazyTiles::KisMagneticLazyTiles(KisPaintDeviceSP dev) { m_dev = KisPainter::convertToAlphaAsGray(dev); QSize s = m_dev->exactBounds().size(); m_tileSize = KritaUtils::optimalPatchSize(); m_tilesPerRow = std::ceil((double) s.width() / (double) m_tileSize.width()); int tilesPerColumn = std::ceil((double) s.height() / (double) m_tileSize.height()); m_dev->setDefaultBounds(dev->defaultBounds()); for (int i = 0; i < tilesPerColumn; i++) { for (int j = 0; j < m_tilesPerRow; j++) { int width = std::min(m_dev->exactBounds().width() - j * m_tileSize.width(), m_tileSize.width()); int height = std::min(m_dev->exactBounds().height() - i * m_tileSize.height(), m_tileSize.height()); QRect temp(j *m_tileSize.width(), i *m_tileSize.height(), width, height); m_tiles.push_back(temp); } } m_radiusRecord = QVector(m_tiles.size(), -1); } void KisMagneticLazyTiles::filter(qreal radius, QRect &rect) { auto divide = [](QPoint p, QSize s){ return QPoint(p.x() / s.width(), p.y() / s.height()); }; QPoint firstTile = divide(rect.topLeft(), m_tileSize); QPoint lastTile = divide(rect.bottomRight(), m_tileSize); - for (int i = firstTile.y(); i <= lastTile.y(); i++) { for (int j = firstTile.x(); j <= lastTile.x(); j++) { int currentTile = i * m_tilesPerRow + j; - if (qFuzzyCompare(radius, m_radiusRecord[currentTile])) { + if (radius != m_radiusRecord[currentTile]) { QRect bounds = m_tiles[currentTile]; KisGaussianKernel::applyTightLoG(m_dev, bounds, radius, -1.0, QBitArray(), nullptr); KisLazyFillTools::normalizeAlpha8Device(m_dev, bounds); m_radiusRecord[currentTile] = radius; } } } } KisMagneticWorker::KisMagneticWorker(const KisPaintDeviceSP &dev) : m_lazyTileFilter(dev) { } QVector KisMagneticWorker::computeEdge(int bounds, QPoint begin, QPoint end, qreal radius) { QRect rect; KisAlgebra2D::accumulateBounds(QVector { begin, end }, &rect); rect = kisGrowRect(rect, bounds); m_lazyTileFilter.filter(radius, rect); VertexDescriptor goal(end); VertexDescriptor start(begin); KisMagneticGraph m_graph(m_lazyTileFilter.device(), 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(m_graph); AStarHeuristic heuristic(goal); QVector result; try { boost::astar_search_no_init( 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; } // KisMagneticWorker::computeEdge void KisMagneticWorker::saveTheImage(vQPointF points) { QImage img = m_lazyTileFilter.device()->convertToQImage(nullptr, m_lazyTileFilter.device()->exactBounds()); const QPointF offset = m_lazyTileFilter.device()->exactBounds().topLeft(); for (QPointF &pt : points) { pt -= offset; } img = img.convertToFormat(QImage::Format_ARGB32); QPainter gc(&img); QPainterPath path; for (int i = 0; i < points.size(); i++) { if (i == 0) { path.moveTo(points[i]); } else { path.lineTo(points[i]); } } gc.setPen(Qt::blue); gc.drawPath(path); gc.setPen(Qt::green); gc.drawEllipse(points[0], 3, 3); gc.setPen(Qt::red); gc.drawEllipse(points[points.count() - 1], 2, 2); + for(QRect &r : m_lazyTileFilter.tiles() ){ + gc.drawRect(r); + } + img.save("result.png"); } // KisMagneticWorker::saveTheImage diff --git a/plugins/tools/selectiontools/KisMagneticWorker.h b/plugins/tools/selectiontools/KisMagneticWorker.h index d39bbf36be..887e4db4a2 100644 --- a/plugins/tools/selectiontools/KisMagneticWorker.h +++ b/plugins/tools/selectiontools/KisMagneticWorker.h @@ -1,49 +1,50 @@ /* * 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 KisMagneticLazyTiles{ private: QVector m_tiles; QVector m_radiusRecord; KisPaintDeviceSP m_dev; QSize m_tileSize; int m_tilesPerRow; public: KisMagneticLazyTiles(KisPaintDeviceSP dev); void filter(qreal radius, QRect &rect); inline KisPaintDeviceSP device() { return m_dev; } + inline QVector tiles() { return m_tiles; } }; class KRITASELECTIONTOOLS_EXPORT KisMagneticWorker { public: KisMagneticWorker(const KisPaintDeviceSP &dev); QVector computeEdge(int bounds, QPoint start, QPoint end, qreal radius); void saveTheImage(vQPointF points); private: KisMagneticLazyTiles m_lazyTileFilter; }; #endif // ifndef KISMAGNETICWORKER_H