aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/matplotlib/py2/src/_path.h
diff options
context:
space:
mode:
authorshumkovnd <shumkovnd@yandex-team.com>2023-11-10 14:39:34 +0300
committershumkovnd <shumkovnd@yandex-team.com>2023-11-10 16:42:24 +0300
commit77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch)
treec51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/matplotlib/py2/src/_path.h
parentdd6d20cadb65582270ac23f4b3b14ae189704b9d (diff)
downloadydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/matplotlib/py2/src/_path.h')
-rw-r--r--contrib/python/matplotlib/py2/src/_path.h1316
1 files changed, 1316 insertions, 0 deletions
diff --git a/contrib/python/matplotlib/py2/src/_path.h b/contrib/python/matplotlib/py2/src/_path.h
new file mode 100644
index 0000000000..76f1894c4a
--- /dev/null
+++ b/contrib/python/matplotlib/py2/src/_path.h
@@ -0,0 +1,1316 @@
+/* -*- mode: c++; c-basic-offset: 4 -*- */
+
+#ifndef __PATH_H__
+#define __PATH_H__
+
+#include <limits>
+#include <math.h>
+#include <vector>
+#include <cmath>
+#include <algorithm>
+
+#include "agg_conv_contour.h"
+#include "agg_conv_curve.h"
+#include "agg_conv_stroke.h"
+#include "agg_conv_transform.h"
+#include "agg_path_storage.h"
+#include "agg_trans_affine.h"
+
+#include "path_converters.h"
+#include "_backend_agg_basic_types.h"
+#include "numpy_cpp.h"
+
+struct XY
+{
+ double x;
+ double y;
+
+ XY(double x_, double y_) : x(x_), y(y_)
+ {
+ }
+
+ bool operator==(const XY& o)
+ {
+ return (x == o.x && y == o.y);
+ }
+
+ bool operator!=(const XY& o)
+ {
+ return (x != o.x || y != o.y);
+ }
+};
+
+typedef std::vector<XY> Polygon;
+
+void _finalize_polygon(std::vector<Polygon> &result, int closed_only)
+{
+ if (result.size() == 0) {
+ return;
+ }
+
+ Polygon &polygon = result.back();
+
+ /* Clean up the last polygon in the result. */
+ if (polygon.size() == 0) {
+ result.pop_back();
+ } else if (closed_only) {
+ if (polygon.size() < 3) {
+ result.pop_back();
+ } else if (polygon.front() != polygon.back()) {
+ polygon.push_back(polygon.front());
+ }
+ }
+}
+
+//
+// The following function was found in the Agg 2.3 examples (interactive_polygon.cpp).
+// It has been generalized to work on (possibly curved) polylines, rather than
+// just polygons. The original comments have been kept intact.
+// -- Michael Droettboom 2007-10-02
+//
+//======= Crossings Multiply algorithm of InsideTest ========================
+//
+// By Eric Haines, 3D/Eye Inc, erich@eye.com
+//
+// This version is usually somewhat faster than the original published in
+// Graphics Gems IV; by turning the division for testing the X axis crossing
+// into a tricky multiplication test this part of the test became faster,
+// which had the additional effect of making the test for "both to left or
+// both to right" a bit slower for triangles than simply computing the
+// intersection each time. The main increase is in triangle testing speed,
+// which was about 15% faster; all other polygon complexities were pretty much
+// the same as before. On machines where division is very expensive (not the
+// case on the HP 9000 series on which I tested) this test should be much
+// faster overall than the old code. Your mileage may (in fact, will) vary,
+// depending on the machine and the test data, but in general I believe this
+// code is both shorter and faster. This test was inspired by unpublished
+// Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
+// Related work by Samosky is in:
+//
+// Samosky, Joseph, "SectionView: A system for interactively specifying and
+// visualizing sections through three-dimensional medical image data",
+// M.S. Thesis, Department of Electrical Engineering and Computer Science,
+// Massachusetts Institute of Technology, 1993.
+//
+// Shoot a test ray along +X axis. The strategy is to compare vertex Y values
+// to the testing point's Y and quickly discard edges which are entirely to one
+// side of the test ray. Note that CONVEX and WINDING code can be added as
+// for the CrossingsTest() code; it is left out here for clarity.
+//
+// Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
+// _point_, returns 1 if inside, 0 if outside.
+template <class PathIterator, class PointArray, class ResultArray>
+void point_in_path_impl(PointArray &points, PathIterator &path, ResultArray &inside_flag)
+{
+ uint8_t yflag1;
+ double vtx0, vty0, vtx1, vty1;
+ double tx, ty;
+ double sx, sy;
+ double x, y;
+ size_t i;
+ bool all_done;
+
+ size_t n = points.size();
+
+ std::vector<uint8_t> yflag0(n);
+ std::vector<uint8_t> subpath_flag(n);
+
+ path.rewind(0);
+
+ for (i = 0; i < n; ++i) {
+ inside_flag[i] = 0;
+ }
+
+ unsigned code = 0;
+ do {
+ if (code != agg::path_cmd_move_to) {
+ code = path.vertex(&x, &y);
+ if (code == agg::path_cmd_stop ||
+ (code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) {
+ continue;
+ }
+ }
+
+ sx = vtx0 = vtx1 = x;
+ sy = vty0 = vty1 = y;
+
+ for (i = 0; i < n; ++i) {
+ ty = points(i, 1);
+
+ if (std::isfinite(ty)) {
+ // get test bit for above/below X axis
+ yflag0[i] = (vty0 >= ty);
+
+ subpath_flag[i] = 0;
+ }
+ }
+
+ do {
+ code = path.vertex(&x, &y);
+
+ // The following cases denote the beginning on a new subpath
+ if (code == agg::path_cmd_stop ||
+ (code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) {
+ x = sx;
+ y = sy;
+ } else if (code == agg::path_cmd_move_to) {
+ break;
+ }
+
+ for (i = 0; i < n; ++i) {
+ tx = points(i, 0);
+ ty = points(i, 1);
+
+ if (!(std::isfinite(tx) && std::isfinite(ty))) {
+ continue;
+ }
+
+ yflag1 = (vty1 >= ty);
+ // Check if endpoints straddle (are on opposite sides) of
+ // X axis (i.e. the Y's differ); if so, +X ray could
+ // intersect this edge. The old test also checked whether
+ // the endpoints are both to the right or to the left of
+ // the test point. However, given the faster intersection
+ // point computation used below, this test was found to be
+ // a break-even proposition for most polygons and a loser
+ // for triangles (where 50% or more of the edges which
+ // survive this test will cross quadrants and so have to
+ // have the X intersection computed anyway). I credit
+ // Joseph Samosky with inspiring me to try dropping the
+ // "both left or both right" part of my code.
+ if (yflag0[i] != yflag1) {
+ // Check intersection of pgon segment with +X ray.
+ // Note if >= point's X; if so, the ray hits it. The
+ // division operation is avoided for the ">=" test by
+ // checking the sign of the first vertex wrto the test
+ // point; idea inspired by Joseph Samosky's and Mark
+ // Haigh-Hutchinson's different polygon inclusion
+ // tests.
+ if (((vty1 - ty) * (vtx0 - vtx1) >= (vtx1 - tx) * (vty0 - vty1)) == yflag1) {
+ subpath_flag[i] ^= 1;
+ }
+ }
+
+ // Move to the next pair of vertices, retaining info as
+ // possible.
+ yflag0[i] = yflag1;
+ }
+
+ vtx0 = vtx1;
+ vty0 = vty1;
+
+ vtx1 = x;
+ vty1 = y;
+ } while (code != agg::path_cmd_stop &&
+ (code & agg::path_cmd_end_poly) != agg::path_cmd_end_poly);
+
+ all_done = true;
+ for (i = 0; i < n; ++i) {
+ tx = points(i, 0);
+ ty = points(i, 1);
+
+ if (!(std::isfinite(tx) && std::isfinite(ty))) {
+ continue;
+ }
+
+ yflag1 = (vty1 >= ty);
+ if (yflag0[i] != yflag1) {
+ if (((vty1 - ty) * (vtx0 - vtx1) >= (vtx1 - tx) * (vty0 - vty1)) == yflag1) {
+ subpath_flag[i] = subpath_flag[i] ^ true;
+ }
+ }
+ inside_flag[i] |= subpath_flag[i];
+ if (inside_flag[i] == 0) {
+ all_done = false;
+ }
+ }
+
+ if (all_done) {
+ break;
+ }
+ } while (code != agg::path_cmd_stop);
+}
+
+template <class PathIterator, class PointArray, class ResultArray>
+inline void points_in_path(PointArray &points,
+ const double r,
+ PathIterator &path,
+ agg::trans_affine &trans,
+ ResultArray &result)
+{
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> no_nans_t;
+ typedef agg::conv_curve<no_nans_t> curve_t;
+ typedef agg::conv_contour<curve_t> contour_t;
+
+ size_t i;
+ for (i = 0; i < points.size(); ++i) {
+ result[i] = false;
+ }
+
+ if (path.total_vertices() < 3) {
+ return;
+ }
+
+ transformed_path_t trans_path(path, trans);
+ no_nans_t no_nans_path(trans_path, true, path.has_curves());
+ curve_t curved_path(no_nans_path);
+ if (r != 0.0) {
+ contour_t contoured_path(curved_path);
+ contoured_path.width(r);
+ point_in_path_impl(points, contoured_path, result);
+ } else {
+ point_in_path_impl(points, curved_path, result);
+ }
+}
+
+template <class PathIterator>
+inline bool point_in_path(
+ double x, double y, const double r, PathIterator &path, agg::trans_affine &trans)
+{
+ npy_intp shape[] = {1, 2};
+ numpy::array_view<double, 2> points(shape);
+ points(0, 0) = x;
+ points(0, 1) = y;
+
+ int result[1];
+ result[0] = 0;
+
+ points_in_path(points, r, path, trans, result);
+
+ return (bool)result[0];
+}
+
+template <class PathIterator, class PointArray, class ResultArray>
+void points_on_path(PointArray &points,
+ const double r,
+ PathIterator &path,
+ agg::trans_affine &trans,
+ ResultArray result)
+{
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> no_nans_t;
+ typedef agg::conv_curve<no_nans_t> curve_t;
+ typedef agg::conv_stroke<curve_t> stroke_t;
+
+ size_t i;
+ for (i = 0; i < points.size(); ++i) {
+ result[i] = false;
+ }
+
+ transformed_path_t trans_path(path, trans);
+ no_nans_t nan_removed_path(trans_path, true, path.has_curves());
+ curve_t curved_path(nan_removed_path);
+ stroke_t stroked_path(curved_path);
+ stroked_path.width(r * 2.0);
+ point_in_path_impl(points, stroked_path, result);
+}
+
+template <class PathIterator>
+inline bool point_on_path(
+ double x, double y, const double r, PathIterator &path, agg::trans_affine &trans)
+{
+ npy_intp shape[] = {1, 2};
+ numpy::array_view<double, 2> points(shape);
+ points(0, 0) = x;
+ points(0, 1) = y;
+
+ int result[1];
+ result[0] = 0;
+
+ points_on_path(points, r, path, trans, result);
+
+ return (bool)result[0];
+}
+
+struct extent_limits
+{
+ double x0;
+ double y0;
+ double x1;
+ double y1;
+ double xm;
+ double ym;
+};
+
+void reset_limits(extent_limits &e)
+{
+ e.x0 = std::numeric_limits<double>::infinity();
+ e.y0 = std::numeric_limits<double>::infinity();
+ e.x1 = -std::numeric_limits<double>::infinity();
+ e.y1 = -std::numeric_limits<double>::infinity();
+ /* xm and ym are the minimum positive values in the data, used
+ by log scaling */
+ e.xm = std::numeric_limits<double>::infinity();
+ e.ym = std::numeric_limits<double>::infinity();
+}
+
+inline void update_limits(double x, double y, extent_limits &e)
+{
+ if (x < e.x0)
+ e.x0 = x;
+ if (y < e.y0)
+ e.y0 = y;
+ if (x > e.x1)
+ e.x1 = x;
+ if (y > e.y1)
+ e.y1 = y;
+ /* xm and ym are the minimum positive values in the data, used
+ by log scaling */
+ if (x > 0.0 && x < e.xm)
+ e.xm = x;
+ if (y > 0.0 && y < e.ym)
+ e.ym = y;
+}
+
+template <class PathIterator>
+void update_path_extents(PathIterator &path, agg::trans_affine &trans, extent_limits &extents)
+{
+ typedef agg::conv_transform<PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removed_t;
+ double x, y;
+ unsigned code;
+
+ transformed_path_t tpath(path, trans);
+ nan_removed_t nan_removed(tpath, true, path.has_curves());
+
+ nan_removed.rewind(0);
+
+ while ((code = nan_removed.vertex(&x, &y)) != agg::path_cmd_stop) {
+ if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) {
+ continue;
+ }
+ update_limits(x, y, extents);
+ }
+}
+
+template <class PathGenerator, class TransformArray, class OffsetArray>
+void get_path_collection_extents(agg::trans_affine &master_transform,
+ PathGenerator &paths,
+ TransformArray &transforms,
+ OffsetArray &offsets,
+ agg::trans_affine &offset_trans,
+ extent_limits &extent)
+{
+ if (offsets.size() != 0 && offsets.dim(1) != 2) {
+ throw std::runtime_error("Offsets array must be Nx2");
+ }
+
+ size_t Npaths = paths.size();
+ size_t Noffsets = offsets.size();
+ size_t N = std::max(Npaths, Noffsets);
+ size_t Ntransforms = std::min(transforms.size(), N);
+ size_t i;
+
+ agg::trans_affine trans;
+
+ reset_limits(extent);
+
+ for (i = 0; i < N; ++i) {
+ typename PathGenerator::path_iterator path(paths(i % Npaths));
+ if (Ntransforms) {
+ size_t ti = i % Ntransforms;
+ trans = agg::trans_affine(transforms(ti, 0, 0),
+ transforms(ti, 1, 0),
+ transforms(ti, 0, 1),
+ transforms(ti, 1, 1),
+ transforms(ti, 0, 2),
+ transforms(ti, 1, 2));
+ } else {
+ trans = master_transform;
+ }
+
+ if (Noffsets) {
+ double xo = offsets(i % Noffsets, 0);
+ double yo = offsets(i % Noffsets, 1);
+ offset_trans.transform(&xo, &yo);
+ trans *= agg::trans_affine_translation(xo, yo);
+ }
+
+ update_path_extents(path, trans, extent);
+ }
+}
+
+template <class PathGenerator, class TransformArray, class OffsetArray>
+void point_in_path_collection(double x,
+ double y,
+ double radius,
+ agg::trans_affine &master_transform,
+ PathGenerator &paths,
+ TransformArray &transforms,
+ OffsetArray &offsets,
+ agg::trans_affine &offset_trans,
+ bool filled,
+ e_offset_position offset_position,
+ std::vector<int> &result)
+{
+ size_t Npaths = paths.size();
+
+ if (Npaths == 0) {
+ return;
+ }
+
+ size_t Noffsets = offsets.size();
+ size_t N = std::max(Npaths, Noffsets);
+ size_t Ntransforms = std::min(transforms.size(), N);
+ size_t i;
+
+ agg::trans_affine trans;
+
+ for (i = 0; i < N; ++i) {
+ typename PathGenerator::path_iterator path = paths(i % Npaths);
+
+ if (Ntransforms) {
+ size_t ti = i % Ntransforms;
+ trans = agg::trans_affine(transforms(ti, 0, 0),
+ transforms(ti, 1, 0),
+ transforms(ti, 0, 1),
+ transforms(ti, 1, 1),
+ transforms(ti, 0, 2),
+ transforms(ti, 1, 2));
+ trans *= master_transform;
+ } else {
+ trans = master_transform;
+ }
+
+ if (Noffsets) {
+ double xo = offsets(i % Noffsets, 0);
+ double yo = offsets(i % Noffsets, 1);
+ offset_trans.transform(&xo, &yo);
+ if (offset_position == OFFSET_POSITION_DATA) {
+ trans = agg::trans_affine_translation(xo, yo) * trans;
+ } else {
+ trans *= agg::trans_affine_translation(xo, yo);
+ }
+ }
+
+ if (filled) {
+ if (point_in_path(x, y, radius, path, trans)) {
+ result.push_back(i);
+ }
+ } else {
+ if (point_on_path(x, y, radius, path, trans)) {
+ result.push_back(i);
+ }
+ }
+ }
+}
+
+template <class PathIterator1, class PathIterator2>
+bool path_in_path(PathIterator1 &a,
+ agg::trans_affine &atrans,
+ PathIterator2 &b,
+ agg::trans_affine &btrans)
+{
+ typedef agg::conv_transform<PathIterator2> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> no_nans_t;
+ typedef agg::conv_curve<no_nans_t> curve_t;
+
+ if (a.total_vertices() < 3) {
+ return false;
+ }
+
+ transformed_path_t b_path_trans(b, btrans);
+ no_nans_t b_no_nans(b_path_trans, true, b.has_curves());
+ curve_t b_curved(b_no_nans);
+
+ double x, y;
+ b_curved.rewind(0);
+ while (b_curved.vertex(&x, &y) != agg::path_cmd_stop) {
+ if (!point_in_path(x, y, 0.0, a, atrans)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/** The clip_path_to_rect code here is a clean-room implementation of
+ the Sutherland-Hodgman clipping algorithm described here:
+
+ http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm
+*/
+
+namespace clip_to_rect_filters
+{
+/* There are four different passes needed to create/remove
+ vertices (one for each side of the rectangle). The differences
+ between those passes are encapsulated in these functor classes.
+*/
+struct bisectx
+{
+ double m_x;
+
+ bisectx(double x) : m_x(x)
+ {
+ }
+
+ inline void bisect(double sx, double sy, double px, double py, double *bx, double *by) const
+ {
+ *bx = m_x;
+ double dx = px - sx;
+ double dy = py - sy;
+ *by = sy + dy * ((m_x - sx) / dx);
+ }
+};
+
+struct xlt : public bisectx
+{
+ xlt(double x) : bisectx(x)
+ {
+ }
+
+ inline bool is_inside(double x, double y) const
+ {
+ return x <= m_x;
+ }
+};
+
+struct xgt : public bisectx
+{
+ xgt(double x) : bisectx(x)
+ {
+ }
+
+ inline bool is_inside(double x, double y) const
+ {
+ return x >= m_x;
+ }
+};
+
+struct bisecty
+{
+ double m_y;
+
+ bisecty(double y) : m_y(y)
+ {
+ }
+
+ inline void bisect(double sx, double sy, double px, double py, double *bx, double *by) const
+ {
+ *by = m_y;
+ double dx = px - sx;
+ double dy = py - sy;
+ *bx = sx + dx * ((m_y - sy) / dy);
+ }
+};
+
+struct ylt : public bisecty
+{
+ ylt(double y) : bisecty(y)
+ {
+ }
+
+ inline bool is_inside(double x, double y) const
+ {
+ return y <= m_y;
+ }
+};
+
+struct ygt : public bisecty
+{
+ ygt(double y) : bisecty(y)
+ {
+ }
+
+ inline bool is_inside(double x, double y) const
+ {
+ return y >= m_y;
+ }
+};
+}
+
+template <class Filter>
+inline void clip_to_rect_one_step(const Polygon &polygon, Polygon &result, const Filter &filter)
+{
+ double sx, sy, px, py, bx, by;
+ bool sinside, pinside;
+ result.clear();
+
+ if (polygon.size() == 0) {
+ return;
+ }
+
+ sx = polygon.back().x;
+ sy = polygon.back().y;
+ for (Polygon::const_iterator i = polygon.begin(); i != polygon.end(); ++i) {
+ px = i->x;
+ py = i->y;
+
+ sinside = filter.is_inside(sx, sy);
+ pinside = filter.is_inside(px, py);
+
+ if (sinside ^ pinside) {
+ filter.bisect(sx, sy, px, py, &bx, &by);
+ result.push_back(XY(bx, by));
+ }
+
+ if (pinside) {
+ result.push_back(XY(px, py));
+ }
+
+ sx = px;
+ sy = py;
+ }
+}
+
+template <class PathIterator>
+void
+clip_path_to_rect(PathIterator &path, agg::rect_d &rect, bool inside, std::vector<Polygon> &results)
+{
+ double xmin, ymin, xmax, ymax;
+ if (rect.x1 < rect.x2) {
+ xmin = rect.x1;
+ xmax = rect.x2;
+ } else {
+ xmin = rect.x2;
+ xmax = rect.x1;
+ }
+
+ if (rect.y1 < rect.y2) {
+ ymin = rect.y1;
+ ymax = rect.y2;
+ } else {
+ ymin = rect.y2;
+ ymax = rect.y1;
+ }
+
+ if (!inside) {
+ std::swap(xmin, xmax);
+ std::swap(ymin, ymax);
+ }
+
+ typedef agg::conv_curve<PathIterator> curve_t;
+ curve_t curve(path);
+
+ Polygon polygon1, polygon2;
+ double x = 0, y = 0;
+ unsigned code = 0;
+ curve.rewind(0);
+
+ do {
+ // Grab the next subpath and store it in polygon1
+ polygon1.clear();
+ do {
+ if (code == agg::path_cmd_move_to) {
+ polygon1.push_back(XY(x, y));
+ }
+
+ code = curve.vertex(&x, &y);
+
+ if (code == agg::path_cmd_stop) {
+ break;
+ }
+
+ if (code != agg::path_cmd_move_to) {
+ polygon1.push_back(XY(x, y));
+ }
+ } while ((code & agg::path_cmd_end_poly) != agg::path_cmd_end_poly);
+
+ // The result of each step is fed into the next (note the
+ // swapping of polygon1 and polygon2 at each step).
+ clip_to_rect_one_step(polygon1, polygon2, clip_to_rect_filters::xlt(xmax));
+ clip_to_rect_one_step(polygon2, polygon1, clip_to_rect_filters::xgt(xmin));
+ clip_to_rect_one_step(polygon1, polygon2, clip_to_rect_filters::ylt(ymax));
+ clip_to_rect_one_step(polygon2, polygon1, clip_to_rect_filters::ygt(ymin));
+
+ // Empty polygons aren't very useful, so skip them
+ if (polygon1.size()) {
+ _finalize_polygon(results, 1);
+ results.push_back(polygon1);
+ }
+ } while (code != agg::path_cmd_stop);
+
+ _finalize_polygon(results, 1);
+}
+
+template <class VerticesArray, class ResultArray>
+void affine_transform_2d(VerticesArray &vertices, agg::trans_affine &trans, ResultArray &result)
+{
+ if (vertices.size() != 0 && vertices.dim(1) != 2) {
+ throw std::runtime_error("Invalid vertices array.");
+ }
+
+ size_t n = vertices.size();
+ double x;
+ double y;
+ double t0;
+ double t1;
+ double t;
+
+ for (size_t i = 0; i < n; ++i) {
+ x = vertices(i, 0);
+ y = vertices(i, 1);
+
+ t0 = trans.sx * x;
+ t1 = trans.shx * y;
+ t = t0 + t1 + trans.tx;
+ result(i, 0) = t;
+
+ t0 = trans.shy * x;
+ t1 = trans.sy * y;
+ t = t0 + t1 + trans.ty;
+ result(i, 1) = t;
+ }
+}
+
+template <class VerticesArray, class ResultArray>
+void affine_transform_1d(VerticesArray &vertices, agg::trans_affine &trans, ResultArray &result)
+{
+ if (vertices.dim(0) != 2) {
+ throw std::runtime_error("Invalid vertices array.");
+ }
+
+ double x;
+ double y;
+ double t0;
+ double t1;
+ double t;
+
+ x = vertices(0);
+ y = vertices(1);
+
+ t0 = trans.sx * x;
+ t1 = trans.shx * y;
+ t = t0 + t1 + trans.tx;
+ result(0) = t;
+
+ t0 = trans.shy * x;
+ t1 = trans.sy * y;
+ t = t0 + t1 + trans.ty;
+ result(1) = t;
+}
+
+template <class BBoxArray>
+int count_bboxes_overlapping_bbox(agg::rect_d &a, BBoxArray &bboxes)
+{
+ agg::rect_d b;
+ int count = 0;
+
+ if (a.x2 < a.x1) {
+ std::swap(a.x1, a.x2);
+ }
+ if (a.y2 < a.y1) {
+ std::swap(a.y1, a.y2);
+ }
+
+ size_t num_bboxes = bboxes.size();
+ for (size_t i = 0; i < num_bboxes; ++i) {
+ b = agg::rect_d(bboxes(i, 0, 0), bboxes(i, 0, 1), bboxes(i, 1, 0), bboxes(i, 1, 1));
+
+ if (b.x2 < b.x1) {
+ std::swap(b.x1, b.x2);
+ }
+ if (b.y2 < b.y1) {
+ std::swap(b.y1, b.y2);
+ }
+ if (!((b.x2 <= a.x1) || (b.y2 <= a.y1) || (b.x1 >= a.x2) || (b.y1 >= a.y2))) {
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+inline bool segments_intersect(const double &x1,
+ const double &y1,
+ const double &x2,
+ const double &y2,
+ const double &x3,
+ const double &y3,
+ const double &x4,
+ const double &y4)
+{
+ double den = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1));
+ if (den == 0.0) {
+ return false;
+ }
+
+ double n1 = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3));
+ double n2 = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3));
+
+ double u1 = n1 / den;
+ double u2 = n2 / den;
+
+ return (u1 >= 0.0 && u1 <= 1.0 && u2 >= 0.0 && u2 <= 1.0);
+}
+
+template <class PathIterator1, class PathIterator2>
+bool path_intersects_path(PathIterator1 &p1, PathIterator2 &p2)
+{
+ typedef PathNanRemover<py::PathIterator> no_nans_t;
+ typedef agg::conv_curve<no_nans_t> curve_t;
+
+ if (p1.total_vertices() < 2 || p2.total_vertices() < 2) {
+ return false;
+ }
+
+ no_nans_t n1(p1, true, p1.has_curves());
+ no_nans_t n2(p2, true, p2.has_curves());
+
+ curve_t c1(n1);
+ curve_t c2(n2);
+
+ double x11, y11, x12, y12;
+ double x21, y21, x22, y22;
+
+ c1.vertex(&x11, &y11);
+ while (c1.vertex(&x12, &y12) != agg::path_cmd_stop) {
+ c2.rewind(0);
+ c2.vertex(&x21, &y21);
+ while (c2.vertex(&x22, &y22) != agg::path_cmd_stop) {
+ if (segments_intersect(x11, y11, x12, y12, x21, y21, x22, y22)) {
+ return true;
+ }
+ x21 = x22;
+ y21 = y22;
+ }
+ x11 = x12;
+ y11 = y12;
+ }
+
+ return false;
+}
+
+// returns whether the segment from (x1,y1) to (x2,y2)
+// intersects the rectangle centered at (cx,cy) with size (w,h)
+// see doc/segment_intersects_rectangle.svg for a more detailed explanation
+inline bool segment_intersects_rectangle(double x1, double y1,
+ double x2, double y2,
+ double cx, double cy,
+ double w, double h)
+{
+ return fabs(x1 + x2 - 2.0 * cx) < fabs(x1 - x2) + w &&
+ fabs(y1 + y2 - 2.0 * cy) < fabs(y1 - y2) + h &&
+ 2.0 * fabs((x1 - cx) * (y1 - y2) - (y1 - cy) * (x1 - x2)) <
+ w * fabs(y1 - y2) + h * fabs(x1 - x2);
+}
+
+template <class PathIterator>
+bool path_intersects_rectangle(PathIterator &path,
+ double rect_x1, double rect_y1,
+ double rect_x2, double rect_y2,
+ bool filled)
+{
+ typedef PathNanRemover<py::PathIterator> no_nans_t;
+ typedef agg::conv_curve<no_nans_t> curve_t;
+
+ if (path.total_vertices() == 0) {
+ return false;
+ }
+
+ no_nans_t no_nans(path, true, path.has_curves());
+ curve_t curve(no_nans);
+
+ double cx = (rect_x1 + rect_x2) * 0.5, cy = (rect_y1 + rect_y2) * 0.5;
+ double w = fabs(rect_x1 - rect_x2), h = fabs(rect_y1 - rect_y2);
+
+ double x1, y1, x2, y2;
+
+ curve.vertex(&x1, &y1);
+ if (2.0 * fabs(x1 - cx) <= w && 2.0 * fabs(y1 - cy) <= h) {
+ return true;
+ }
+
+ while (curve.vertex(&x2, &y2) != agg::path_cmd_stop) {
+ if (segment_intersects_rectangle(x1, y1, x2, y2, cx, cy, w, h)) {
+ return true;
+ }
+ x1 = x2;
+ y1 = y2;
+ }
+
+ if (filled) {
+ agg::trans_affine trans;
+ if (point_in_path(cx, cy, 0.0, path, trans)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <class PathIterator>
+void convert_path_to_polygons(PathIterator &path,
+ agg::trans_affine &trans,
+ double width,
+ double height,
+ int closed_only,
+ std::vector<Polygon> &result)
+{
+ typedef agg::conv_transform<py::PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removal_t;
+ typedef PathClipper<nan_removal_t> clipped_t;
+ typedef PathSimplifier<clipped_t> simplify_t;
+ typedef agg::conv_curve<simplify_t> curve_t;
+
+ bool do_clip = width != 0.0 && height != 0.0;
+ bool simplify = path.should_simplify();
+
+ transformed_path_t tpath(path, trans);
+ nan_removal_t nan_removed(tpath, true, path.has_curves());
+ clipped_t clipped(nan_removed, do_clip && !path.has_curves(), width, height);
+ simplify_t simplified(clipped, simplify, path.simplify_threshold());
+ curve_t curve(simplified);
+
+ result.push_back(Polygon());
+ Polygon *polygon = &result.back();
+ double x, y;
+ unsigned code;
+
+ while ((code = curve.vertex(&x, &y)) != agg::path_cmd_stop) {
+ if ((code & agg::path_cmd_end_poly) == agg::path_cmd_end_poly) {
+ _finalize_polygon(result, 1);
+ result.push_back(Polygon());
+ polygon = &result.back();
+ } else {
+ if (code == agg::path_cmd_move_to) {
+ _finalize_polygon(result, closed_only);
+ result.push_back(Polygon());
+ polygon = &result.back();
+ }
+ polygon->push_back(XY(x, y));
+ }
+ }
+
+ _finalize_polygon(result, closed_only);
+}
+
+template <class VertexSource>
+void
+__cleanup_path(VertexSource &source, std::vector<double> &vertices, std::vector<npy_uint8> &codes)
+{
+ unsigned code;
+ double x, y;
+ do {
+ code = source.vertex(&x, &y);
+ vertices.push_back(x);
+ vertices.push_back(y);
+ codes.push_back((npy_uint8)code);
+ } while (code != agg::path_cmd_stop);
+}
+
+template <class PathIterator>
+void cleanup_path(PathIterator &path,
+ agg::trans_affine &trans,
+ bool remove_nans,
+ bool do_clip,
+ const agg::rect_base<double> &rect,
+ e_snap_mode snap_mode,
+ double stroke_width,
+ bool do_simplify,
+ bool return_curves,
+ SketchParams sketch_params,
+ std::vector<double> &vertices,
+ std::vector<unsigned char> &codes)
+{
+ typedef agg::conv_transform<py::PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removal_t;
+ typedef PathClipper<nan_removal_t> clipped_t;
+ typedef PathSnapper<clipped_t> snapped_t;
+ typedef PathSimplifier<snapped_t> simplify_t;
+ typedef agg::conv_curve<simplify_t> curve_t;
+ typedef Sketch<curve_t> sketch_t;
+
+ transformed_path_t tpath(path, trans);
+ nan_removal_t nan_removed(tpath, remove_nans, path.has_curves());
+ clipped_t clipped(nan_removed, do_clip && !path.has_curves(), rect);
+ snapped_t snapped(clipped, snap_mode, path.total_vertices(), stroke_width);
+ simplify_t simplified(snapped, do_simplify, path.simplify_threshold());
+
+ vertices.reserve(path.total_vertices() * 2);
+ codes.reserve(path.total_vertices());
+
+ if (return_curves && sketch_params.scale == 0.0) {
+ __cleanup_path(simplified, vertices, codes);
+ } else {
+ curve_t curve(simplified);
+ sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness);
+ __cleanup_path(sketch, vertices, codes);
+ }
+}
+
+void quad2cubic(double x0, double y0,
+ double x1, double y1,
+ double x2, double y2,
+ double *outx, double *outy)
+{
+
+ outx[0] = x0 + 2./3. * (x1 - x0);
+ outy[0] = y0 + 2./3. * (y1 - y0);
+ outx[1] = outx[0] + 1./3. * (x2 - x0);
+ outy[1] = outy[0] + 1./3. * (y2 - y0);
+ outx[2] = x2;
+ outy[2] = y2;
+}
+
+char *__append_to_string(char *p, char **buffer, size_t *buffersize,
+ const char *content)
+{
+ for (const char *i = content; *i; ++i) {
+ if (p < *buffer) {
+ /* This is just an internal error */
+ return NULL;
+ }
+ if ((size_t)(p - *buffer) >= *buffersize) {
+ ptrdiff_t diff = p - *buffer;
+ *buffersize *= 2;
+ *buffer = (char *)realloc(*buffer, *buffersize);
+ if (*buffer == NULL) {
+ return NULL;
+ }
+ p = *buffer + diff;
+ }
+
+ *p++ = *i;
+ }
+
+ return p;
+}
+
+
+char *__add_number(double val, const char *format, int precision,
+ char **buffer, char *p, size_t *buffersize)
+{
+ char *result;
+
+#if PY_VERSION_HEX >= 0x02070000
+ char *str;
+ str = PyOS_double_to_string(val, format[0], precision, 0, NULL);
+#else
+ char str[64];
+ PyOS_ascii_formatd(str, 64, format, val);
+#endif
+
+ // Delete trailing zeros and decimal point
+ char *q = str;
+ for (; *q != 0; ++q) {
+ // Find the end of the string
+ }
+
+ --q;
+ for (; q >= str && *q == '0'; --q) {
+ // Rewind through all the zeros
+ }
+
+ // If the end is a decimal qoint, delete that too
+ if (q >= str && *q == '.') {
+ --q;
+ }
+
+ // Truncate the string
+ ++q;
+ *q = 0;
+
+#if PY_VERSION_HEX >= 0x02070000
+ if ((result = __append_to_string(p, buffer, buffersize, str)) == NULL) {
+ PyMem_Free(str);
+ return NULL;
+ }
+ PyMem_Free(str);
+#else
+ if ((result = __append_to_string(p, buffer, buffersize, str)) == NULL) {
+ return NULL;
+ }
+#endif
+
+ return result;
+}
+
+
+template <class PathIterator>
+int __convert_to_string(PathIterator &path,
+ int precision,
+ char **codes,
+ bool postfix,
+ char **buffer,
+ size_t *buffersize)
+{
+#if PY_VERSION_HEX >= 0x02070000
+ const char *format = "f";
+#else
+ char format[64];
+ snprintf(format, 64, "%s.%df", "%", precision);
+#endif
+
+ char *p = *buffer;
+ double x[3];
+ double y[3];
+ double last_x = 0.0;
+ double last_y = 0.0;
+
+ const int sizes[] = { 1, 1, 2, 3 };
+ int size = 0;
+ unsigned code;
+
+ while ((code = path.vertex(&x[0], &y[0])) != agg::path_cmd_stop) {
+ if (code == 0x4f) {
+ if ((p = __append_to_string(p, buffer, buffersize, codes[4])) == NULL) return 1;
+ } else if (code < 5) {
+ size = sizes[code - 1];
+
+ for (int i = 1; i < size; ++i) {
+ unsigned subcode = path.vertex(&x[i], &y[i]);
+ if (subcode != code) {
+ return 2;
+ }
+ }
+
+ /* For formats that don't support quad curves, convert to
+ cubic curves */
+ if (code == CURVE3 && codes[code - 1][0] == '\0') {
+ quad2cubic(last_x, last_y, x[0], y[0], x[1], y[1], x, y);
+ code++;
+ size = 3;
+ }
+
+ if (!postfix) {
+ if ((p = __append_to_string(p, buffer, buffersize, codes[code - 1])) == NULL) return 1;
+ if ((p = __append_to_string(p, buffer, buffersize, " ")) == NULL) return 1;
+ }
+
+ for (int i = 0; i < size; ++i) {
+ if ((p = __add_number(x[i], format, precision, buffer, p, buffersize)) == NULL) return 1;
+ if ((p = __append_to_string(p, buffer, buffersize, " ")) == NULL) return 1;
+ if ((p = __add_number(y[i], format, precision, buffer, p, buffersize)) == NULL) return 1;
+ if ((p = __append_to_string(p, buffer, buffersize, " ")) == NULL) return 1;
+ }
+
+ if (postfix) {
+ if ((p = __append_to_string(p, buffer, buffersize, codes[code - 1])) == NULL) return 1;
+ }
+
+ last_x = x[size - 1];
+ last_y = y[size - 1];
+ } else {
+ // Unknown code value
+ return 2;
+ }
+
+ if ((p = __append_to_string(p, buffer, buffersize, "\n")) == NULL) return 1;
+ }
+
+ *buffersize = p - *buffer;
+
+ return 0;
+}
+
+template <class PathIterator>
+int convert_to_string(PathIterator &path,
+ agg::trans_affine &trans,
+ agg::rect_d &clip_rect,
+ bool simplify,
+ SketchParams sketch_params,
+ int precision,
+ char **codes,
+ bool postfix,
+ char **buffer,
+ size_t *buffersize)
+{
+ typedef agg::conv_transform<py::PathIterator> transformed_path_t;
+ typedef PathNanRemover<transformed_path_t> nan_removal_t;
+ typedef PathClipper<nan_removal_t> clipped_t;
+ typedef PathSimplifier<clipped_t> simplify_t;
+ typedef agg::conv_curve<simplify_t> curve_t;
+ typedef Sketch<curve_t> sketch_t;
+
+ bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2);
+
+ transformed_path_t tpath(path, trans);
+ nan_removal_t nan_removed(tpath, true, path.has_curves());
+ clipped_t clipped(nan_removed, do_clip && !path.has_curves(), clip_rect);
+ simplify_t simplified(clipped, simplify, path.simplify_threshold());
+
+ *buffersize = path.total_vertices() * (precision + 5) * 4;
+ if (*buffersize == 0) {
+ return 0;
+ }
+
+ if (sketch_params.scale != 0.0) {
+ *buffersize *= 10.0;
+ }
+
+ *buffer = (char *)malloc(*buffersize);
+ if (*buffer == NULL) {
+ return 1;
+ }
+
+ if (sketch_params.scale == 0.0) {
+ return __convert_to_string(simplified, precision, codes, postfix, buffer, buffersize);
+ } else {
+ curve_t curve(simplified);
+ sketch_t sketch(curve, sketch_params.scale, sketch_params.length, sketch_params.randomness);
+ return __convert_to_string(sketch, precision, codes, postfix, buffer, buffersize);
+ }
+
+}
+
+template<class T>
+struct _is_sorted
+{
+ bool operator()(PyArrayObject *array)
+ {
+ npy_intp size;
+ npy_intp i;
+ T last_value;
+ T current_value;
+
+ size = PyArray_DIM(array, 0);
+
+ // std::isnan is only in C++11, which we don't yet require,
+ // so we use the "self == self" trick
+ for (i = 0; i < size; ++i) {
+ last_value = *((T *)PyArray_GETPTR1(array, i));
+ if (last_value == last_value) {
+ break;
+ }
+ }
+
+ if (i == size) {
+ // The whole array is non-finite
+ return false;
+ }
+
+ for (; i < size; ++i) {
+ current_value = *((T *)PyArray_GETPTR1(array, i));
+ if (current_value == current_value) {
+ if (current_value < last_value) {
+ return false;
+ }
+ last_value = current_value;
+ }
+ }
+
+ return true;
+ }
+};
+
+
+template<class T>
+struct _is_sorted_int
+{
+ bool operator()(PyArrayObject *array)
+ {
+ npy_intp size;
+ npy_intp i;
+ T last_value;
+ T current_value;
+
+ size = PyArray_DIM(array, 0);
+
+ last_value = *((T *)PyArray_GETPTR1(array, 0));
+
+ for (i = 1; i < size; ++i) {
+ current_value = *((T *)PyArray_GETPTR1(array, i));
+ if (current_value < last_value) {
+ return false;
+ }
+ last_value = current_value;
+ }
+
+ return true;
+ }
+};
+
+
+#endif