9#if defined(AST_WITH_AGG)
11#include "agg/agg_clip_liang_barsky.h"
12#include "agg/agg_conv_segmentator.h"
19template <
int QueueSize>
22 EmbeddedQueue() : m_queue_read(0), m_queue_write(0) {}
26 inline void set(
const unsigned cmd_,
const double x_,
const double y_) {
27 cmd = cmd_; x = x_; y = y_;
32 int m_queue_read, m_queue_write;
33 item m_queue[QueueSize];
35 inline void queue_push(
const unsigned cmd,
const double x,
const double y) {
36 m_queue[m_queue_write++].set(cmd, x, y);
38 inline bool queue_nonempty() {
return m_queue_read < m_queue_write; }
39 inline bool queue_pop(
unsigned *cmd,
double *x,
double *y) {
40 if (queue_nonempty()) {
41 const item &front = m_queue[m_queue_read++];
42 *cmd = front.cmd; *x = front.x; *y = front.y;
45 m_queue_read = m_queue_write = 0;
48 inline void queue_clear() { m_queue_read = m_queue_write = 0; }
52static const size_t num_extra_points_map[] = {0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
57class RandomNumberGenerator {
58 static const uint32_t a = 214013, c = 2531011;
61 RandomNumberGenerator() : m_seed(0) {}
62 explicit RandomNumberGenerator(
int seed) : m_seed(seed) {}
63 void seed(
int seed) { m_seed = seed; }
65 m_seed = a * m_seed + c;
66 return (
double)m_seed / (double)(1LL << 32);
73template <
class VertexSource>
74class PathNanRemover :
protected EmbeddedQueue<4> {
75 VertexSource *m_source;
76 bool m_remove_nans, m_has_codes;
77 bool valid_segment_exists, m_last_segment_valid, m_was_broken;
78 double m_initX, m_initY;
81 PathNanRemover(VertexSource &source,
bool remove_nans,
bool has_codes)
82 : m_source(&source), m_remove_nans(remove_nans), m_has_codes(has_codes),
83 m_last_segment_valid(false), m_was_broken(false) {
84 m_initX = std::nan(
""); m_initY = std::nan(
"");
85 valid_segment_exists =
false;
88 inline void rewind(
unsigned path_id) {
90 m_source->rewind(path_id);
93 inline unsigned vertex(
double *x,
double *y) {
95 if (!m_remove_nans)
return m_source->vertex(x, y);
98 if (queue_pop(&code, x, y))
return code;
100 bool needs_move_to =
false;
102 code = m_source->vertex(x, y);
103 if (code == agg::path_cmd_stop)
return code;
104 if (code == (agg::path_cmd_end_poly | agg::path_flags_close) && valid_segment_exists) {
106 if (m_last_segment_valid && std::isfinite(m_initX) && std::isfinite(m_initY)) {
107 queue_push(agg::path_cmd_line_to, m_initX, m_initY);
110 m_was_broken =
false;
break;
111 }
else {
return code; }
112 }
else if (code == agg::path_cmd_move_to) {
113 m_initX = *x; m_initY = *y; m_was_broken =
false;
116 if (needs_move_to) queue_push(agg::path_cmd_move_to, *x, *y);
118 size_t num_extra = num_extra_points_map[code & 0xF];
119 m_last_segment_valid = std::isfinite(*x) && std::isfinite(*y);
120 queue_push(code, *x, *y);
121 for (
size_t i = 0; i < num_extra; ++i) {
122 m_source->vertex(x, y);
123 m_last_segment_valid = m_last_segment_valid && std::isfinite(*x) && std::isfinite(*y);
124 queue_push(code, *x, *y);
126 if (m_last_segment_valid) { valid_segment_exists =
true;
break; }
128 m_was_broken =
true; queue_clear();
129 if (std::isfinite(*x) && std::isfinite(*y)) {
130 queue_push(agg::path_cmd_move_to, *x, *y); needs_move_to =
false;
131 }
else { needs_move_to =
true; }
133 if (queue_pop(&code, x, y))
return code;
134 return agg::path_cmd_stop;
136 code = m_source->vertex(x, y);
137 if (code == agg::path_cmd_stop ||
138 (code == (agg::path_cmd_end_poly | agg::path_flags_close) && valid_segment_exists))
140 if (!(std::isfinite(*x) && std::isfinite(*y))) {
142 code = m_source->vertex(x, y);
143 if (code == agg::path_cmd_stop ||
144 (code == (agg::path_cmd_end_poly | agg::path_flags_close) && valid_segment_exists))
146 }
while (!(std::isfinite(*x) && std::isfinite(*y)));
147 return agg::path_cmd_move_to;
149 valid_segment_exists =
true;
158template <
class VertexSource>
159class PathClipper :
public EmbeddedQueue<3> {
160 VertexSource *m_source;
162 agg::rect_base<double> m_cliprect;
163 double m_lastX, m_lastY, m_initX, m_initY;
164 bool m_moveto, m_has_init, m_was_clipped;
167 PathClipper(VertexSource &source,
bool do_clipping,
double width,
double height)
168 : m_source(&source), m_do_clipping(do_clipping),
169 m_cliprect(-1.0, -1.0, width + 1.0, height + 1.0),
170 m_moveto(true), m_has_init(false), m_was_clipped(false) {
171 m_lastX = std::nan(
""); m_lastY = std::nan(
"");
172 m_initX = std::nan(
""); m_initY = std::nan(
"");
175 PathClipper(VertexSource &source,
bool do_clipping,
const agg::rect_base<double> &rect)
176 : m_source(&source), m_do_clipping(do_clipping), m_cliprect(rect),
177 m_moveto(true), m_has_init(false), m_was_clipped(false) {
178 m_lastX = std::nan(
""); m_lastY = std::nan(
"");
179 m_initX = std::nan(
""); m_initY = std::nan(
"");
180 m_cliprect.x1 -= 1.0; m_cliprect.y1 -= 1.0; m_cliprect.x2 += 1.0; m_cliprect.y2 += 1.0;
183 inline void rewind(
unsigned path_id) {
184 m_has_init = m_was_clipped =
false; m_moveto =
true;
185 m_source->rewind(path_id);
188 int draw_clipped_line(
double x0,
double y0,
double x1,
double y1,
bool closed =
false) {
189 unsigned moved = agg::clip_line_segment(&x0, &y0, &x1, &y1, m_cliprect);
190 m_was_clipped = m_was_clipped || (moved != 0);
192 if (moved & 1 || m_moveto) queue_push(agg::path_cmd_move_to, x0, y0);
193 queue_push(agg::path_cmd_line_to, x1, y1);
194 if (closed && !m_was_clipped)
195 queue_push(agg::path_cmd_end_poly | agg::path_flags_close, x1, y1);
202 unsigned vertex(
double *x,
double *y) {
203 unsigned code;
bool emit_moveto =
false;
204 if (!m_do_clipping)
return m_source->vertex(x, y);
205 if (queue_pop(&code, x, y))
return code;
207 while ((code = m_source->vertex(x, y)) != agg::path_cmd_stop) {
210 case (agg::path_cmd_end_poly | agg::path_flags_close):
211 if (m_has_init) draw_clipped_line(m_lastX, m_lastY, m_initX, m_initY,
true);
212 else queue_push(agg::path_cmd_end_poly | agg::path_flags_close, m_lastX, m_lastY);
213 if (queue_nonempty())
goto exit_loop;
215 case agg::path_cmd_move_to:
216 if (m_moveto && m_has_init && m_lastX >= m_cliprect.x1 && m_lastX <= m_cliprect.x2
217 && m_lastY >= m_cliprect.y1 && m_lastY <= m_cliprect.y2) {
218 queue_push(agg::path_cmd_move_to, m_lastX, m_lastY); emit_moveto =
true;
220 m_initX = m_lastX = *x; m_initY = m_lastY = *y;
221 m_has_init =
true; m_moveto =
true; m_was_clipped =
false;
222 if (emit_moveto)
goto exit_loop;
224 case agg::path_cmd_line_to:
225 if (draw_clipped_line(m_lastX, m_lastY, *x, *y)) { m_lastX = *x; m_lastY = *y;
goto exit_loop; }
226 m_lastX = *x; m_lastY = *y;
229 if (m_moveto) { queue_push(agg::path_cmd_move_to, m_lastX, m_lastY); m_moveto =
false; }
230 queue_push(code, *x, *y); m_lastX = *x; m_lastY = *y;
235 if (queue_pop(&code, x, y))
return code;
236 if (m_moveto && m_has_init && m_lastX >= m_cliprect.x1 && m_lastX <= m_cliprect.x2
237 && m_lastY >= m_cliprect.y1 && m_lastY <= m_cliprect.y2) {
238 *x = m_lastX; *y = m_lastY; m_moveto =
false;
239 return agg::path_cmd_move_to;
241 return agg::path_cmd_stop;
248enum e_snap_mode { SNAP_AUTO, SNAP_FALSE, SNAP_TRUE };
253template <
class VertexSource>
255 VertexSource *m_source;
260 static bool should_snap(VertexSource &path, e_snap_mode snap_mode,
unsigned total_vertices) {
261 double x0 = 0, y0 = 0, x1 = 0, y1 = 0;
unsigned code;
264 if (total_vertices > 1024)
return false;
265 code = path.vertex(&x0, &y0);
266 if (code == agg::path_cmd_stop)
return false;
267 while ((code = path.vertex(&x1, &y1)) != agg::path_cmd_stop) {
269 case agg::path_cmd_curve3:
270 case agg::path_cmd_curve4:
return false;
271 case agg::path_cmd_line_to:
272 if (fabs(x0 - x1) >= 1e-4 && fabs(y0 - y1) >= 1e-4)
return false;
277 case SNAP_FALSE:
return false;
278 case SNAP_TRUE:
return true;
283 PathSnapper(VertexSource &source, e_snap_mode snap_mode,
unsigned total_vertices = 15,
284 double stroke_width = 0.0) : m_source(&source) {
285 m_snap = should_snap(source, snap_mode, total_vertices);
287 int is_odd = mpl_round_to_int(stroke_width) % 2;
288 m_snap_value = (is_odd) ? 0.5 : 0.0;
293 inline void rewind(
unsigned path_id) { m_source->rewind(path_id); }
294 inline unsigned vertex(
double *x,
double *y) {
295 unsigned code = m_source->vertex(x, y);
296 if (m_snap && agg::is_vertex(code)) {
297 *x = floor(*x + 0.5) + m_snap_value;
298 *y = floor(*y + 0.5) + m_snap_value;
302 inline bool is_snapping() {
return m_snap; }
308template <
class VertexSource>
309class PathSimplifier :
protected EmbeddedQueue<9> {
311 PathSimplifier(VertexSource &source,
bool do_simplify,
double simplify_threshold)
312 : m_source(&source), m_simplify(do_simplify),
313 m_moveto(true), m_after_moveto(false), m_clipped(false),
314 m_has_init(false), m_simplify_threshold(simplify_threshold * simplify_threshold),
315 m_initX(0), m_initY(0),
316 m_lastx(0), m_lasty(0), m_origdx(0), m_origdy(0), m_origdNorm2(0),
317 m_dnorm2ForwardMax(0), m_dnorm2BackwardMax(0),
318 m_nextX(0), m_nextY(0), m_nextBackwardX(0), m_nextBackwardY(0),
319 m_currVecStartX(0), m_currVecStartY(0),
320 m_lastForwardMax(false), m_lastBackwardMax(false) {}
322 inline void rewind(
unsigned path_id) { queue_clear(); m_moveto =
true; m_source->rewind(path_id); }
324 unsigned vertex(
double *x,
double *y) {
326 if (!m_simplify)
return m_source->vertex(x, y);
327 if (queue_pop(&cmd, x, y))
return cmd;
329 while ((cmd = m_source->vertex(x, y)) != agg::path_cmd_stop) {
330 if (m_moveto || cmd == agg::path_cmd_move_to) {
331 if (m_origdNorm2 != 0.0 && !m_after_moveto) _push(x, y);
332 m_after_moveto =
true;
333 m_has_init = std::isfinite(*x) && std::isfinite(*y);
334 if (m_has_init) { m_initX = *x; m_initY = *y; }
335 m_lastx = *x; m_lasty = *y;
336 m_moveto =
false; m_origdNorm2 = 0.0; m_dnorm2BackwardMax = 0.0; m_clipped =
true;
337 if (queue_nonempty())
break;
340 m_after_moveto =
false;
342 if (agg::is_close(cmd)) {
343 if (m_has_init) { *x = m_initX; *y = m_initY; }
else continue;
346 if (m_origdNorm2 == 0.0) {
347 if (m_clipped) { queue_push(agg::path_cmd_move_to, m_lastx, m_lasty); m_clipped =
false; }
348 m_origdx = *x - m_lastx; m_origdy = *y - m_lasty;
349 m_origdNorm2 = m_origdx * m_origdx + m_origdy * m_origdy;
350 m_dnorm2ForwardMax = m_origdNorm2; m_dnorm2BackwardMax = 0.0;
351 m_lastForwardMax =
true; m_lastBackwardMax =
false;
352 m_currVecStartX = m_lastx; m_currVecStartY = m_lasty;
353 m_nextX = m_lastx = *x; m_nextY = m_lasty = *y;
357 double totdx = *x - m_currVecStartX, totdy = *y - m_currVecStartY;
358 double totdot = m_origdx * totdx + m_origdy * totdy;
359 double paradx = totdot * m_origdx / m_origdNorm2, parady = totdot * m_origdy / m_origdNorm2;
360 double perpdx = totdx - paradx, perpdy = totdy - parady;
361 double perpdNorm2 = perpdx * perpdx + perpdy * perpdy;
363 if (perpdNorm2 < m_simplify_threshold) {
364 double paradNorm2 = paradx * paradx + parady * parady;
365 m_lastForwardMax = m_lastBackwardMax =
false;
367 if (paradNorm2 > m_dnorm2ForwardMax) { m_lastForwardMax =
true; m_dnorm2ForwardMax = paradNorm2; m_nextX = *x; m_nextY = *y; }
369 if (paradNorm2 > m_dnorm2BackwardMax) { m_lastBackwardMax =
true; m_dnorm2BackwardMax = paradNorm2; m_nextBackwardX = *x; m_nextBackwardY = *y; }
371 m_lastx = *x; m_lasty = *y;
378 if (cmd == agg::path_cmd_stop) {
379 if (m_origdNorm2 != 0.0) {
380 queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to, m_nextX, m_nextY);
381 if (m_dnorm2BackwardMax > 0.0)
382 queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to, m_nextBackwardX, m_nextBackwardY);
385 queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to, m_lastx, m_lasty);
387 queue_push(agg::path_cmd_stop, 0.0, 0.0);
390 if (queue_pop(&cmd, x, y))
return cmd;
391 return agg::path_cmd_stop;
395 VertexSource *m_source;
396 bool m_simplify, m_moveto, m_after_moveto, m_clipped, m_has_init;
397 double m_simplify_threshold;
398 double m_initX, m_initY, m_lastx, m_lasty, m_origdx, m_origdy, m_origdNorm2;
399 double m_dnorm2ForwardMax, m_dnorm2BackwardMax, m_nextX, m_nextY;
400 double m_nextBackwardX, m_nextBackwardY, m_currVecStartX, m_currVecStartY;
401 bool m_lastForwardMax, m_lastBackwardMax;
403 inline void _push(
double *x,
double *y) {
404 bool needToPushBack = (m_dnorm2BackwardMax > 0.0);
405 if (needToPushBack) {
406 if (m_lastForwardMax) { queue_push(agg::path_cmd_line_to, m_nextBackwardX, m_nextBackwardY); queue_push(agg::path_cmd_line_to, m_nextX, m_nextY); }
407 else { queue_push(agg::path_cmd_line_to, m_nextX, m_nextY); queue_push(agg::path_cmd_line_to, m_nextBackwardX, m_nextBackwardY); }
408 }
else { queue_push(agg::path_cmd_line_to, m_nextX, m_nextY); }
409 if (m_clipped) queue_push(agg::path_cmd_move_to, m_lastx, m_lasty);
410 else if (!m_lastForwardMax && !m_lastBackwardMax) queue_push(agg::path_cmd_line_to, m_lastx, m_lasty);
411 m_origdx = *x - m_lastx; m_origdy = *y - m_lasty;
412 m_origdNorm2 = m_origdx * m_origdx + m_origdy * m_origdy;
413 m_dnorm2ForwardMax = m_origdNorm2; m_lastForwardMax =
true;
414 m_currVecStartX = m_queue[m_queue_write - 1].x;
415 m_currVecStartY = m_queue[m_queue_write - 1].y;
416 m_lastx = m_nextX = *x; m_lasty = m_nextY = *y;
417 m_dnorm2BackwardMax = 0.0; m_lastBackwardMax =
false;
425template <
class VertexSource>
428 Sketch(VertexSource &source,
double scale,
double length,
double randomness)
429 : m_source(&source), m_scale(scale), m_length(length), m_randomness(randomness),
430 m_segmented(source), m_last_x(0), m_last_y(0), m_p(0), m_has_last(false), m_rand(0)
433 const double d_M_PI = 3.14159265358979323846;
434 if (m_length <= std::numeric_limits<double>::epsilon() || m_randomness <= std::numeric_limits<double>::epsilon()) {
437 m_p_scale = (2.0 * d_M_PI) / (m_length * m_randomness);
439 m_log_randomness = (m_randomness <= std::numeric_limits<double>::epsilon()) ? 0.0 : 2.0 * log(m_randomness);
442 unsigned vertex(
double *x,
double *y) {
443 if (m_scale == 0.0)
return m_source->vertex(x, y);
444 unsigned code = m_segmented.vertex(x, y);
445 if (code == agg::path_cmd_move_to) { m_has_last =
false; m_p = 0.0; }
447 double d_rand = m_rand.get_double();
448 m_p += exp(d_rand * m_log_randomness);
449 double den = m_last_x - *x, num = m_last_y - *y;
450 double len = num * num + den * den;
451 m_last_x = *x; m_last_y = *y;
454 double r = sin(m_p * m_p_scale) * m_scale;
455 double roverlen = r / len;
456 *x += roverlen * num;
457 *y -= roverlen * den;
459 }
else { m_last_x = *x; m_last_y = *y; }
464 inline void rewind(
unsigned path_id) {
465 m_has_last =
false; m_p = 0.0;
466 if (m_scale != 0.0) { m_rand.seed(0); m_segmented.rewind(path_id); }
467 else m_source->rewind(path_id);
471 VertexSource *m_source;
472 double m_scale, m_length, m_randomness;
473 agg::conv_segmentator<VertexSource> m_segmented;
474 double m_last_x, m_last_y, m_p, m_p_scale, m_log_randomness;
476 RandomNumberGenerator m_rand;