Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMagneticWorker.cc
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2019 Kuntal Majumder <hellozee@disroot.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-only
5 */
6
7#include "KisMagneticWorker.h"
8
11#include <kis_algebra_2d.h>
12#include <kis_painter.h>
13
14#include <QtCore>
15#include <QPolygon>
16#include <QPainter>
17#include <QPainterPath>
18
19#include <boost/graph/astar_search.hpp>
20#include <krita_utils.h>
21
22#include "KisMagneticGraph.h"
23
26 typedef double data_type;
27 typedef std::pair<key_type, data_type> value_type;
28
29 explicit DistanceMap(double const &dval)
30 : m_default(dval)
31 { }
32
34 {
35 if (m.find(k) == m.end())
36 m[k] = m_default;
37 return m[k];
38 }
39
40private:
41 std::map<key_type, data_type> m;
43};
44
46 PredecessorMap() = default;
47
48 PredecessorMap(PredecessorMap const &that) = default;
49
52 typedef boost::read_write_property_map_tag category;
53
58
59 std::map<VertexDescriptor, VertexDescriptor> m_map;
60};
61
63{
64 auto found = m.m_map.find(v);
65 return found != m.m_map.end() ? found->second : v;
66}
67
72
74{
75 return std::sqrt(std::pow(p1.y - p2.y, 2) + std::pow(p1.x - p2.x, 2));
76}
77
78class AStarHeuristic : public boost::astar_heuristic<KisMagneticGraph, double>
79{
80private:
82
83public:
85 m_goal(goal)
86 { }
87
89 {
90 return EuclideanDistance(v, m_goal);
91 }
92};
93
94struct GoalFound { };
95
96class AStarGoalVisitor : public boost::default_astar_visitor
97{
98public:
99 explicit AStarGoalVisitor(VertexDescriptor goal) : m_goal(goal){ }
100
102 {
103 Q_UNUSED(g);
104 if (u == m_goal) {
105 throw GoalFound();
106 }
107 }
108
109private:
111};
112
113struct WeightMap {
114 typedef std::pair<VertexDescriptor, VertexDescriptor> key_type;
115 typedef double data_type;
116 typedef std::pair<key_type, data_type> value_type;
117
118 WeightMap() = default;
119
120 explicit WeightMap(const KisMagneticGraph &g) :
121 m_graph(g)
122 { }
123
125 {
126 if (m_map.find(k) == m_map.end()) {
127 double edge_gradient = (m_graph.getIntensity(k.first) + m_graph.getIntensity(k.second)) / 2.0;
128 m_map[k] = EuclideanDistance(k.first, k.second) + 255.0 - edge_gradient;
129 }
130 return m_map[k];
131 }
132
133private:
134 std::map<key_type, data_type> m_map;
136};
137
139{
141 QSize s = dev->defaultBounds()->bounds().size();
143 m_tilesPerRow = (int) std::ceil((double) s.width() / (double) m_tileSize.width());
144 int tilesPerColumn = (int) std::ceil((double) s.height() / (double) m_tileSize.height());
146
147 for (int i = 0; i < tilesPerColumn; i++) {
148 for (int j = 0; j < m_tilesPerRow; j++) {
149 int width = std::min(s.width() - j * m_tileSize.width(), m_tileSize.width());
150 int height = std::min(s.height() - i * m_tileSize.height(), m_tileSize.height());
151 QRect temp(j *m_tileSize.width(), i *m_tileSize.height(), width, height);
152 m_tiles.push_back(temp);
153 }
154 }
156}
157
158void KisMagneticLazyTiles::filter(qreal radius, QRect &rect)
159{
160 auto divide = [](QPoint p, QSize s){
161 return QPoint(p.x() / s.width(), p.y() / s.height());
162 };
163
164 QPoint firstTile = divide(rect.topLeft(), m_tileSize);
165 QPoint lastTile = divide(rect.bottomRight(), m_tileSize);
166 for (int i = firstTile.y(); i <= lastTile.y(); i++) {
167 for (int j = firstTile.x(); j <= lastTile.x(); j++) {
168 int currentTile = i * m_tilesPerRow + j;
169 if (currentTile < m_tiles.size()
170 && currentTile < m_radiusRecord.size()
171 && radius != m_radiusRecord[currentTile]) {
172 QRect bounds = m_tiles[currentTile];
173 KisGaussianKernel::applyTightLoG(m_dev, bounds, radius, -1.0, QBitArray(), nullptr);
175 m_radiusRecord[currentTile] = radius;
176 }
177 }
178 }
179}
180
182 m_lazyTileFilter(dev)
183{ }
184
185QVector<QPointF> KisMagneticWorker::computeEdge(int bounds, QPoint begin, QPoint end, qreal radius)
186{
187 QRect rect;
191
192 VertexDescriptor goal(end);
193 VertexDescriptor start(begin);
194
196
197 // How many maps does it require?
198 // 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
199 PredecessorMap pmap;
200 DistanceMap dmap(std::numeric_limits<double>::max());
201 dmap[start] = 0;
202 std::map<VertexDescriptor, double> rmap;
203 std::map<VertexDescriptor, boost::default_color_type> cmap;
204 std::map<VertexDescriptor, double> imap;
205 WeightMap wmap(*m_graph);
206 AStarHeuristic heuristic(goal);
207 QVector<QPointF> result;
208
209 try {
210 boost::astar_search_no_init(
211 *m_graph, start, heuristic,
212 boost::visitor(AStarGoalVisitor(goal))
213 .distance_map(boost::associative_property_map<DistanceMap>(dmap))
214 .predecessor_map(boost::ref(pmap))
215 .weight_map(boost::associative_property_map<WeightMap>(wmap))
216 .vertex_index_map(boost::associative_property_map<std::map<VertexDescriptor, double> >(imap))
217 .rank_map(boost::associative_property_map<std::map<VertexDescriptor, double> >(rmap))
218 .color_map(boost::associative_property_map<std::map<VertexDescriptor, boost::default_color_type> >
219 (cmap))
220 .distance_combine(std::plus<double>())
221 .distance_compare(std::less<double>())
222 );
223 } catch (GoalFound const &) {
224 for (VertexDescriptor u = goal; u != start; u = pmap[u]) {
225 result.push_front(QPointF(u.x, u.y));
226 }
227 }
228
229 result.push_front(QPoint(start.x, start.y));
230
231 return result;
232} // KisMagneticWorker::computeEdge
233
235{
237}
238
240{
242
243 const QPointF offset = m_lazyTileFilter.device()->exactBounds().topLeft();
244 for (QPointF &pt : points) {
245 pt -= offset;
246 }
247
248 img.convertTo(QImage::Format_ARGB32);
249 QPainter gc(&img);
250
251 QPainterPath path;
252
253 for (int i = 0; i < points.size(); i++) {
254 if (i == 0) {
255 path.moveTo(points[i]);
256 } else {
257 path.lineTo(points[i]);
258 }
259 }
260
261 gc.setPen(Qt::blue);
262 gc.drawPath(path);
263
264 gc.setPen(Qt::green);
265 gc.drawEllipse(points[0], 3, 3);
266 gc.setPen(Qt::red);
267 gc.drawEllipse(points[points.count() - 1], 2, 2);
268
269 for (QRect &r : m_lazyTileFilter.tiles() ) {
270 gc.drawRect(r);
271 }
272
273 img.save("result.png");
274} // KisMagneticWorker::saveTheImage
float value(const T *src, size_t ch)
const Params2D p
qreal v
qreal u
QPointF p2
QPointF p1
VertexDescriptor get(PredecessorMap const &m, VertexDescriptor v)
void put(PredecessorMap &m, VertexDescriptor key, VertexDescriptor value)
double EuclideanDistance(VertexDescriptor p1, VertexDescriptor p2)
AStarGoalVisitor(VertexDescriptor goal)
void examine_vertex(VertexDescriptor u, KisMagneticGraph const &g)
VertexDescriptor m_goal
VertexDescriptor m_goal
double operator()(VertexDescriptor v)
AStarHeuristic(VertexDescriptor goal)
virtual QRect bounds() const =0
static void applyTightLoG(KisPaintDeviceSP device, const QRect &rect, qreal radius, qreal coeff, const QBitArray &channelFlags, KoUpdater *progressUpdater)
KisPaintDeviceSP device()
QVector< QRect > m_tiles
QVector< QRect > tiles()
void filter(qreal radius, QRect &rect)
KisPaintDeviceSP m_dev
QVector< qreal > m_radiusRecord
KisMagneticLazyTiles(KisPaintDeviceSP dev)
KisMagneticGraph * m_graph
QVector< QPointF > computeEdge(int bounds, QPoint start, QPoint end, qreal radius)
qreal intensity(QPoint pt)
KisMagneticLazyTiles m_lazyTileFilter
KisMagneticWorker(const KisPaintDeviceSP &dev)
void saveTheImage(vQPointF points)
void setDefaultBounds(KisDefaultBoundsBaseSP bounds)
QRect exactBounds() const
QImage convertToQImage(const KoColorProfile *dstProfile, qint32 x, qint32 y, qint32 w, qint32 h, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags()) const
KisDefaultBoundsBaseSP defaultBounds() const
static KisPaintDeviceSP convertToAlphaAsGray(KisPaintDeviceSP src)
#define bounds(x, a, b)
T kisGrowRect(const T &rect, U offset)
Definition kis_global.h:186
void accumulateBounds(const Point &pt, Rect *bounds)
void normalizeAlpha8Device(KisPaintDeviceSP dev, const QRect &rect)
QSize optimalPatchSize()
std::pair< key_type, data_type > value_type
DistanceMap(double const &dval)
VertexDescriptor key_type
data_type const m_default
std::map< key_type, data_type > m
data_type & operator[](key_type const &k)
quint8 getIntensity(VertexDescriptor pt)
PredecessorMap()=default
VertexDescriptor key_type
std::map< VertexDescriptor, VertexDescriptor > m_map
boost::read_write_property_map_tag category
PredecessorMap(PredecessorMap const &that)=default
VertexDescriptor value_type
VertexDescriptor & operator[](VertexDescriptor v)
std::pair< key_type, data_type > value_type
std::map< key_type, data_type > m_map
WeightMap()=default
std::pair< VertexDescriptor, VertexDescriptor > key_type
WeightMap(const KisMagneticGraph &g)
KisMagneticGraph m_graph
data_type & operator[](key_type const &k)