9#if defined(AST_WITH_AGG)
11A_SUPPRESS_WARNINGS_BEGIN
12#include "agg/agg_clip_liang_barsky.h"
13#include "agg/agg_conv_segmentator.h"
21template <
int QueueSize>
24 EmbeddedQueue() : m_queue_read(0), m_queue_write(0) {}
28 inline void set(
const unsigned cmd_,
const double x_,
const double y_) {
29 cmd = cmd_; x = x_; y = y_;
34 int m_queue_read, m_queue_write;
35 item m_queue[QueueSize];
37 inline void queue_push(
const unsigned cmd,
const double x,
const double y) {
38 m_queue[m_queue_write++].set(cmd, x, y);
40 inline bool queue_nonempty() {
return m_queue_read < m_queue_write; }
41 inline bool queue_pop(
unsigned *cmd,
double *x,
double *y) {
42 if (queue_nonempty()) {
43 const item &front = m_queue[m_queue_read++];
44 *cmd = front.cmd; *x = front.x; *y = front.y;
47 m_queue_read = m_queue_write = 0;
50 inline void queue_clear() { m_queue_read = m_queue_write = 0; }
54static const size_t num_extra_points_map[] = {0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
59class RandomNumberGenerator {
60 static const uint32_t a = 214013, c = 2531011;
63 RandomNumberGenerator() : m_seed(0) {}
64 explicit RandomNumberGenerator(
int seed) : m_seed(seed) {}
65 void seed(
int seed) { m_seed = seed; }
67 m_seed = a * m_seed + c;
68 return (
double)m_seed / (double)(1LL << 32);
75template <
class VertexSource>
76class PathNanRemover :
protected EmbeddedQueue<4> {
77 VertexSource *m_source;
78 bool m_remove_nans, m_has_codes;
79 bool valid_segment_exists, m_last_segment_valid, m_was_broken;
80 double m_initX, m_initY;
83 PathNanRemover(VertexSource &source,
bool remove_nans,
bool has_codes)
84 : m_source(&source), m_remove_nans(remove_nans), m_has_codes(has_codes),
85 m_last_segment_valid(false), m_was_broken(false) {
86 m_initX = std::nan(
""); m_initY = std::nan(
"");
87 valid_segment_exists =
false;
90 inline void rewind(
unsigned path_id) {
92 m_source->rewind(path_id);
95 inline unsigned vertex(
double *x,
double *y) {
97 if (!m_remove_nans)
return m_source->vertex(x, y);
100 if (queue_pop(&code, x, y))
return code;
102 bool needs_move_to =
false;
104 code = m_source->vertex(x, y);
105 if (code == agg::path_cmd_stop)
return code;
106 if (code == (agg::path_cmd_end_poly | agg::path_flags_close) && valid_segment_exists) {
108 if (m_last_segment_valid && std::isfinite(m_initX) && std::isfinite(m_initY)) {
109 queue_push(agg::path_cmd_line_to, m_initX, m_initY);
112 m_was_broken =
false;
break;
113 }
else {
return code; }
114 }
else if (code == agg::path_cmd_move_to) {
115 m_initX = *x; m_initY = *y; m_was_broken =
false;
118 if (needs_move_to) queue_push(agg::path_cmd_move_to, *x, *y);
120 size_t num_extra = num_extra_points_map[code & 0xF];
121 m_last_segment_valid = std::isfinite(*x) && std::isfinite(*y);
122 queue_push(code, *x, *y);
123 for (
size_t i = 0; i < num_extra; ++i) {
124 m_source->vertex(x, y);
125 m_last_segment_valid = m_last_segment_valid && std::isfinite(*x) && std::isfinite(*y);
126 queue_push(code, *x, *y);
128 if (m_last_segment_valid) { valid_segment_exists =
true;
break; }
130 m_was_broken =
true; queue_clear();
131 if (std::isfinite(*x) && std::isfinite(*y)) {
132 queue_push(agg::path_cmd_move_to, *x, *y); needs_move_to =
false;
133 }
else { needs_move_to =
true; }
135 if (queue_pop(&code, x, y))
return code;
136 return agg::path_cmd_stop;
138 code = m_source->vertex(x, y);
139 if (code == agg::path_cmd_stop ||
140 (code == (agg::path_cmd_end_poly | agg::path_flags_close) && valid_segment_exists))
142 if (!(std::isfinite(*x) && std::isfinite(*y))) {
144 code = m_source->vertex(x, y);
145 if (code == agg::path_cmd_stop ||
146 (code == (agg::path_cmd_end_poly | agg::path_flags_close) && valid_segment_exists))
148 }
while (!(std::isfinite(*x) && std::isfinite(*y)));
149 return agg::path_cmd_move_to;
151 valid_segment_exists =
true;
160template <
class VertexSource>
161class PathClipper :
public EmbeddedQueue<3> {
162 VertexSource *m_source;
164 agg::rect_base<double> m_cliprect;
165 double m_lastX, m_lastY, m_initX, m_initY;
166 bool m_moveto, m_has_init, m_was_clipped;
169 PathClipper(VertexSource &source,
bool do_clipping,
double width,
double height)
170 : m_source(&source), m_do_clipping(do_clipping),
171 m_cliprect(-1.0, -1.0, width + 1.0, height + 1.0),
172 m_moveto(true), m_has_init(false), m_was_clipped(false) {
173 m_lastX = std::nan(
""); m_lastY = std::nan(
"");
174 m_initX = std::nan(
""); m_initY = std::nan(
"");
177 PathClipper(VertexSource &source,
bool do_clipping,
const agg::rect_base<double> &rect)
178 : m_source(&source), m_do_clipping(do_clipping), m_cliprect(rect),
179 m_moveto(true), m_has_init(false), m_was_clipped(false) {
180 m_lastX = std::nan(
""); m_lastY = std::nan(
"");
181 m_initX = std::nan(
""); m_initY = std::nan(
"");
182 m_cliprect.x1 -= 1.0; m_cliprect.y1 -= 1.0; m_cliprect.x2 += 1.0; m_cliprect.y2 += 1.0;
185 inline void rewind(
unsigned path_id) {
186 m_has_init = m_was_clipped =
false; m_moveto =
true;
187 m_source->rewind(path_id);
190 int draw_clipped_line(
double x0,
double y0,
double x1,
double y1,
bool closed =
false) {
191 unsigned moved = agg::clip_line_segment(&x0, &y0, &x1, &y1, m_cliprect);
192 m_was_clipped = m_was_clipped || (moved != 0);
194 if (moved & 1 || m_moveto) queue_push(agg::path_cmd_move_to, x0, y0);
195 queue_push(agg::path_cmd_line_to, x1, y1);
196 if (closed && !m_was_clipped)
197 queue_push(agg::path_cmd_end_poly | agg::path_flags_close, x1, y1);
204 unsigned vertex(
double *x,
double *y) {
205 unsigned code;
bool emit_moveto =
false;
206 if (!m_do_clipping)
return m_source->vertex(x, y);
207 if (queue_pop(&code, x, y))
return code;
209 while ((code = m_source->vertex(x, y)) != agg::path_cmd_stop) {
212 case (agg::path_cmd_end_poly | agg::path_flags_close):
213 if (m_has_init) draw_clipped_line(m_lastX, m_lastY, m_initX, m_initY,
true);
214 else queue_push(agg::path_cmd_end_poly | agg::path_flags_close, m_lastX, m_lastY);
215 if (queue_nonempty())
goto exit_loop;
217 case agg::path_cmd_move_to:
218 if (m_moveto && m_has_init && m_lastX >= m_cliprect.x1 && m_lastX <= m_cliprect.x2
219 && m_lastY >= m_cliprect.y1 && m_lastY <= m_cliprect.y2) {
220 queue_push(agg::path_cmd_move_to, m_lastX, m_lastY); emit_moveto =
true;
222 m_initX = m_lastX = *x; m_initY = m_lastY = *y;
223 m_has_init =
true; m_moveto =
true; m_was_clipped =
false;
224 if (emit_moveto)
goto exit_loop;
226 case agg::path_cmd_line_to:
227 if (draw_clipped_line(m_lastX, m_lastY, *x, *y)) { m_lastX = *x; m_lastY = *y;
goto exit_loop; }
228 m_lastX = *x; m_lastY = *y;
231 if (m_moveto) { queue_push(agg::path_cmd_move_to, m_lastX, m_lastY); m_moveto =
false; }
232 queue_push(code, *x, *y); m_lastX = *x; m_lastY = *y;
237 if (queue_pop(&code, x, y))
return code;
238 if (m_moveto && m_has_init && m_lastX >= m_cliprect.x1 && m_lastX <= m_cliprect.x2
239 && m_lastY >= m_cliprect.y1 && m_lastY <= m_cliprect.y2) {
240 *x = m_lastX; *y = m_lastY; m_moveto =
false;
241 return agg::path_cmd_move_to;
243 return agg::path_cmd_stop;
250enum e_snap_mode { SNAP_AUTO, SNAP_FALSE, SNAP_TRUE };
255template <
class VertexSource>
257 VertexSource *m_source;
262 static bool should_snap(VertexSource &path, e_snap_mode snap_mode,
unsigned total_vertices) {
263 double x0 = 0, y0 = 0, x1 = 0, y1 = 0;
unsigned code;
266 if (total_vertices > 1024)
return false;
267 code = path.vertex(&x0, &y0);
268 if (code == agg::path_cmd_stop)
return false;
269 while ((code = path.vertex(&x1, &y1)) != agg::path_cmd_stop) {
271 case agg::path_cmd_curve3:
272 case agg::path_cmd_curve4:
return false;
273 case agg::path_cmd_line_to:
274 if (fabs(x0 - x1) >= 1e-4 && fabs(y0 - y1) >= 1e-4)
return false;
279 case SNAP_FALSE:
return false;
280 case SNAP_TRUE:
return true;
285 PathSnapper(VertexSource &source, e_snap_mode snap_mode,
unsigned total_vertices = 15,
286 double stroke_width = 0.0) : m_source(&source) {
287 m_snap = should_snap(source, snap_mode, total_vertices);
289 int is_odd = mpl_round_to_int(stroke_width) % 2;
290 m_snap_value = (is_odd) ? 0.5 : 0.0;
295 inline void rewind(
unsigned path_id) { m_source->rewind(path_id); }
296 inline unsigned vertex(
double *x,
double *y) {
297 unsigned code = m_source->vertex(x, y);
298 if (m_snap && agg::is_vertex(code)) {
299 *x = floor(*x + 0.5) + m_snap_value;
300 *y = floor(*y + 0.5) + m_snap_value;
304 inline bool is_snapping() {
return m_snap; }
310template <
class VertexSource>
311class PathSimplifier :
protected EmbeddedQueue<9> {
313 PathSimplifier(VertexSource &source,
bool do_simplify,
double simplify_threshold)
314 : m_source(&source), m_simplify(do_simplify),
315 m_moveto(true), m_after_moveto(false), m_clipped(false),
316 m_has_init(false), m_simplify_threshold(simplify_threshold * simplify_threshold),
317 m_initX(0), m_initY(0),
318 m_lastx(0), m_lasty(0), m_origdx(0), m_origdy(0), m_origdNorm2(0),
319 m_dnorm2ForwardMax(0), m_dnorm2BackwardMax(0),
320 m_nextX(0), m_nextY(0), m_nextBackwardX(0), m_nextBackwardY(0),
321 m_currVecStartX(0), m_currVecStartY(0),
322 m_lastForwardMax(false), m_lastBackwardMax(false) {}
324 inline void rewind(
unsigned path_id) { queue_clear(); m_moveto =
true; m_source->rewind(path_id); }
326 unsigned vertex(
double *x,
double *y) {
328 if (!m_simplify)
return m_source->vertex(x, y);
329 if (queue_pop(&cmd, x, y))
return cmd;
331 while ((cmd = m_source->vertex(x, y)) != agg::path_cmd_stop) {
332 if (m_moveto || cmd == agg::path_cmd_move_to) {
333 if (m_origdNorm2 != 0.0 && !m_after_moveto) _push(x, y);
334 m_after_moveto =
true;
335 m_has_init = std::isfinite(*x) && std::isfinite(*y);
336 if (m_has_init) { m_initX = *x; m_initY = *y; }
337 m_lastx = *x; m_lasty = *y;
338 m_moveto =
false; m_origdNorm2 = 0.0; m_dnorm2BackwardMax = 0.0; m_clipped =
true;
339 if (queue_nonempty())
break;
342 m_after_moveto =
false;
344 if (agg::is_close(cmd)) {
345 if (m_has_init) { *x = m_initX; *y = m_initY; }
else continue;
348 if (m_origdNorm2 == 0.0) {
349 if (m_clipped) { queue_push(agg::path_cmd_move_to, m_lastx, m_lasty); m_clipped =
false; }
350 m_origdx = *x - m_lastx; m_origdy = *y - m_lasty;
351 m_origdNorm2 = m_origdx * m_origdx + m_origdy * m_origdy;
352 m_dnorm2ForwardMax = m_origdNorm2; m_dnorm2BackwardMax = 0.0;
353 m_lastForwardMax =
true; m_lastBackwardMax =
false;
354 m_currVecStartX = m_lastx; m_currVecStartY = m_lasty;
355 m_nextX = m_lastx = *x; m_nextY = m_lasty = *y;
359 double totdx = *x - m_currVecStartX, totdy = *y - m_currVecStartY;
360 double totdot = m_origdx * totdx + m_origdy * totdy;
361 double paradx = totdot * m_origdx / m_origdNorm2, parady = totdot * m_origdy / m_origdNorm2;
362 double perpdx = totdx - paradx, perpdy = totdy - parady;
363 double perpdNorm2 = perpdx * perpdx + perpdy * perpdy;
365 if (perpdNorm2 < m_simplify_threshold) {
366 double paradNorm2 = paradx * paradx + parady * parady;
367 m_lastForwardMax = m_lastBackwardMax =
false;
369 if (paradNorm2 > m_dnorm2ForwardMax) { m_lastForwardMax =
true; m_dnorm2ForwardMax = paradNorm2; m_nextX = *x; m_nextY = *y; }
371 if (paradNorm2 > m_dnorm2BackwardMax) { m_lastBackwardMax =
true; m_dnorm2BackwardMax = paradNorm2; m_nextBackwardX = *x; m_nextBackwardY = *y; }
373 m_lastx = *x; m_lasty = *y;
380 if (cmd == agg::path_cmd_stop) {
381 if (m_origdNorm2 != 0.0) {
382 queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to, m_nextX, m_nextY);
383 if (m_dnorm2BackwardMax > 0.0)
384 queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to, m_nextBackwardX, m_nextBackwardY);
387 queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to, m_lastx, m_lasty);
389 queue_push(agg::path_cmd_stop, 0.0, 0.0);
392 if (queue_pop(&cmd, x, y))
return cmd;
393 return agg::path_cmd_stop;
397 VertexSource *m_source;
398 bool m_simplify, m_moveto, m_after_moveto, m_clipped, m_has_init;
399 double m_simplify_threshold;
400 double m_initX, m_initY, m_lastx, m_lasty, m_origdx, m_origdy, m_origdNorm2;
401 double m_dnorm2ForwardMax, m_dnorm2BackwardMax, m_nextX, m_nextY;
402 double m_nextBackwardX, m_nextBackwardY, m_currVecStartX, m_currVecStartY;
403 bool m_lastForwardMax, m_lastBackwardMax;
405 inline void _push(
double *x,
double *y) {
406 bool needToPushBack = (m_dnorm2BackwardMax > 0.0);
407 if (needToPushBack) {
408 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); }
409 else { queue_push(agg::path_cmd_line_to, m_nextX, m_nextY); queue_push(agg::path_cmd_line_to, m_nextBackwardX, m_nextBackwardY); }
410 }
else { queue_push(agg::path_cmd_line_to, m_nextX, m_nextY); }
411 if (m_clipped) queue_push(agg::path_cmd_move_to, m_lastx, m_lasty);
412 else if (!m_lastForwardMax && !m_lastBackwardMax) queue_push(agg::path_cmd_line_to, m_lastx, m_lasty);
413 m_origdx = *x - m_lastx; m_origdy = *y - m_lasty;
414 m_origdNorm2 = m_origdx * m_origdx + m_origdy * m_origdy;
415 m_dnorm2ForwardMax = m_origdNorm2; m_lastForwardMax =
true;
416 m_currVecStartX = m_queue[m_queue_write - 1].x;
417 m_currVecStartY = m_queue[m_queue_write - 1].y;
418 m_lastx = m_nextX = *x; m_lasty = m_nextY = *y;
419 m_dnorm2BackwardMax = 0.0; m_lastBackwardMax =
false;
427template <
class VertexSource>
430 Sketch(VertexSource &source,
double scale,
double length,
double randomness)
431 : m_source(&source), m_scale(scale), m_length(length), m_randomness(randomness),
432 m_segmented(source), m_last_x(0), m_last_y(0), m_p(0), m_has_last(false), m_rand(0)
435 const double d_M_PI = 3.14159265358979323846;
436 if (m_length <= std::numeric_limits<double>::epsilon() || m_randomness <= std::numeric_limits<double>::epsilon()) {
439 m_p_scale = (2.0 * d_M_PI) / (m_length * m_randomness);
441 m_log_randomness = (m_randomness <= std::numeric_limits<double>::epsilon()) ? 0.0 : 2.0 * log(m_randomness);
444 unsigned vertex(
double *x,
double *y) {
445 if (m_scale == 0.0)
return m_source->vertex(x, y);
446 unsigned code = m_segmented.vertex(x, y);
447 if (code == agg::path_cmd_move_to) { m_has_last =
false; m_p = 0.0; }
449 double d_rand = m_rand.get_double();
450 m_p += exp(d_rand * m_log_randomness);
451 double den = m_last_x - *x, num = m_last_y - *y;
452 double len = num * num + den * den;
453 m_last_x = *x; m_last_y = *y;
456 double r = sin(m_p * m_p_scale) * m_scale;
457 double roverlen = r / len;
458 *x += roverlen * num;
459 *y -= roverlen * den;
461 }
else { m_last_x = *x; m_last_y = *y; }
466 inline void rewind(
unsigned path_id) {
467 m_has_last =
false; m_p = 0.0;
468 if (m_scale != 0.0) { m_rand.seed(0); m_segmented.rewind(path_id); }
469 else m_source->rewind(path_id);
473 VertexSource *m_source;
474 double m_scale, m_length, m_randomness;
475 agg::conv_segmentator<VertexSource> m_segmented;
476 double m_last_x, m_last_y, m_p, m_p_scale, m_log_randomness;
478 RandomNumberGenerator m_rand;
482A_SUPPRESS_WARNINGS_END