diff options
author | shumkovnd <shumkovnd@yandex-team.com> | 2023-11-10 14:39:34 +0300 |
---|---|---|
committer | shumkovnd <shumkovnd@yandex-team.com> | 2023-11-10 16:42:24 +0300 |
commit | 77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch) | |
tree | c51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/matplotlib/py2/src/_path.h | |
parent | dd6d20cadb65582270ac23f4b3b14ae189704b9d (diff) | |
download | ydb-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.h | 1316 |
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 |