aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/matplotlib/py2/src/path_converters.h
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/python/matplotlib/py2/src/path_converters.h')
-rw-r--r--contrib/python/matplotlib/py2/src/path_converters.h1011
1 files changed, 0 insertions, 1011 deletions
diff --git a/contrib/python/matplotlib/py2/src/path_converters.h b/contrib/python/matplotlib/py2/src/path_converters.h
deleted file mode 100644
index db40c18d5a..0000000000
--- a/contrib/python/matplotlib/py2/src/path_converters.h
+++ /dev/null
@@ -1,1011 +0,0 @@
-/* -*- mode: c++; c-basic-offset: 4 -*- */
-
-#ifndef __PATH_CONVERTERS_H__
-#define __PATH_CONVERTERS_H__
-
-#include <cmath>
-#include <stdint.h>
-#include "agg_path_storage.h"
-#include "agg_clip_liang_barsky.h"
-#include "mplutils.h"
-#include "agg_conv_segmentator.h"
-
-/*
- This file contains a number of vertex converters that modify
- paths. They all work as iterators, where the output is generated
- on-the-fly, and don't require a copy of the full data.
-
- Each class represents a discrete step in a "path-cleansing" pipeline.
- They are currently applied in the following order in the Agg backend:
-
- 1. Affine transformation (implemented in Agg, not here)
-
- 2. PathNanRemover: skips over segments containing non-finite numbers
- by inserting MOVETO commands
-
- 3. PathClipper: Clips line segments to a given rectangle. This is
- helpful for data reduction, and also to avoid a limitation in
- Agg where coordinates can not be larger than 24-bit signed
- integers.
-
- 4. PathSnapper: Rounds the path to the nearest center-pixels.
- This makes rectilinear curves look much better.
-
- 5. PathSimplifier: Removes line segments from highly dense paths
- that would not have an impact on their appearance. Speeds up
- rendering and reduces file sizes.
-
- 6. curve-to-line-segment conversion (implemented in Agg, not here)
-
- 7. stroking (implemented in Agg, not here)
- */
-
-/************************************************************
- This is a base class for vertex converters that need to queue their
- output. It is designed to be as fast as possible vs. the STL's queue
- which is more flexible.
- */
-template <int QueueSize>
-class EmbeddedQueue
-{
- protected:
- EmbeddedQueue() : m_queue_read(0), m_queue_write(0)
- {
- // empty
- }
-
- struct item
- {
- item()
- {
- }
-
- inline void set(const unsigned cmd_, const double x_, const double y_)
- {
- cmd = cmd_;
- x = x_;
- y = y_;
- }
- unsigned cmd;
- double x;
- double y;
- };
- int m_queue_read;
- int m_queue_write;
- item m_queue[QueueSize];
-
- inline void queue_push(const unsigned cmd, const double x, const double y)
- {
- m_queue[m_queue_write++].set(cmd, x, y);
- }
-
- inline bool queue_nonempty()
- {
- return m_queue_read < m_queue_write;
- }
-
- inline bool queue_pop(unsigned *cmd, double *x, double *y)
- {
- if (queue_nonempty()) {
- const item &front = m_queue[m_queue_read++];
- *cmd = front.cmd;
- *x = front.x;
- *y = front.y;
-
- return true;
- }
-
- m_queue_read = 0;
- m_queue_write = 0;
-
- return false;
- }
-
- inline void queue_clear()
- {
- m_queue_read = 0;
- m_queue_write = 0;
- }
-};
-
-/* Defines when path segment types have more than one vertex */
-static const size_t num_extra_points_map[] =
- {0, 0, 0, 1,
- 2, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0
- };
-
-/* An implementation of a simple linear congruential random number
- generator. This is a "classic" and fast RNG which works fine for
- our purposes of sketching lines, but should not be used for things
- that matter, like crypto. We are implementing this ourselves
- rather than using the C stdlib so that the seed state is not shared
- with other third-party code. There are recent C++ options, but we
- still require nothing later than C++98 for compatibility
- reasons. */
-class RandomNumberGenerator
-{
-private:
- /* These are the same constants from MS Visual C++, which
- has the nice property that the modulus is 2^32, thus
- saving an explicit modulo operation
- */
- static const uint32_t a = 214013;
- static const uint32_t c = 2531011;
- uint32_t m_seed;
-
-public:
- RandomNumberGenerator() : m_seed(0) {}
- RandomNumberGenerator(int seed) : m_seed(seed) {}
-
- void seed(int seed)
- {
- m_seed = seed;
- }
-
- double get_double()
- {
- m_seed = (a * m_seed + c);
- return (double)m_seed / (double)(1LL << 32);
- }
-};
-
-/*
- PathNanRemover is a vertex converter that removes non-finite values
- from the vertices list, and inserts MOVETO commands as necessary to
- skip over them. If a curve segment contains at least one non-finite
- value, the entire curve segment will be skipped.
- */
-template <class VertexSource>
-class PathNanRemover : protected EmbeddedQueue<4>
-{
- VertexSource *m_source;
- bool m_remove_nans;
- bool m_has_curves;
-
- public:
- /* has_curves should be true if the path contains bezier curve
- segments, as this requires a slower algorithm to remove the
- NaNs. When in doubt, set to true.
- */
- PathNanRemover(VertexSource &source, bool remove_nans, bool has_curves)
- : m_source(&source), m_remove_nans(remove_nans), m_has_curves(has_curves)
- {
- // empty
- }
-
- inline void rewind(unsigned path_id)
- {
- queue_clear();
- m_source->rewind(path_id);
- }
-
- inline unsigned vertex(double *x, double *y)
- {
- unsigned code;
-
- if (!m_remove_nans) {
- return m_source->vertex(x, y);
- }
-
- if (m_has_curves) {
- /* This is the slow method for when there might be curves. */
- if (queue_pop(&code, x, y)) {
- return code;
- }
-
- bool needs_move_to = false;
- while (true) {
- /* The approach here is to push each full curve
- segment into the queue. If any non-finite values
- are found along the way, the queue is emptied, and
- the next curve segment is handled. */
- code = m_source->vertex(x, y);
- if (code == agg::path_cmd_stop ||
- code == (agg::path_cmd_end_poly | agg::path_flags_close)) {
- return code;
- }
-
- if (needs_move_to) {
- queue_push(agg::path_cmd_move_to, *x, *y);
- }
-
- size_t num_extra_points = num_extra_points_map[code & 0xF];
- bool has_nan = (!(std::isfinite(*x) && std::isfinite(*y)));
- queue_push(code, *x, *y);
-
- /* Note: this test can not be short-circuited, since we need to
- advance through the entire curve no matter what */
- for (size_t i = 0; i < num_extra_points; ++i) {
- m_source->vertex(x, y);
- has_nan = has_nan || !(std::isfinite(*x) && std::isfinite(*y));
- queue_push(code, *x, *y);
- }
-
- if (!has_nan) {
- break;
- }
-
- queue_clear();
-
- /* If the last point is finite, we use that for the
- moveto, otherwise, we'll use the first vertex of
- the next curve. */
- if (std::isfinite(*x) && std::isfinite(*y)) {
- queue_push(agg::path_cmd_move_to, *x, *y);
- needs_move_to = false;
- } else {
- needs_move_to = true;
- }
- }
-
- if (queue_pop(&code, x, y)) {
- return code;
- } else {
- return agg::path_cmd_stop;
- }
- } else // !m_has_curves
- {
- /* This is the fast path for when we know we have no curves */
- code = m_source->vertex(x, y);
-
- if (code == agg::path_cmd_stop ||
- code == (agg::path_cmd_end_poly | agg::path_flags_close)) {
- return code;
- }
-
- if (!(std::isfinite(*x) && std::isfinite(*y))) {
- do {
- code = m_source->vertex(x, y);
- if (code == agg::path_cmd_stop ||
- code == (agg::path_cmd_end_poly | agg::path_flags_close)) {
- return code;
- }
- } while (!(std::isfinite(*x) && std::isfinite(*y)));
- return agg::path_cmd_move_to;
- }
-
- return code;
- }
- }
-};
-
-/************************************************************
- PathClipper uses the Liang-Barsky line clipping algorithm (as
- implemented in Agg) to clip the path to a given rectangle. Lines
- will never extend outside of the rectangle. Curve segments are not
- clipped, but are always included in their entirety.
- */
-template <class VertexSource>
-class PathClipper : public EmbeddedQueue<3>
-{
- VertexSource *m_source;
- bool m_do_clipping;
- agg::rect_base<double> m_cliprect;
- double m_lastX;
- double m_lastY;
- bool m_moveto;
- double m_initX;
- double m_initY;
- bool m_has_init;
-
- public:
- PathClipper(VertexSource &source, bool do_clipping, double width, double height)
- : m_source(&source),
- m_do_clipping(do_clipping),
- m_cliprect(-1.0, -1.0, width + 1.0, height + 1.0),
- m_moveto(true),
- m_has_init(false)
- {
- // empty
- }
-
- PathClipper(VertexSource &source, bool do_clipping, const agg::rect_base<double> &rect)
- : m_source(&source),
- m_do_clipping(do_clipping),
- m_cliprect(rect),
- m_moveto(true),
- m_has_init(false)
- {
- m_cliprect.x1 -= 1.0;
- m_cliprect.y1 -= 1.0;
- m_cliprect.x2 += 1.0;
- m_cliprect.y2 += 1.0;
- }
-
- inline void rewind(unsigned path_id)
- {
- m_has_init = false;
- m_moveto = true;
- m_source->rewind(path_id);
- }
-
- int draw_clipped_line(double x0, double y0, double x1, double y1)
- {
- unsigned moved = agg::clip_line_segment(&x0, &y0, &x1, &y1, m_cliprect);
- // moved >= 4 - Fully clipped
- // moved & 1 != 0 - First point has been moved
- // moved & 2 != 0 - Second point has been moved
- if (moved < 4) {
- if (moved & 1 || m_moveto) {
- queue_push(agg::path_cmd_move_to, x0, y0);
- }
- queue_push(agg::path_cmd_line_to, x1, y1);
-
- m_moveto = false;
- return 1;
- }
-
- return 0;
- }
-
- unsigned vertex(double *x, double *y)
- {
- unsigned code;
- bool emit_moveto = false;
-
- if (m_do_clipping) {
- /* This is the slow path where we actually do clipping */
-
- if (queue_pop(&code, x, y)) {
- return code;
- }
-
- while ((code = m_source->vertex(x, y)) != agg::path_cmd_stop) {
- emit_moveto = false;
-
- switch (code) {
- case (agg::path_cmd_end_poly | agg::path_flags_close):
- if (m_has_init) {
- draw_clipped_line(m_lastX, m_lastY, m_initX, m_initY);
- }
- queue_push(
- agg::path_cmd_end_poly | agg::path_flags_close,
- m_lastX, m_lastY);
- goto exit_loop;
-
- case agg::path_cmd_move_to:
-
- // was the last command a moveto (and we have
- // seen at least one command ?
- // if so, shove it in the queue if in clip box
- if (m_moveto && m_has_init &&
- m_lastX >= m_cliprect.x1 &&
- m_lastX <= m_cliprect.x2 &&
- m_lastY >= m_cliprect.y1 &&
- m_lastY <= m_cliprect.y2) {
- // push the last moveto onto the queue
- queue_push(agg::path_cmd_move_to, m_lastX, m_lastY);
- // flag that we need to emit it
- emit_moveto = true;
- }
- // update the internal state for this moveto
- m_initX = m_lastX = *x;
- m_initY = m_lastY = *y;
- m_has_init = true;
- m_moveto = true;
- // if the last command was moveto exit the loop to emit the code
- if (emit_moveto) {
- goto exit_loop;
- }
- // else, break and get the next point
- break;
-
- case agg::path_cmd_line_to:
- if (draw_clipped_line(m_lastX, m_lastY, *x, *y)) {
- m_lastX = *x;
- m_lastY = *y;
- goto exit_loop;
- }
- m_lastX = *x;
- m_lastY = *y;
- break;
-
- default:
- if (m_moveto) {
- queue_push(agg::path_cmd_move_to, m_lastX, m_lastY);
- m_moveto = false;
- }
-
- queue_push(code, *x, *y);
- m_lastX = *x;
- m_lastY = *y;
- goto exit_loop;
- }
- }
-
- exit_loop:
-
- if (queue_pop(&code, x, y)) {
- return code;
- }
-
- if (m_moveto &&
- m_lastX >= m_cliprect.x1 &&
- m_lastX <= m_cliprect.x2 &&
- m_lastY >= m_cliprect.y1 &&
- m_lastY <= m_cliprect.y2) {
- *x = m_lastX;
- *y = m_lastY;
- m_moveto = false;
- return agg::path_cmd_move_to;
- }
-
- return agg::path_cmd_stop;
- } else {
- // If not doing any clipping, just pass along the vertices
- // verbatim
- return m_source->vertex(x, y);
- }
- }
-};
-
-/************************************************************
- PathSnapper rounds vertices to their nearest center-pixels. This
- makes rectilinear paths (rectangles, horizontal and vertical lines
- etc.) look much cleaner.
-*/
-enum e_snap_mode {
- SNAP_AUTO,
- SNAP_FALSE,
- SNAP_TRUE
-};
-
-template <class VertexSource>
-class PathSnapper
-{
- private:
- VertexSource *m_source;
- bool m_snap;
- double m_snap_value;
-
- static bool should_snap(VertexSource &path, e_snap_mode snap_mode, unsigned total_vertices)
- {
- // If this contains only straight horizontal or vertical lines, it should be
- // snapped to the nearest pixels
- double x0 = 0, y0 = 0, x1 = 0, y1 = 0;
- unsigned code;
-
- switch (snap_mode) {
- case SNAP_AUTO:
- if (total_vertices > 1024) {
- return false;
- }
-
- code = path.vertex(&x0, &y0);
- if (code == agg::path_cmd_stop) {
- return false;
- }
-
- while ((code = path.vertex(&x1, &y1)) != agg::path_cmd_stop) {
- switch (code) {
- case agg::path_cmd_curve3:
- case agg::path_cmd_curve4:
- return false;
- case agg::path_cmd_line_to:
- if (!(fabs(x0 - x1) < 1e-4 || fabs(y0 - y1) < 1e-4)) {
- return false;
- }
- }
- x0 = x1;
- y0 = y1;
- }
-
- return true;
- case SNAP_FALSE:
- return false;
- case SNAP_TRUE:
- return true;
- }
-
- return false;
- }
-
- public:
- /*
- snap_mode should be one of:
- - SNAP_AUTO: Examine the path to determine if it should be snapped
- - SNAP_TRUE: Force snapping
- - SNAP_FALSE: No snapping
- */
- PathSnapper(VertexSource &source,
- e_snap_mode snap_mode,
- unsigned total_vertices = 15,
- double stroke_width = 0.0)
- : m_source(&source)
- {
- m_snap = should_snap(source, snap_mode, total_vertices);
-
- if (m_snap) {
- int is_odd = (int)mpl_round(stroke_width) % 2;
- m_snap_value = (is_odd) ? 0.5 : 0.0;
- }
-
- source.rewind(0);
- }
-
- inline void rewind(unsigned path_id)
- {
- m_source->rewind(path_id);
- }
-
- inline unsigned vertex(double *x, double *y)
- {
- unsigned code;
- code = m_source->vertex(x, y);
- if (m_snap && agg::is_vertex(code)) {
- *x = floor(*x + 0.5) + m_snap_value;
- *y = floor(*y + 0.5) + m_snap_value;
- }
- return code;
- }
-
- inline bool is_snapping()
- {
- return m_snap;
- }
-};
-
-/************************************************************
- PathSimplifier reduces the number of vertices in a dense path without
- changing its appearance.
-*/
-template <class VertexSource>
-class PathSimplifier : protected EmbeddedQueue<9>
-{
- public:
- /* Set simplify to true to perform simplification */
- PathSimplifier(VertexSource &source, bool do_simplify, double simplify_threshold)
- : m_source(&source),
- m_simplify(do_simplify),
- /* we square simplify_threshold so that we can compute
- norms without doing the square root every step. */
- m_simplify_threshold(simplify_threshold * simplify_threshold),
-
- m_moveto(true),
- m_after_moveto(false),
- m_clipped(false),
-
- // the x, y values from last iteration
- m_lastx(0.0),
- m_lasty(0.0),
-
- // the dx, dy comprising the original vector, used in conjunction
- // with m_currVecStart* to define the original vector.
- m_origdx(0.0),
- m_origdy(0.0),
-
- // the squared norm of the original vector
- m_origdNorm2(0.0),
-
- // maximum squared norm of vector in forward (parallel) direction
- m_dnorm2ForwardMax(0.0),
- // maximum squared norm of vector in backward (anti-parallel) direction
- m_dnorm2BackwardMax(0.0),
-
- // was the last point the furthest from lastWritten in the
- // forward (parallel) direction?
- m_lastForwardMax(false),
- // was the last point the furthest from lastWritten in the
- // backward (anti-parallel) direction?
- m_lastBackwardMax(false),
-
- // added to queue when _push is called
- m_nextX(0.0),
- m_nextY(0.0),
-
- // added to queue when _push is called if any backwards
- // (anti-parallel) vectors were observed
- m_nextBackwardX(0.0),
- m_nextBackwardY(0.0),
-
- // start of the current vector that is being simplified
- m_currVecStartX(0.0),
- m_currVecStartY(0.0)
- {
- // empty
- }
-
- inline void rewind(unsigned path_id)
- {
- queue_clear();
- m_moveto = true;
- m_source->rewind(path_id);
- }
-
- unsigned vertex(double *x, double *y)
- {
- unsigned cmd;
-
- /* The simplification algorithm doesn't support curves or compound paths
- so we just don't do it at all in that case... */
- if (!m_simplify) {
- return m_source->vertex(x, y);
- }
-
- /* idea: we can skip drawing many lines: we can combine
- sequential parallel lines into a
- single line instead of redrawing lines over the same
- points. The loop below works a bit like a state machine,
- where what it does depends on what it did in the last
- looping. To test whether sequential lines are close to
- parallel, I calculate the distance moved perpendicular to
- the last line. Once it gets too big, the lines cannot be
- combined. */
-
- /* This code was originally written by Allan Haldane and I
- have modified to work in-place -- meaning not creating an
- entirely new path list each time. In order to do that
- without too much additional code complexity, it keeps a
- small queue around so that multiple points can be emitted
- in a single call, and those points will be popped from the
- queue in subsequent calls. The following block will empty
- the queue before proceeding to the main loop below.
- -- Michael Droettboom */
-
- /* This code was originally written by Allan Haldane and
- updated by Michael Droettboom. I have modified it to
- handle anti-parallel vectors. This is done essentially
- the same way as parallel vectors, but requires a little
- additional book-keeping to track whether or not we have
- observed an anti-parallel vector during the current run.
- -- Kevin Rose */
-
- if (queue_pop(&cmd, x, y)) {
- return cmd;
- }
-
- /* The main simplification loop. The point is to consume only
- as many points as necessary until something has been added
- to the outbound queue, not to run through the entire path
- in one go. This eliminates the need to allocate and fill
- an entire additional path array on each draw. */
- while ((cmd = m_source->vertex(x, y)) != agg::path_cmd_stop) {
- /* if we are starting a new path segment, move to the first point
- + init */
-
- if (m_moveto || cmd == agg::path_cmd_move_to) {
- /* m_moveto check is not generally needed because
- m_source generates an initial moveto; but it is
- retained for safety in case circumstances arise
- where this is not true. */
- if (m_origdNorm2 != 0.0 && !m_after_moveto) {
- /* m_origdNorm2 is nonzero only if we have a
- vector; the m_after_moveto check ensures we
- push this vector to the queue only once. */
- _push(x, y);
- }
- m_after_moveto = true;
- m_lastx = *x;
- m_lasty = *y;
- m_moveto = false;
- m_origdNorm2 = 0.0;
- m_dnorm2BackwardMax = 0.0;
- m_clipped = true;
- if (queue_nonempty()) {
- /* If we did a push, empty the queue now. */
- break;
- }
- continue;
- }
- m_after_moveto = false;
-
- /* NOTE: We used to skip this very short segments, but if
- you have a lot of them cumulatively, you can miss
- maxima or minima in the data. */
-
- /* Don't render line segments less than one pixel long */
- /* if (fabs(*x - m_lastx) < 1.0 && fabs(*y - m_lasty) < 1.0) */
- /* { */
- /* continue; */
- /* } */
-
- /* if we have no orig vector, set it to this vector and
- continue. this orig vector is the reference vector we
- will build up the line to */
- if (m_origdNorm2 == 0.0) {
- if (m_clipped) {
- queue_push(agg::path_cmd_move_to, m_lastx, m_lasty);
- m_clipped = false;
- }
-
- m_origdx = *x - m_lastx;
- m_origdy = *y - m_lasty;
- m_origdNorm2 = m_origdx * m_origdx + m_origdy * m_origdy;
-
- // set all the variables to reflect this new orig vector
- m_dnorm2ForwardMax = m_origdNorm2;
- m_dnorm2BackwardMax = 0.0;
- m_lastForwardMax = true;
- m_lastBackwardMax = false;
-
- m_currVecStartX = m_lastx;
- m_currVecStartY = m_lasty;
- m_nextX = m_lastx = *x;
- m_nextY = m_lasty = *y;
- continue;
- }
-
- /* If got to here, then we have an orig vector and we just got
- a vector in the sequence. */
-
- /* Check that the perpendicular distance we have moved
- from the last written point compared to the line we are
- building is not too much. If o is the orig vector (we
- are building on), and v is the vector from the last
- written point to the current point, then the
- perpendicular vector is p = v - (o.v)o/(o.o)
- (here, a.b indicates the dot product of a and b). */
-
- /* get the v vector */
- double totdx = *x - m_currVecStartX;
- double totdy = *y - m_currVecStartY;
-
- /* get the dot product o.v */
- double totdot = m_origdx * totdx + m_origdy * totdy;
-
- /* get the para vector ( = (o.v)o/(o.o)) */
- double paradx = totdot * m_origdx / m_origdNorm2;
- double parady = totdot * m_origdy / m_origdNorm2;
-
- /* get the perp vector ( = v - para) */
- double perpdx = totdx - paradx;
- double perpdy = totdy - parady;
-
- /* get the squared norm of perp vector ( = p.p) */
- double perpdNorm2 = perpdx * perpdx + perpdy * perpdy;
-
- /* If the perpendicular vector is less than
- m_simplify_threshold pixels in size, then merge
- current x,y with the current vector */
- if (perpdNorm2 < m_simplify_threshold) {
- /* check if the current vector is parallel or
- anti-parallel to the orig vector. In either case,
- test if it is the longest of the vectors
- we are merging in that direction. If it is, then
- update the current vector in that direction. */
- double paradNorm2 = paradx * paradx + parady * parady;
-
- m_lastForwardMax = false;
- m_lastBackwardMax = false;
- if (totdot > 0.0) {
- if (paradNorm2 > m_dnorm2ForwardMax) {
- m_lastForwardMax = true;
- m_dnorm2ForwardMax = paradNorm2;
- m_nextX = *x;
- m_nextY = *y;
- }
- } else {
- if (paradNorm2 > m_dnorm2BackwardMax) {
- m_lastBackwardMax = true;
- m_dnorm2BackwardMax = paradNorm2;
- m_nextBackwardX = *x;
- m_nextBackwardY = *y;
- }
- }
-
- m_lastx = *x;
- m_lasty = *y;
- continue;
- }
-
- /* If we get here, then this vector was not similar enough to the
- line we are building, so we need to draw that line and start the
- next one. */
-
- /* If the line needs to extend in the opposite direction from the
- direction we are drawing in, move back to we start drawing from
- back there. */
- _push(x, y);
-
- break;
- }
-
- /* Fill the queue with the remaining vertices if we've finished the
- path in the above loop. */
- if (cmd == agg::path_cmd_stop) {
- if (m_origdNorm2 != 0.0) {
- queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to
- : agg::path_cmd_line_to,
- m_nextX,
- m_nextY);
- if (m_dnorm2BackwardMax > 0.0) {
- queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to
- : agg::path_cmd_line_to,
- m_nextBackwardX,
- m_nextBackwardY);
- }
- m_moveto = false;
- }
- queue_push((m_moveto || m_after_moveto) ? agg::path_cmd_move_to : agg::path_cmd_line_to,
- m_lastx,
- m_lasty);
- m_moveto = false;
- queue_push(agg::path_cmd_stop, 0.0, 0.0);
- }
-
- /* Return the first item in the queue, if any, otherwise
- indicate that we're done. */
- if (queue_pop(&cmd, x, y)) {
- return cmd;
- } else {
- return agg::path_cmd_stop;
- }
- }
-
- private:
- VertexSource *m_source;
- bool m_simplify;
- double m_simplify_threshold;
-
- bool m_moveto;
- bool m_after_moveto;
- bool m_clipped;
- double m_lastx, m_lasty;
-
- double m_origdx;
- double m_origdy;
- double m_origdNorm2;
- double m_dnorm2ForwardMax;
- double m_dnorm2BackwardMax;
- bool m_lastForwardMax;
- bool m_lastBackwardMax;
- double m_nextX;
- double m_nextY;
- double m_nextBackwardX;
- double m_nextBackwardY;
- double m_currVecStartX;
- double m_currVecStartY;
-
- inline void _push(double *x, double *y)
- {
- bool needToPushBack = (m_dnorm2BackwardMax > 0.0);
-
- /* If we observed any backward (anti-parallel) vectors, then
- we need to push both forward and backward vectors. */
- if (needToPushBack) {
- /* If the last vector seen was the maximum in the forward direction,
- then we need to push the forward after the backward. Otherwise,
- the last vector seen was the maximum in the backward direction,
- or somewhere in between, either way we are safe pushing forward
- before backward. */
- 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);
- } else {
- queue_push(agg::path_cmd_line_to, m_nextX, m_nextY);
- queue_push(agg::path_cmd_line_to, m_nextBackwardX, m_nextBackwardY);
- }
- } else {
- /* If we did not observe any backwards vectors, just push forward. */
- queue_push(agg::path_cmd_line_to, m_nextX, m_nextY);
- }
-
- /* If we clipped some segments between this line and the next line
- we are starting, we also need to move to the last point. */
- if (m_clipped) {
- queue_push(agg::path_cmd_move_to, m_lastx, m_lasty);
- } else if ((!m_lastForwardMax) && (!m_lastBackwardMax)) {
- /* If the last line was not the longest line, then move
- back to the end point of the last line in the
- sequence. Only do this if not clipped, since in that
- case lastx,lasty is not part of the line just drawn. */
-
- /* Would be move_to if not for the artifacts */
- queue_push(agg::path_cmd_line_to, m_lastx, m_lasty);
- }
-
- /* Now reset all the variables to get ready for the next line */
- m_origdx = *x - m_lastx;
- m_origdy = *y - m_lasty;
- m_origdNorm2 = m_origdx * m_origdx + m_origdy * m_origdy;
-
- m_dnorm2ForwardMax = m_origdNorm2;
- m_lastForwardMax = true;
- m_currVecStartX = m_queue[m_queue_write - 1].x;
- m_currVecStartY = m_queue[m_queue_write - 1].y;
- m_lastx = m_nextX = *x;
- m_lasty = m_nextY = *y;
- m_dnorm2BackwardMax = 0.0;
- m_lastBackwardMax = false;
-
- m_clipped = false;
- }
-};
-
-template <class VertexSource>
-class Sketch
-{
- public:
- /*
- scale: the scale of the wiggle perpendicular to the original
- line (in pixels)
-
- length: the base wavelength of the wiggle along the
- original line (in pixels)
-
- randomness: the factor that the sketch length will randomly
- shrink and expand.
- */
- Sketch(VertexSource &source, double scale, double length, double randomness)
- : m_source(&source),
- m_scale(scale),
- m_length(length),
- m_randomness(randomness),
- m_segmented(source),
- m_last_x(0.0),
- m_last_y(0.0),
- m_has_last(false),
- m_p(0.0),
- m_rand(0)
- {
- rewind(0);
- }
-
- unsigned vertex(double *x, double *y)
- {
- if (m_scale == 0.0) {
- return m_source->vertex(x, y);
- }
-
- unsigned code = m_segmented.vertex(x, y);
-
- if (code == agg::path_cmd_move_to) {
- m_has_last = false;
- m_p = 0.0;
- }
-
- if (m_has_last) {
- // We want the "cursor" along the sine wave to move at a
- // random rate.
- double d_rand = m_rand.get_double();
- double d_M_PI = 3.14159265358979323846;
- m_p += pow(m_randomness, d_rand * 2.0 - 1.0);
- double r = sin(m_p / (m_length / (d_M_PI * 2.0))) * m_scale;
- double den = m_last_x - *x;
- double num = m_last_y - *y;
- double len = num * num + den * den;
- m_last_x = *x;
- m_last_y = *y;
- if (len != 0) {
- len = sqrt(len);
- *x += r * num / len;
- *y += r * -den / len;
- }
- } else {
- m_last_x = *x;
- m_last_y = *y;
- }
-
- m_has_last = true;
-
- return code;
- }
-
- inline void rewind(unsigned path_id)
- {
- m_has_last = false;
- m_p = 0.0;
- if (m_scale != 0.0) {
- m_rand.seed(0);
- m_segmented.rewind(path_id);
- } else {
- m_source->rewind(path_id);
- }
- }
-
- private:
- VertexSource *m_source;
- double m_scale;
- double m_length;
- double m_randomness;
- agg::conv_segmentator<VertexSource> m_segmented;
- double m_last_x;
- double m_last_y;
- bool m_has_last;
- double m_p;
- RandomNumberGenerator m_rand;
-};
-
-#endif // __PATH_CONVERTERS_H__