Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
homogeneoussplines.h
Go to the documentation of this file.
1/* This file is part of the libdepixelize project
2 Copyright (C) 2013 Vinícius dos Santos Oliveira <vini.ipsmaker@gmail.com>
3
4 GNU Lesser General Public License Usage
5 This library is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Lesser General Public License as published by the
7 Free Software Foundation; either version 2.1 of the License, or (at your
8 option) any later version.
9 You should have received a copy of the GNU Lesser General Public License
10 along with this library. If not, see <http://www.gnu.org/licenses/>.
11
12 GNU General Public License Usage
13 Alternatively, this library may be used under the terms of the GNU General
14 Public License as published by the Free Software Foundation, either version
15 2 of the License, or (at your option) any later version.
16 You should have received a copy of the GNU General Public License along with
17 this library. If not, see <http://www.gnu.org/licenses/>.
18
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Lesser General Public License for more details.
23*/
24
25#ifndef LIBDEPIXELIZE_TRACER_HOMOGENEOUSSPLINES_H
26#define LIBDEPIXELIZE_TRACER_HOMOGENEOUSSPLINES_H
27
28#include "simplifiedvoronoi.h"
29#include "point.h"
30#include <algorithm>
31#include <utility>
32
33namespace Tracer {
34
35template<typename T>
37{
38public:
39 struct Polygon
40 {
41 typedef std::vector< Point<T> > Points;
42 typedef typename Points::iterator points_iter;
43 typedef typename Points::const_iterator const_points_iter;
44 typedef typename std::vector<Points>::iterator holes_iter;
45 typedef typename std::vector<Points>::const_iterator const_holes_iter;
46
48 Polygon(const guint8 (&rgba)[4])
49 {
50 for ( int i = 0 ; i != 4 ; ++i )
51 this->rgba[i] = rgba[i];
52 }
53
54 std::vector< Point<T> > vertices;
55
59 std::vector< std::vector< Point<T> > > holes;
60
61 guint8 rgba[4];
62 };
63
64 typedef typename std::vector<Polygon>::iterator iterator;
65 typedef typename std::vector<Polygon>::const_iterator const_iterator;
66 typedef typename std::vector<Polygon>::size_type size_type;
67
68 template<bool adjust_splines>
70
71 // Iterators
73 {
74 return _polygons.begin();
75 }
76
78 {
79 return _polygons.begin();
80 }
81
83 {
84 return _polygons.end();
85 }
86
88 {
89 return _polygons.end();
90 }
91
93 {
94 return _polygons.size();
95 }
96
97 int width() const
98 {
99 return _width;
100 }
101
102 int height() const
103 {
104 return _height;
105 }
106
107private:
108 typedef std::vector< Point<T> > Points;
109 typedef typename Points::iterator points_iter;
110 typedef typename Points::const_iterator points_citer;
111 typedef typename Points::reverse_iterator points_riter;
112 typedef typename Points::const_reverse_iterator points_criter;
113
114 typedef std::pair<points_iter, points_iter> points_range;
115 typedef std::pair<points_citer, points_citer> points_crange;
116
118 {
119 bool ok; //< share an edge
121 const Points *src;
122
123 // the interval is closed on both ends
124 // different from [begin, end) STL style
127 };
128
130 {
131 bool ok; //< share an edge
132
133 // Greater range. The one that should be erased from the vector.
135
136 // Smaller range. The one that should be used to create a new vector.
138 };
139
143 CommonEdge _common_edge(Points &dst, const Points &src);
144
154
158 void _polygon_union(CommonEdge common_edge);
159
167 void _fill_holes(std::vector<Points> &holes, points_iter region_begin,
168 points_iter region_end);
169
170 std::vector<Polygon> _polygons;
173};
174
175template<class T>
176template<bool adjust_splines>
178 adjust_splines> &voronoi) :
179 _width(voronoi.width()),
180 _height(voronoi.height())
181{
182 //if (!voronoi.size())
183 // return;
185
187 voronoi_citer;
188
189 // Identify visible edges (group polygons with the same color)
190 for ( voronoi_citer cell_it = voronoi.begin(), cell_end = voronoi.end()
191 ; cell_it != cell_end ; ++cell_it ) {
192 bool found = false;
193 for ( iterator polygon_it = _polygons.begin(),
194 polygon_end = _polygons.end()
195 ; polygon_it != polygon_end ; ++polygon_it ) {
196 if ( same_color(polygon_it->rgba, cell_it->rgba) ) {
197 CommonEdge common_edge = _common_edge(polygon_it->vertices,
198 cell_it->vertices);
199 if ( common_edge.ok ) {
200 _polygon_union(common_edge);
201 found = true;
202
203 for ( iterator polygon2_it = polygon_it + 1
204 ; polygon2_it != polygon_end ; ++polygon2_it ) {
205 if ( same_color(polygon_it->rgba, polygon2_it->rgba) ) {
206 CommonEdge common_edge2
207 = _common_edge(polygon_it->vertices,
208 polygon2_it->vertices);
209 if ( common_edge2.ok ) {
210 _polygon_union(common_edge2);
211 _polygons.erase(polygon2_it);
212 break;
213 }
214 }
215 }
216
217 break;
218 }
219 }
220 }
221 if ( !found ) {
222 Polygon polygon(cell_it->rgba);
223 polygon.vertices = cell_it->vertices;
224 _polygons.insert(_polygons.end(), polygon);
225 }
226 }
227
228 // Find polygons with holes and fix them
229 // This iteration runs such complex time-consuming algorithm, but each
230 // polygon has an independent result. They wouldn't even need to share/sync
231 // results and the only waste would be a join at the end of the for.
232 for ( typename std::vector<Polygon>::iterator it = _polygons.begin(),
233 end = _polygons.end() ; it != end ; ++it ) {
234 SelfCommonEdge ce = _common_edge(it->vertices, it->vertices.rbegin());
235 while ( ce.ok ) {
236 _fill_holes(it->holes, ce.sml_end.base(), ce.sml_begin.base());
237 it->vertices.erase(ce.grt_end.base() + 1, ce.grt_begin.base());
238 ce = _common_edge(it->vertices, ce.grt_end);
239 }
240 }
241}
242
243// it can infinite loop if points of both entities are equal,
244// but this shouldn't happen if user has only access to Kopf2011 interface
245template<class T>
248{
249 // It's an edge, then the points are closer together. After we find the
250 // first point, there is no need for check against all points of the src
251 // a second time
252
253 const points_iter dst_begin = dst.begin();
254 const points_iter dst_end = dst.end();
255
256 const points_citer src_begin = src.begin();
257 const points_citer src_end = src.end();
258
259 for ( points_iter it = dst_begin ; it != dst_end ; ++it ) {
260 points_citer src_it = std::find(src_begin, src_end, *it);
261
262 if ( src_it == src_end )
263 continue;
264
265 points_iter dst_common_edge_begin = it;
266 points_citer src_common_edge_end = src_it;
267
268 // iterate until find the beginning of the common edge range
269 while ( *dst_common_edge_begin == *src_common_edge_end ) {
270 if ( dst_common_edge_begin == dst_begin )
271 dst_common_edge_begin = dst_end - 1;
272 else
273 --dst_common_edge_begin;
274
275 ++src_common_edge_end;
276 if ( src_common_edge_end == src_end )
277 src_common_edge_end = src_begin;
278 }
279
280 // fix {dst_begin, src_end} range
281 ++dst_common_edge_begin;
282 if ( dst_common_edge_begin == dst_end )
283 dst_common_edge_begin = dst_begin;
284
285 if ( src_common_edge_end == src_begin )
286 src_common_edge_end = src_end - 1;
287 else
288 --src_common_edge_end;
289
290 points_iter dst_common_edge_end = it;
291 points_citer src_common_edge_begin = src_it;
292
293 // find the end of the common edge range
294 while ( *dst_common_edge_end == *src_common_edge_begin ) {
295 ++dst_common_edge_end;
296 if ( dst_common_edge_end == dst_end )
297 dst_common_edge_end = dst_begin;
298
299 if ( src_common_edge_begin == src_begin )
300 src_common_edge_begin = src_end - 1;
301 else
302 --src_common_edge_begin;
303 }
304
305 // fix {dst_end, src_begin} range
306 if ( dst_common_edge_end == dst_begin )
307 dst_common_edge_end = dst_end - 1;
308 else
309 --dst_common_edge_end;
310
311 ++src_common_edge_begin;
312 if ( src_common_edge_begin == src_end )
313 src_common_edge_begin = src_begin;
314
315 CommonEdge ret;
316
317 // if only one point in common
318 if ( dst_common_edge_begin == dst_common_edge_end )
319 continue;
320
321 ret.ok = true;
322
323 ret.dst = &dst;
324 ret.dst_begin = dst_common_edge_begin;
325 ret.dst_end = dst_common_edge_end;
326
327 ret.src = &src;
328 ret.src_begin = src_common_edge_begin;
329 ret.src_end = src_common_edge_end;
330
331 return ret;
332 }
333
334 CommonEdge ret;
335 ret.ok = false;
336 return ret;
337}
338
339template<class T>
342{
343 SelfCommonEdge ret;
344
345 ret.grt_end = points.rend();
346
347 for ( ; it != ret.grt_end ; ++it ) {
348 ret.sml_end = std::find(it + 1, ret.grt_end, *it);
349
350 if ( ret.sml_end == ret.grt_end )
351 continue;
352
353 ret.grt_begin = it;
354 ret.grt_end = ret.sml_end + 1;
355
356 ret.sml_begin = it;
357
358 while ( *ret.sml_begin == *ret.sml_end ) {
359 ++ret.sml_begin;
360 --ret.sml_end;
361 }
362
363 --ret.sml_begin;
364 ++ret.sml_end;
365 ++ret.sml_end;
366
367 ret.ok = true;
368 return ret;
369 }
370
371 ret.ok = false;
372 return ret;
373}
374
375template<class T>
377{
378 Points &dst = *common_edge.dst;
379 const Points &src = *common_edge.src;
380
381 // the rotated cell must be inserted before (dst.begin() + index)
382 typename Points::difference_type index;
383
384 // first, we remove the common edge in dst
385 if ( common_edge.dst_begin < common_edge.dst_end ) {
386 // common edge is in the middle of dst
387
388 index = dst.erase(common_edge.dst_begin,
389 common_edge.dst_end + 1) - dst.begin();
390 } else {
391 // common edge cross the end of dst
392
393 dst.erase(common_edge.dst_begin, dst.end());
394 dst.erase(dst.begin(), common_edge.dst_end);
395 index = dst.end() - dst.begin();
396 }
397
398 // second, we copy src points to polygon
399 if ( common_edge.src_begin < common_edge.src_end ) {
400 // common edge is in the middle of src
401
402 const typename Points::difference_type nfirstinserted
403 = src.end() - common_edge.src_end;
404 const typename Points::difference_type nsecondinserted
405 = 1 + (common_edge.src_begin - src.begin());
406
407 dst.reserve(dst.size() + nfirstinserted + nsecondinserted);
408
409 dst.insert(dst.begin() + index, common_edge.src_end, src.end());
410
411 dst.insert(dst.begin() + index + nfirstinserted,
412 src.begin(), common_edge.src_begin + 1);
413 } else {
414 // common edge cross the end of src
415
416 dst.reserve(dst.size() + 1
417 + (common_edge.src_begin - common_edge.src_end));
418
419 dst.insert(dst.begin() + index,
420 common_edge.src_end, common_edge.src_begin + 1);
421 }
422}
423
424// The following piece of code is so evil that you could end up invoking an
425// ancient beast if you proceed to read it, but I'll be able to explain it in
426// the form of some video (text is not so representative as an image).
427template<class T>
428void HomogeneousSplines<T>::_fill_holes(std::vector<Points> &holes,
429 points_iter region_begin,
430 points_iter region_end)
431{
432 // the exact location might not always be back and iterators will be
433 // invalidated after some insertions, then the index is required
434 const typename std::vector<Points>::size_type hole_index = holes.size();
435 holes.resize(hole_index + 1);
436
437 for ( points_iter it = region_begin + 1 ; it != region_end ; ++it ) {
438 points_iter res = std::find(it + 1, region_end, *it);
439 if ( res == region_end )
440 continue;
441
442 holes[hole_index].insert(holes[hole_index].end(), region_begin,
443 it);
444 region_begin = res;
445
446 do {
447 ++it;
448 --res;
449 } while ( *it == *res );
450 _fill_holes(holes, it - 1, res + 2);
451
452 it = region_begin;
453 }
454
455 holes[hole_index].insert(holes[hole_index].end(), region_begin,
456 region_end - 1);
457}
458
459} // namespace Tracer
460
461#endif // LIBDEPIXELIZE_TRACER_HOMOGENEOUSSPLINES_H
462
463/*
464 Local Variables:
465 mode:c++
466 c-file-style:"stroustrup"
467 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
468 indent-tabs-mode:nil
469 fill-column:99
470 End:
471*/
472// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
void _polygon_union(CommonEdge common_edge)
std::vector< Polygon >::size_type size_type
const_iterator end() const
CommonEdge _common_edge(Points &dst, const Points &src)
Return ok == true if they share an edge (more than one point).
Points::const_iterator points_citer
std::vector< Polygon >::iterator iterator
std::pair< points_iter, points_iter > points_range
std::vector< Polygon > _polygons
const_iterator begin() const
std::vector< Point< T > > Points
Points::reverse_iterator points_riter
void _fill_holes(std::vector< Points > &holes, points_iter region_begin, points_iter region_end)
Weird recursive function created to solve the complex problem to fill polygons holes without the need...
Points::const_reverse_iterator points_criter
std::vector< Polygon >::const_iterator const_iterator
std::pair< points_citer, points_citer > points_crange
HomogeneousSplines(const SimplifiedVoronoi< T, adjust_splines > &voronoi)
std::vector< Cell >::const_iterator const_iterator
Geom::Point end
bool same_color(const guint8(&a)[4], const guint8(&b)[4])
Definition colorspace.h:49
std::vector< std::vector< Point< T > > > holes
It may be benefited from C++11 move references.
std::vector< Points >::const_iterator const_holes_iter
std::vector< Points >::iterator holes_iter
int index
double height
double width