diff options
author | maxim-yurchuk <maxim-yurchuk@yandex-team.com> | 2025-02-11 13:26:52 +0300 |
---|---|---|
committer | maxim-yurchuk <maxim-yurchuk@yandex-team.com> | 2025-02-11 13:57:59 +0300 |
commit | f895bba65827952ed934b2b46f9a45e30a191fd2 (patch) | |
tree | 03260c906d9ec41cdc03e2a496b15d407459cec0 /contrib/python/matplotlib/py3/src | |
parent | 5f7060466f7b9707818c2091e1a25c14f33c3474 (diff) | |
download | ydb-f895bba65827952ed934b2b46f9a45e30a191fd2.tar.gz |
Remove deps on pandas
<https://github.com/ydb-platform/ydb/pull/14418>
<https://github.com/ydb-platform/ydb/pull/14419>
\-- аналогичные правки в gh
Хочу залить в обход синка, чтобы посмотреть удалится ли pandas в нашей gh репе через piglet
commit_hash:abca127aa37d4dbb94b07e1e18cdb8eb5b711860
Diffstat (limited to 'contrib/python/matplotlib/py3/src')
29 files changed, 0 insertions, 14907 deletions
diff --git a/contrib/python/matplotlib/py3/src/_backend_agg.cpp b/contrib/python/matplotlib/py3/src/_backend_agg.cpp deleted file mode 100644 index 335e409719..0000000000 --- a/contrib/python/matplotlib/py3/src/_backend_agg.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#define NO_IMPORT_ARRAY - -#include "_backend_agg.h" -#include "mplutils.h" - -void BufferRegion::to_string_argb(uint8_t *buf) -{ - unsigned char *pix; - unsigned char tmp; - size_t i, j; - - memcpy(buf, data, (size_t) height * stride); - - for (i = 0; i < (size_t)height; ++i) { - pix = buf + i * stride; - for (j = 0; j < (size_t)width; ++j) { - // Convert rgba to argb - tmp = pix[2]; - pix[2] = pix[0]; - pix[0] = tmp; - pix += 4; - } - } -} - -RendererAgg::RendererAgg(unsigned int width, unsigned int height, double dpi) - : width(width), - height(height), - dpi(dpi), - NUMBYTES((size_t)width * (size_t)height * 4), - pixBuffer(NULL), - renderingBuffer(), - alphaBuffer(NULL), - alphaMaskRenderingBuffer(), - alphaMask(alphaMaskRenderingBuffer), - pixfmtAlphaMask(alphaMaskRenderingBuffer), - rendererBaseAlphaMask(), - rendererAlphaMask(), - scanlineAlphaMask(), - slineP8(), - slineBin(), - pixFmt(), - rendererBase(), - rendererAA(), - rendererBin(), - theRasterizer(32768), - lastclippath(NULL), - _fill_color(agg::rgba(1, 1, 1, 0)) -{ - unsigned stride(width * 4); - - pixBuffer = new agg::int8u[NUMBYTES]; - renderingBuffer.attach(pixBuffer, width, height, stride); - pixFmt.attach(renderingBuffer); - rendererBase.attach(pixFmt); - rendererBase.clear(_fill_color); - rendererAA.attach(rendererBase); - rendererBin.attach(rendererBase); - hatch_size = int(dpi); - hatchBuffer = new agg::int8u[hatch_size * hatch_size * 4]; - hatchRenderingBuffer.attach(hatchBuffer, hatch_size, hatch_size, hatch_size * 4); -} - -RendererAgg::~RendererAgg() -{ - delete[] hatchBuffer; - delete[] alphaBuffer; - delete[] pixBuffer; -} - -void RendererAgg::create_alpha_buffers() -{ - if (!alphaBuffer) { - alphaBuffer = new agg::int8u[width * height]; - alphaMaskRenderingBuffer.attach(alphaBuffer, width, height, width); - rendererBaseAlphaMask.attach(pixfmtAlphaMask); - rendererAlphaMask.attach(rendererBaseAlphaMask); - } -} - -BufferRegion *RendererAgg::copy_from_bbox(agg::rect_d in_rect) -{ - agg::rect_i rect( - (int)in_rect.x1, height - (int)in_rect.y2, (int)in_rect.x2, height - (int)in_rect.y1); - - BufferRegion *reg = NULL; - reg = new BufferRegion(rect); - - agg::rendering_buffer rbuf; - rbuf.attach(reg->get_data(), reg->get_width(), reg->get_height(), reg->get_stride()); - - pixfmt pf(rbuf); - renderer_base rb(pf); - rb.copy_from(renderingBuffer, &rect, -rect.x1, -rect.y1); - - return reg; -} - -void RendererAgg::restore_region(BufferRegion ®ion) -{ - if (region.get_data() == NULL) { - throw std::runtime_error("Cannot restore_region from NULL data"); - } - - agg::rendering_buffer rbuf; - rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride()); - - rendererBase.copy_from(rbuf, 0, region.get_rect().x1, region.get_rect().y1); -} - -// Restore the part of the saved region with offsets -void -RendererAgg::restore_region(BufferRegion ®ion, int xx1, int yy1, int xx2, int yy2, int x, int y ) -{ - if (region.get_data() == NULL) { - throw std::runtime_error("Cannot restore_region from NULL data"); - } - - agg::rect_i &rrect = region.get_rect(); - - agg::rect_i rect(xx1 - rrect.x1, (yy1 - rrect.y1), xx2 - rrect.x1, (yy2 - rrect.y1)); - - agg::rendering_buffer rbuf; - rbuf.attach(region.get_data(), region.get_width(), region.get_height(), region.get_stride()); - - rendererBase.copy_from(rbuf, &rect, x, y); -} - -bool RendererAgg::render_clippath(py::PathIterator &clippath, - const agg::trans_affine &clippath_trans, - e_snap_mode snap_mode) -{ - typedef agg::conv_transform<py::PathIterator> transformed_path_t; - typedef PathNanRemover<transformed_path_t> nan_removed_t; - /* Unlike normal Paths, the clip path cannot be clipped to the Figure bbox, - * because it needs to remain a complete closed path, so there is no - * PathClipper<nan_removed_t> step. */ - typedef PathSnapper<nan_removed_t> snapped_t; - typedef PathSimplifier<snapped_t> simplify_t; - typedef agg::conv_curve<simplify_t> curve_t; - - bool has_clippath = (clippath.total_vertices() != 0); - - if (has_clippath && - (clippath.get_id() != lastclippath || clippath_trans != lastclippath_transform)) { - create_alpha_buffers(); - agg::trans_affine trans(clippath_trans); - trans *= agg::trans_affine_scaling(1.0, -1.0); - trans *= agg::trans_affine_translation(0.0, (double)height); - - rendererBaseAlphaMask.clear(agg::gray8(0, 0)); - transformed_path_t transformed_clippath(clippath, trans); - nan_removed_t nan_removed_clippath(transformed_clippath, true, clippath.has_codes()); - snapped_t snapped_clippath(nan_removed_clippath, snap_mode, clippath.total_vertices(), 0.0); - simplify_t simplified_clippath(snapped_clippath, - clippath.should_simplify() && !clippath.has_codes(), - clippath.simplify_threshold()); - curve_t curved_clippath(simplified_clippath); - theRasterizer.add_path(curved_clippath); - rendererAlphaMask.color(agg::gray8(255, 255)); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, rendererAlphaMask); - lastclippath = clippath.get_id(); - lastclippath_transform = clippath_trans; - } - - return has_clippath; -} - -void RendererAgg::clear() -{ - //"clear the rendered buffer"; - - rendererBase.clear(_fill_color); -} diff --git a/contrib/python/matplotlib/py3/src/_backend_agg.h b/contrib/python/matplotlib/py3/src/_backend_agg.h deleted file mode 100644 index 61c24232a8..0000000000 --- a/contrib/python/matplotlib/py3/src/_backend_agg.h +++ /dev/null @@ -1,1280 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* _backend_agg.h -*/ - -#ifndef MPL_BACKEND_AGG_H -#define MPL_BACKEND_AGG_H - -#include <cmath> -#include <vector> -#include <algorithm> - -#include "agg_alpha_mask_u8.h" -#include "agg_conv_curve.h" -#include "agg_conv_dash.h" -#include "agg_conv_stroke.h" -#include "agg_image_accessors.h" -#include "agg_pixfmt_amask_adaptor.h" -#include "agg_pixfmt_gray.h" -#include "agg_pixfmt_rgba.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_renderer_base.h" -#include "agg_renderer_scanline.h" -#include "agg_rendering_buffer.h" -#include "agg_scanline_bin.h" -#include "agg_scanline_p.h" -#include "agg_scanline_storage_aa.h" -#include "agg_scanline_storage_bin.h" -#include "agg_scanline_u.h" -#include "agg_span_allocator.h" -#include "agg_span_converter.h" -#include "agg_span_gouraud_rgba.h" -#include "agg_span_image_filter_gray.h" -#include "agg_span_image_filter_rgba.h" -#include "agg_span_interpolator_linear.h" -#include "agg_span_pattern_rgba.h" -#include "util/agg_color_conv_rgb8.h" - -#include "_backend_agg_basic_types.h" -#include "path_converters.h" -#include "array.h" -#include "agg_workaround.h" - -/**********************************************************************/ - -// a helper class to pass agg::buffer objects around. - -class BufferRegion -{ - public: - BufferRegion(const agg::rect_i &r) : rect(r) - { - width = r.x2 - r.x1; - height = r.y2 - r.y1; - stride = width * 4; - data = new agg::int8u[stride * height]; - } - - virtual ~BufferRegion() - { - delete[] data; - }; - - agg::int8u *get_data() - { - return data; - } - - agg::rect_i &get_rect() - { - return rect; - } - - int get_width() - { - return width; - } - - int get_height() - { - return height; - } - - int get_stride() - { - return stride; - } - - void to_string_argb(uint8_t *buf); - - private: - agg::int8u *data; - agg::rect_i rect; - int width; - int height; - int stride; - - private: - // prevent copying - BufferRegion(const BufferRegion &); - BufferRegion &operator=(const BufferRegion &); -}; - -#define MARKER_CACHE_SIZE 512 - -// the renderer -class RendererAgg -{ - public: - - typedef fixed_blender_rgba_plain<agg::rgba8, agg::order_rgba> fixed_blender_rgba32_plain; - typedef agg::pixfmt_alpha_blend_rgba<fixed_blender_rgba32_plain, agg::rendering_buffer> pixfmt; - typedef agg::renderer_base<pixfmt> renderer_base; - typedef agg::renderer_scanline_aa_solid<renderer_base> renderer_aa; - typedef agg::renderer_scanline_bin_solid<renderer_base> renderer_bin; - typedef agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> rasterizer; - - typedef agg::scanline_p8 scanline_p8; - typedef agg::scanline_bin scanline_bin; - typedef agg::amask_no_clip_gray8 alpha_mask_type; - typedef agg::scanline_u8_am<alpha_mask_type> scanline_am; - - typedef agg::renderer_base<agg::pixfmt_gray8> renderer_base_alpha_mask_type; - typedef agg::renderer_scanline_aa_solid<renderer_base_alpha_mask_type> renderer_alpha_mask_type; - - /* TODO: Remove facepair_t */ - typedef std::pair<bool, agg::rgba> facepair_t; - - RendererAgg(unsigned int width, unsigned int height, double dpi); - - virtual ~RendererAgg(); - - unsigned int get_width() - { - return width; - } - - unsigned int get_height() - { - return height; - } - - template <class PathIterator> - void draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, agg::rgba &color); - - template <class PathIterator> - void draw_markers(GCAgg &gc, - PathIterator &marker_path, - agg::trans_affine &marker_path_trans, - PathIterator &path, - agg::trans_affine &trans, - agg::rgba face); - - template <class ImageArray> - void draw_text_image(GCAgg &gc, ImageArray &image, int x, int y, double angle); - - template <class ImageArray> - void draw_image(GCAgg &gc, - double x, - double y, - ImageArray &image); - - template <class PathGenerator, - class TransformArray, - class OffsetArray, - class ColorArray, - class LineWidthArray, - class AntialiasedArray> - void draw_path_collection(GCAgg &gc, - agg::trans_affine &master_transform, - PathGenerator &path, - TransformArray &transforms, - OffsetArray &offsets, - agg::trans_affine &offset_trans, - ColorArray &facecolors, - ColorArray &edgecolors, - LineWidthArray &linewidths, - DashesVector &linestyles, - AntialiasedArray &antialiaseds); - - template <class CoordinateArray, class OffsetArray, class ColorArray> - void draw_quad_mesh(GCAgg &gc, - agg::trans_affine &master_transform, - unsigned int mesh_width, - unsigned int mesh_height, - CoordinateArray &coordinates, - OffsetArray &offsets, - agg::trans_affine &offset_trans, - ColorArray &facecolors, - bool antialiased, - ColorArray &edgecolors); - - template <class PointArray, class ColorArray> - void draw_gouraud_triangle(GCAgg &gc, - PointArray &points, - ColorArray &colors, - agg::trans_affine &trans); - - template <class PointArray, class ColorArray> - void draw_gouraud_triangles(GCAgg &gc, - PointArray &points, - ColorArray &colors, - agg::trans_affine &trans); - - agg::rect_i get_content_extents(); - void clear(); - - BufferRegion *copy_from_bbox(agg::rect_d in_rect); - void restore_region(BufferRegion ®); - void restore_region(BufferRegion ®ion, int xx1, int yy1, int xx2, int yy2, int x, int y); - - unsigned int width, height; - double dpi; - size_t NUMBYTES; // the number of bytes in buffer - - agg::int8u *pixBuffer; - agg::rendering_buffer renderingBuffer; - - agg::int8u *alphaBuffer; - agg::rendering_buffer alphaMaskRenderingBuffer; - alpha_mask_type alphaMask; - agg::pixfmt_gray8 pixfmtAlphaMask; - renderer_base_alpha_mask_type rendererBaseAlphaMask; - renderer_alpha_mask_type rendererAlphaMask; - scanline_am scanlineAlphaMask; - - scanline_p8 slineP8; - scanline_bin slineBin; - pixfmt pixFmt; - renderer_base rendererBase; - renderer_aa rendererAA; - renderer_bin rendererBin; - rasterizer theRasterizer; - - void *lastclippath; - agg::trans_affine lastclippath_transform; - - size_t hatch_size; - agg::int8u *hatchBuffer; - agg::rendering_buffer hatchRenderingBuffer; - - agg::rgba _fill_color; - - protected: - inline double points_to_pixels(double points) - { - return points * dpi / 72.0; - } - - template <class R> - void set_clipbox(const agg::rect_d &cliprect, R &rasterizer); - - bool render_clippath(py::PathIterator &clippath, const agg::trans_affine &clippath_trans, e_snap_mode snap_mode); - - template <class PathIteratorType> - void _draw_path(PathIteratorType &path, bool has_clippath, const facepair_t &face, GCAgg &gc); - - template <class PathIterator, - class PathGenerator, - class TransformArray, - class OffsetArray, - class ColorArray, - class LineWidthArray, - class AntialiasedArray> - void _draw_path_collection_generic(GCAgg &gc, - agg::trans_affine master_transform, - const agg::rect_d &cliprect, - PathIterator &clippath, - const agg::trans_affine &clippath_trans, - PathGenerator &path_generator, - TransformArray &transforms, - OffsetArray &offsets, - const agg::trans_affine &offset_trans, - ColorArray &facecolors, - ColorArray &edgecolors, - LineWidthArray &linewidths, - DashesVector &linestyles, - AntialiasedArray &antialiaseds, - bool check_snap, - bool has_codes); - - template <class PointArray, class ColorArray> - void _draw_gouraud_triangle(PointArray &points, - ColorArray &colors, - agg::trans_affine trans, - bool has_clippath); - - private: - void create_alpha_buffers(); - - // prevent copying - RendererAgg(const RendererAgg &); - RendererAgg &operator=(const RendererAgg &); -}; - -/*************************************************************************** - * Implementation - */ - -template <class path_t> -inline void -RendererAgg::_draw_path(path_t &path, bool has_clippath, const facepair_t &face, GCAgg &gc) -{ - typedef agg::conv_stroke<path_t> stroke_t; - typedef agg::conv_dash<path_t> dash_t; - typedef agg::conv_stroke<dash_t> stroke_dash_t; - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type; - typedef agg::renderer_scanline_bin_solid<amask_ren_type> amask_bin_renderer_type; - - // Render face - if (face.first) { - theRasterizer.add_path(path); - - if (gc.isaa) { - if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); - ren.color(face.second); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); - } else { - rendererAA.color(face.second); - agg::render_scanlines(theRasterizer, slineP8, rendererAA); - } - } else { - if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_bin_renderer_type ren(r); - ren.color(face.second); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); - } else { - rendererBin.color(face.second); - agg::render_scanlines(theRasterizer, slineP8, rendererBin); - } - } - } - - // Render hatch - if (gc.has_hatchpath()) { - // Reset any clipping that may be in effect, since we'll be - // drawing the hatch in a scratch buffer at origin (0, 0) - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - - // Create and transform the path - typedef agg::conv_transform<py::PathIterator> hatch_path_trans_t; - typedef agg::conv_curve<hatch_path_trans_t> hatch_path_curve_t; - typedef agg::conv_stroke<hatch_path_curve_t> hatch_path_stroke_t; - - py::PathIterator hatch_path(gc.hatchpath); - agg::trans_affine hatch_trans; - hatch_trans *= agg::trans_affine_scaling(1.0, -1.0); - hatch_trans *= agg::trans_affine_translation(0.0, 1.0); - hatch_trans *= agg::trans_affine_scaling(hatch_size, hatch_size); - hatch_path_trans_t hatch_path_trans(hatch_path, hatch_trans); - hatch_path_curve_t hatch_path_curve(hatch_path_trans); - hatch_path_stroke_t hatch_path_stroke(hatch_path_curve); - hatch_path_stroke.width(points_to_pixels(gc.hatch_linewidth)); - hatch_path_stroke.line_cap(agg::square_cap); - - // Render the path into the hatch buffer - pixfmt hatch_img_pixf(hatchRenderingBuffer); - renderer_base rb(hatch_img_pixf); - renderer_aa rs(rb); - rb.clear(_fill_color); - rs.color(gc.hatch_color); - - theRasterizer.add_path(hatch_path_curve); - agg::render_scanlines(theRasterizer, slineP8, rs); - theRasterizer.add_path(hatch_path_stroke); - agg::render_scanlines(theRasterizer, slineP8, rs); - - // Put clipping back on, if originally set on entry to this - // function - set_clipbox(gc.cliprect, theRasterizer); - if (has_clippath) { - render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - } - - // Transfer the hatch to the main image buffer - typedef agg::image_accessor_wrap<pixfmt, - agg::wrap_mode_repeat_auto_pow2, - agg::wrap_mode_repeat_auto_pow2> img_source_type; - typedef agg::span_pattern_rgba<img_source_type> span_gen_type; - agg::span_allocator<agg::rgba8> sa; - img_source_type img_src(hatch_img_pixf); - span_gen_type sg(img_src, 0, 0); - theRasterizer.add_path(path); - - if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type ren(pfa); - agg::render_scanlines_aa(theRasterizer, slineP8, ren, sa, sg); - } else { - agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, sa, sg); - } - } - - // Render stroke - if (gc.linewidth != 0.0) { - double linewidth = points_to_pixels(gc.linewidth); - if (!gc.isaa) { - linewidth = (linewidth < 0.5) ? 0.5 : mpl_round(linewidth); - } - if (gc.dashes.size() == 0) { - stroke_t stroke(path); - stroke.width(points_to_pixels(gc.linewidth)); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.miter_limit(points_to_pixels(gc.linewidth)); - theRasterizer.add_path(stroke); - } else { - dash_t dash(path); - gc.dashes.dash_to_stroke(dash, dpi, gc.isaa); - stroke_dash_t stroke(dash); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.width(linewidth); - stroke.miter_limit(points_to_pixels(gc.linewidth)); - theRasterizer.add_path(stroke); - } - - if (gc.isaa) { - if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); - ren.color(gc.color); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); - } else { - rendererAA.color(gc.color); - agg::render_scanlines(theRasterizer, slineP8, rendererAA); - } - } else { - if (has_clippath) { - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_bin_renderer_type ren(r); - ren.color(gc.color); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); - } else { - rendererBin.color(gc.color); - agg::render_scanlines(theRasterizer, slineBin, rendererBin); - } - } - } -} - -template <class PathIterator> -inline void -RendererAgg::draw_path(GCAgg &gc, PathIterator &path, agg::trans_affine &trans, agg::rgba &color) -{ - typedef agg::conv_transform<py::PathIterator> transformed_path_t; - typedef PathNanRemover<transformed_path_t> nan_removed_t; - typedef PathClipper<nan_removed_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; - - facepair_t face(color.a != 0.0, color); - - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - set_clipbox(gc.cliprect, theRasterizer); - bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - - trans *= agg::trans_affine_scaling(1.0, -1.0); - trans *= agg::trans_affine_translation(0.0, (double)height); - bool clip = !face.first && !gc.has_hatchpath(); - bool simplify = path.should_simplify() && clip; - double snapping_linewidth = points_to_pixels(gc.linewidth); - if (gc.color.a == 0.0) { - snapping_linewidth = 0.0; - } - - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, path.has_codes()); - clipped_t clipped(nan_removed, clip, width, height); - snapped_t snapped(clipped, gc.snap_mode, path.total_vertices(), snapping_linewidth); - simplify_t simplified(snapped, simplify, path.simplify_threshold()); - curve_t curve(simplified); - sketch_t sketch(curve, gc.sketch.scale, gc.sketch.length, gc.sketch.randomness); - - _draw_path(sketch, has_clippath, face, gc); -} - -template <class PathIterator> -inline void RendererAgg::draw_markers(GCAgg &gc, - PathIterator &marker_path, - agg::trans_affine &marker_trans, - PathIterator &path, - agg::trans_affine &trans, - agg::rgba color) -{ - typedef agg::conv_transform<py::PathIterator> transformed_path_t; - typedef PathNanRemover<transformed_path_t> nan_removed_t; - typedef PathSnapper<nan_removed_t> snap_t; - typedef agg::conv_curve<snap_t> curve_t; - typedef agg::conv_stroke<curve_t> stroke_t; - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - typedef agg::renderer_scanline_aa_solid<amask_ren_type> amask_aa_renderer_type; - - // Deal with the difference in y-axis direction - marker_trans *= agg::trans_affine_scaling(1.0, -1.0); - - trans *= agg::trans_affine_scaling(1.0, -1.0); - trans *= agg::trans_affine_translation(0.5, (double)height + 0.5); - - transformed_path_t marker_path_transformed(marker_path, marker_trans); - nan_removed_t marker_path_nan_removed(marker_path_transformed, true, marker_path.has_codes()); - snap_t marker_path_snapped(marker_path_nan_removed, - gc.snap_mode, - marker_path.total_vertices(), - points_to_pixels(gc.linewidth)); - curve_t marker_path_curve(marker_path_snapped); - - if (!marker_path_snapped.is_snapping()) { - // If the path snapper isn't in effect, at least make sure the marker - // at (0, 0) is in the center of a pixel. This, importantly, makes - // the circle markers look centered around the point they refer to. - marker_trans *= agg::trans_affine_translation(0.5, 0.5); - } - - transformed_path_t path_transformed(path, trans); - nan_removed_t path_nan_removed(path_transformed, false, false); - snap_t path_snapped(path_nan_removed, SNAP_FALSE, path.total_vertices(), 0.0); - curve_t path_curve(path_snapped); - path_curve.rewind(0); - - facepair_t face(color.a != 0.0, color); - - // maxim's suggestions for cached scanlines - agg::scanline_storage_aa8 scanlines; - theRasterizer.reset(); - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - agg::rect_i marker_size(0x7FFFFFFF, 0x7FFFFFFF, -0x7FFFFFFF, -0x7FFFFFFF); - - agg::int8u staticFillCache[MARKER_CACHE_SIZE]; - agg::int8u staticStrokeCache[MARKER_CACHE_SIZE]; - agg::int8u *fillCache = staticFillCache; - agg::int8u *strokeCache = staticStrokeCache; - - try - { - unsigned fillSize = 0; - if (face.first) { - theRasterizer.add_path(marker_path_curve); - agg::render_scanlines(theRasterizer, slineP8, scanlines); - fillSize = scanlines.byte_size(); - if (fillSize >= MARKER_CACHE_SIZE) { - fillCache = new agg::int8u[fillSize]; - } - scanlines.serialize(fillCache); - marker_size = agg::rect_i(scanlines.min_x(), - scanlines.min_y(), - scanlines.max_x(), - scanlines.max_y()); - } - - stroke_t stroke(marker_path_curve); - stroke.width(points_to_pixels(gc.linewidth)); - stroke.line_cap(gc.cap); - stroke.line_join(gc.join); - stroke.miter_limit(points_to_pixels(gc.linewidth)); - theRasterizer.reset(); - theRasterizer.add_path(stroke); - agg::render_scanlines(theRasterizer, slineP8, scanlines); - unsigned strokeSize = scanlines.byte_size(); - if (strokeSize >= MARKER_CACHE_SIZE) { - strokeCache = new agg::int8u[strokeSize]; - } - scanlines.serialize(strokeCache); - marker_size = agg::rect_i(std::min(marker_size.x1, scanlines.min_x()), - std::min(marker_size.y1, scanlines.min_y()), - std::max(marker_size.x2, scanlines.max_x()), - std::max(marker_size.y2, scanlines.max_y())); - - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - set_clipbox(gc.cliprect, rendererBase); - bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - - double x, y; - - agg::serialized_scanlines_adaptor_aa8 sa; - agg::serialized_scanlines_adaptor_aa8::embedded_scanline sl; - - agg::rect_d clipping_rect(-1.0 - marker_size.x2, - -1.0 - marker_size.y2, - 1.0 + width - marker_size.x1, - 1.0 + height - marker_size.y1); - - if (has_clippath) { - while (path_curve.vertex(&x, &y) != agg::path_cmd_stop) { - if (!(std::isfinite(x) && std::isfinite(y))) { - continue; - } - - /* These values are correctly snapped above -- so we don't want - to round here, we really only want to truncate */ - x = floor(x); - y = floor(y); - - // Cull points outside the boundary of the image. - // Values that are too large may overflow and create - // segfaults. - // http://sourceforge.net/tracker/?func=detail&aid=2865490&group_id=80706&atid=560720 - if (!clipping_rect.hit_test(x, y)) { - continue; - } - - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r); - - if (face.first) { - ren.color(face.second); - sa.init(fillCache, fillSize, x, y); - agg::render_scanlines(sa, sl, ren); - } - ren.color(gc.color); - sa.init(strokeCache, strokeSize, x, y); - agg::render_scanlines(sa, sl, ren); - } - } else { - while (path_curve.vertex(&x, &y) != agg::path_cmd_stop) { - if (!(std::isfinite(x) && std::isfinite(y))) { - continue; - } - - /* These values are correctly snapped above -- so we don't want - to round here, we really only want to truncate */ - x = floor(x); - y = floor(y); - - // Cull points outside the boundary of the image. - // Values that are too large may overflow and create - // segfaults. - // http://sourceforge.net/tracker/?func=detail&aid=2865490&group_id=80706&atid=560720 - if (!clipping_rect.hit_test(x, y)) { - continue; - } - - if (face.first) { - rendererAA.color(face.second); - sa.init(fillCache, fillSize, x, y); - agg::render_scanlines(sa, sl, rendererAA); - } - - rendererAA.color(gc.color); - sa.init(strokeCache, strokeSize, x, y); - agg::render_scanlines(sa, sl, rendererAA); - } - } - } - catch (...) - { - if (fillCache != staticFillCache) - delete[] fillCache; - if (strokeCache != staticStrokeCache) - delete[] strokeCache; - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - throw; - } - - if (fillCache != staticFillCache) - delete[] fillCache; - if (strokeCache != staticStrokeCache) - delete[] strokeCache; - - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); -} - -/** - * This is a custom span generator that converts spans in the - * 8-bit inverted greyscale font buffer to rgba that agg can use. - */ -template <class ChildGenerator> -class font_to_rgba -{ - public: - typedef ChildGenerator child_type; - typedef agg::rgba8 color_type; - typedef typename child_type::color_type child_color_type; - typedef agg::span_allocator<child_color_type> span_alloc_type; - - private: - child_type *_gen; - color_type _color; - span_alloc_type _allocator; - - public: - font_to_rgba(child_type *gen, color_type color) : _gen(gen), _color(color) - { - } - - inline void generate(color_type *output_span, int x, int y, unsigned len) - { - _allocator.allocate(len); - child_color_type *input_span = _allocator.span(); - _gen->generate(input_span, x, y, len); - - do { - *output_span = _color; - output_span->a = ((unsigned int)_color.a * (unsigned int)input_span->v) >> 8; - ++output_span; - ++input_span; - } while (--len); - } - - void prepare() - { - _gen->prepare(); - } -}; - -template <class ImageArray> -inline void RendererAgg::draw_text_image(GCAgg &gc, ImageArray &image, int x, int y, double angle) -{ - typedef agg::span_allocator<agg::rgba8> color_span_alloc_type; - typedef agg::span_interpolator_linear<> interpolator_type; - typedef agg::image_accessor_clip<agg::pixfmt_gray8> image_accessor_type; - typedef agg::span_image_filter_gray<image_accessor_type, interpolator_type> image_span_gen_type; - typedef font_to_rgba<image_span_gen_type> span_gen_type; - typedef agg::renderer_scanline_aa<renderer_base, color_span_alloc_type, span_gen_type> - renderer_type; - - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - if (angle != 0.0) { - agg::rendering_buffer srcbuf( - image.data(), (unsigned)image.dim(1), - (unsigned)image.dim(0), (unsigned)image.dim(1)); - agg::pixfmt_gray8 pixf_img(srcbuf); - - set_clipbox(gc.cliprect, theRasterizer); - - agg::trans_affine mtx; - mtx *= agg::trans_affine_translation(0, -image.dim(0)); - mtx *= agg::trans_affine_rotation(-angle * (agg::pi / 180.0)); - mtx *= agg::trans_affine_translation(x, y); - - agg::path_storage rect; - rect.move_to(0, 0); - rect.line_to(image.dim(1), 0); - rect.line_to(image.dim(1), image.dim(0)); - rect.line_to(0, image.dim(0)); - rect.line_to(0, 0); - agg::conv_transform<agg::path_storage> rect2(rect, mtx); - - agg::trans_affine inv_mtx(mtx); - inv_mtx.invert(); - - agg::image_filter_lut filter; - filter.calculate(agg::image_filter_spline36()); - interpolator_type interpolator(inv_mtx); - color_span_alloc_type sa; - image_accessor_type ia(pixf_img, agg::gray8(0)); - image_span_gen_type image_span_generator(ia, interpolator, filter); - span_gen_type output_span_generator(&image_span_generator, gc.color); - renderer_type ri(rendererBase, sa, output_span_generator); - - theRasterizer.add_path(rect2); - agg::render_scanlines(theRasterizer, slineP8, ri); - } else { - agg::rect_i fig, text; - - int deltay = y - image.dim(0); - - fig.init(0, 0, width, height); - text.init(x, deltay, x + image.dim(1), y); - text.clip(fig); - - if (gc.cliprect.x1 != 0.0 || gc.cliprect.y1 != 0.0 || gc.cliprect.x2 != 0.0 || gc.cliprect.y2 != 0.0) { - agg::rect_i clip; - - clip.init(mpl_round_to_int(gc.cliprect.x1), - mpl_round_to_int(height - gc.cliprect.y2), - mpl_round_to_int(gc.cliprect.x2), - mpl_round_to_int(height - gc.cliprect.y1)); - text.clip(clip); - } - - if (text.x2 > text.x1) { - int deltax = text.x2 - text.x1; - int deltax2 = text.x1 - x; - for (int yi = text.y1; yi < text.y2; ++yi) { - pixFmt.blend_solid_hspan(text.x1, yi, deltax, gc.color, - &image(yi - deltay, deltax2)); - } - } - } -} - -class span_conv_alpha -{ - public: - typedef agg::rgba8 color_type; - - double m_alpha; - - span_conv_alpha(double alpha) : m_alpha(alpha) - { - } - - void prepare() - { - } - void generate(color_type *span, int x, int y, unsigned len) const - { - do { - span->a = (agg::int8u)((double)span->a * m_alpha); - ++span; - } while (--len); - } -}; - -template <class ImageArray> -inline void RendererAgg::draw_image(GCAgg &gc, - double x, - double y, - ImageArray &image) -{ - double alpha = gc.alpha; - - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - set_clipbox(gc.cliprect, theRasterizer); - bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - - agg::rendering_buffer buffer; - buffer.attach( - image.data(), (unsigned)image.dim(1), (unsigned)image.dim(0), -(int)image.dim(1) * 4); - pixfmt pixf(buffer); - - if (has_clippath) { - agg::trans_affine mtx; - agg::path_storage rect; - - mtx *= agg::trans_affine_translation((int)x, (int)(height - (y + image.dim(0)))); - - rect.move_to(0, 0); - rect.line_to(image.dim(1), 0); - rect.line_to(image.dim(1), image.dim(0)); - rect.line_to(0, image.dim(0)); - rect.line_to(0, 0); - - agg::conv_transform<agg::path_storage> rect2(rect, mtx); - - agg::trans_affine inv_mtx(mtx); - inv_mtx.invert(); - - typedef agg::span_allocator<agg::rgba8> color_span_alloc_type; - typedef agg::image_accessor_clip<pixfmt> image_accessor_type; - typedef agg::span_interpolator_linear<> interpolator_type; - typedef agg::span_image_filter_rgba_nn<image_accessor_type, interpolator_type> - image_span_gen_type; - typedef agg::span_converter<image_span_gen_type, span_conv_alpha> span_conv; - - color_span_alloc_type sa; - image_accessor_type ia(pixf, agg::rgba8(0, 0, 0, 0)); - interpolator_type interpolator(inv_mtx); - image_span_gen_type image_span_generator(ia, interpolator); - span_conv_alpha conv_alpha(alpha); - span_conv spans(image_span_generator, conv_alpha); - - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - typedef agg::renderer_scanline_aa<amask_ren_type, color_span_alloc_type, span_conv> - renderer_type_alpha; - - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - renderer_type_alpha ri(r, sa, spans); - - theRasterizer.add_path(rect2); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, ri); - } else { - set_clipbox(gc.cliprect, rendererBase); - rendererBase.blend_from( - pixf, 0, (int)x, (int)(height - (y + image.dim(0))), (agg::int8u)(alpha * 255)); - } - - rendererBase.reset_clipping(true); -} - -template <class PathIterator, - class PathGenerator, - class TransformArray, - class OffsetArray, - class ColorArray, - class LineWidthArray, - class AntialiasedArray> -inline void RendererAgg::_draw_path_collection_generic(GCAgg &gc, - agg::trans_affine master_transform, - const agg::rect_d &cliprect, - PathIterator &clippath, - const agg::trans_affine &clippath_trans, - PathGenerator &path_generator, - TransformArray &transforms, - OffsetArray &offsets, - const agg::trans_affine &offset_trans, - ColorArray &facecolors, - ColorArray &edgecolors, - LineWidthArray &linewidths, - DashesVector &linestyles, - AntialiasedArray &antialiaseds, - bool check_snap, - bool has_codes) -{ - typedef agg::conv_transform<typename PathGenerator::path_iterator> transformed_path_t; - typedef PathNanRemover<transformed_path_t> nan_removed_t; - typedef PathClipper<nan_removed_t> clipped_t; - typedef PathSnapper<clipped_t> snapped_t; - typedef agg::conv_curve<snapped_t> snapped_curve_t; - typedef agg::conv_curve<clipped_t> curve_t; - - size_t Npaths = path_generator.num_paths(); - size_t Noffsets = offsets.size(); - size_t N = std::max(Npaths, Noffsets); - - size_t Ntransforms = transforms.size(); - size_t Nfacecolors = facecolors.size(); - size_t Nedgecolors = edgecolors.size(); - size_t Nlinewidths = linewidths.size(); - size_t Nlinestyles = std::min(linestyles.size(), N); - size_t Naa = antialiaseds.size(); - - if ((Nfacecolors == 0 && Nedgecolors == 0) || Npaths == 0) { - return; - } - - // Handle any clipping globally - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - set_clipbox(cliprect, theRasterizer); - bool has_clippath = render_clippath(clippath, clippath_trans, gc.snap_mode); - - // Set some defaults, assuming no face or edge - gc.linewidth = 0.0; - facepair_t face; - face.first = Nfacecolors != 0; - agg::trans_affine trans; - bool do_clip = !face.first && !gc.has_hatchpath(); - - for (int i = 0; i < (int)N; ++i) { - typename PathGenerator::path_iterator path = path_generator(i); - - if (Ntransforms) { - int it = i % Ntransforms; - trans = agg::trans_affine(transforms(it, 0, 0), - transforms(it, 1, 0), - transforms(it, 0, 1), - transforms(it, 1, 1), - transforms(it, 0, 2), - transforms(it, 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); - trans *= agg::trans_affine_translation(xo, yo); - } - - // These transformations must be done post-offsets - trans *= agg::trans_affine_scaling(1.0, -1.0); - trans *= agg::trans_affine_translation(0.0, (double)height); - - if (Nfacecolors) { - int ic = i % Nfacecolors; - face.second = agg::rgba(facecolors(ic, 0), facecolors(ic, 1), facecolors(ic, 2), facecolors(ic, 3)); - } - - if (Nedgecolors) { - int ic = i % Nedgecolors; - gc.color = agg::rgba(edgecolors(ic, 0), edgecolors(ic, 1), edgecolors(ic, 2), edgecolors(ic, 3)); - - if (Nlinewidths) { - gc.linewidth = linewidths(i % Nlinewidths); - } else { - gc.linewidth = 1.0; - } - if (Nlinestyles) { - gc.dashes = linestyles[i % Nlinestyles]; - } - } - - if (check_snap) { - gc.isaa = antialiaseds(i % Naa); - - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, has_codes); - clipped_t clipped(nan_removed, do_clip, width, height); - snapped_t snapped( - clipped, gc.snap_mode, path.total_vertices(), points_to_pixels(gc.linewidth)); - if (has_codes) { - snapped_curve_t curve(snapped); - _draw_path(curve, has_clippath, face, gc); - } else { - _draw_path(snapped, has_clippath, face, gc); - } - } else { - gc.isaa = antialiaseds(i % Naa); - - transformed_path_t tpath(path, trans); - nan_removed_t nan_removed(tpath, true, has_codes); - clipped_t clipped(nan_removed, do_clip, width, height); - if (has_codes) { - curve_t curve(clipped); - _draw_path(curve, has_clippath, face, gc); - } else { - _draw_path(clipped, has_clippath, face, gc); - } - } - } -} - -template <class PathGenerator, - class TransformArray, - class OffsetArray, - class ColorArray, - class LineWidthArray, - class AntialiasedArray> -inline void RendererAgg::draw_path_collection(GCAgg &gc, - agg::trans_affine &master_transform, - PathGenerator &path, - TransformArray &transforms, - OffsetArray &offsets, - agg::trans_affine &offset_trans, - ColorArray &facecolors, - ColorArray &edgecolors, - LineWidthArray &linewidths, - DashesVector &linestyles, - AntialiasedArray &antialiaseds) -{ - _draw_path_collection_generic(gc, - master_transform, - gc.cliprect, - gc.clippath.path, - gc.clippath.trans, - path, - transforms, - offsets, - offset_trans, - facecolors, - edgecolors, - linewidths, - linestyles, - antialiaseds, - true, - true); -} - -template <class CoordinateArray> -class QuadMeshGenerator -{ - unsigned m_meshWidth; - unsigned m_meshHeight; - CoordinateArray m_coordinates; - - class QuadMeshPathIterator - { - unsigned m_iterator; - unsigned m_m, m_n; - const CoordinateArray *m_coordinates; - - public: - QuadMeshPathIterator(unsigned m, unsigned n, const CoordinateArray *coordinates) - : m_iterator(0), m_m(m), m_n(n), m_coordinates(coordinates) - { - } - - private: - inline unsigned vertex(unsigned idx, double *x, double *y) - { - size_t m = m_m + ((idx & 0x2) >> 1); - size_t n = m_n + (((idx + 1) & 0x2) >> 1); - *x = (*m_coordinates)(n, m, 0); - *y = (*m_coordinates)(n, m, 1); - return (idx) ? agg::path_cmd_line_to : agg::path_cmd_move_to; - } - - public: - inline unsigned vertex(double *x, double *y) - { - if (m_iterator >= total_vertices()) { - return agg::path_cmd_stop; - } - return vertex(m_iterator++, x, y); - } - - inline void rewind(unsigned path_id) - { - m_iterator = path_id; - } - - inline unsigned total_vertices() - { - return 5; - } - - inline bool should_simplify() - { - return false; - } - }; - - public: - typedef QuadMeshPathIterator path_iterator; - - inline QuadMeshGenerator(unsigned meshWidth, unsigned meshHeight, CoordinateArray &coordinates) - : m_meshWidth(meshWidth), m_meshHeight(meshHeight), m_coordinates(coordinates) - { - } - - inline size_t num_paths() const - { - return (size_t) m_meshWidth * m_meshHeight; - } - - inline path_iterator operator()(size_t i) const - { - return QuadMeshPathIterator(i % m_meshWidth, i / m_meshWidth, &m_coordinates); - } -}; - -template <class CoordinateArray, class OffsetArray, class ColorArray> -inline void RendererAgg::draw_quad_mesh(GCAgg &gc, - agg::trans_affine &master_transform, - unsigned int mesh_width, - unsigned int mesh_height, - CoordinateArray &coordinates, - OffsetArray &offsets, - agg::trans_affine &offset_trans, - ColorArray &facecolors, - bool antialiased, - ColorArray &edgecolors) -{ - QuadMeshGenerator<CoordinateArray> path_generator(mesh_width, mesh_height, coordinates); - - array::empty<double> transforms; - array::scalar<double, 1> linewidths(gc.linewidth); - array::scalar<uint8_t, 1> antialiaseds(antialiased); - DashesVector linestyles; - - _draw_path_collection_generic(gc, - master_transform, - gc.cliprect, - gc.clippath.path, - gc.clippath.trans, - path_generator, - transforms, - offsets, - offset_trans, - facecolors, - edgecolors, - linewidths, - linestyles, - antialiaseds, - true, // check_snap - false); -} - -template <class PointArray, class ColorArray> -inline void RendererAgg::_draw_gouraud_triangle(PointArray &points, - ColorArray &colors, - agg::trans_affine trans, - bool has_clippath) -{ - typedef agg::rgba8 color_t; - typedef agg::span_gouraud_rgba<color_t> span_gen_t; - typedef agg::span_allocator<color_t> span_alloc_t; - - trans *= agg::trans_affine_scaling(1.0, -1.0); - trans *= agg::trans_affine_translation(0.0, (double)height); - - double tpoints[3][2]; - - for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 2; ++j) { - tpoints[i][j] = points(i, j); - } - trans.transform(&tpoints[i][0], &tpoints[i][1]); - if(std::isnan(tpoints[i][0]) || std::isnan(tpoints[i][1])) { - return; - } - } - - span_alloc_t span_alloc; - span_gen_t span_gen; - - span_gen.colors(agg::rgba(colors(0, 0), colors(0, 1), colors(0, 2), colors(0, 3)), - agg::rgba(colors(1, 0), colors(1, 1), colors(1, 2), colors(1, 3)), - agg::rgba(colors(2, 0), colors(2, 1), colors(2, 2), colors(2, 3))); - span_gen.triangle(tpoints[0][0], - tpoints[0][1], - tpoints[1][0], - tpoints[1][1], - tpoints[2][0], - tpoints[2][1], - 0.5); - - theRasterizer.add_path(span_gen); - - if (has_clippath) { - typedef agg::pixfmt_amask_adaptor<pixfmt, alpha_mask_type> pixfmt_amask_type; - typedef agg::renderer_base<pixfmt_amask_type> amask_ren_type; - typedef agg::renderer_scanline_aa<amask_ren_type, span_alloc_t, span_gen_t> - amask_aa_renderer_type; - - pixfmt_amask_type pfa(pixFmt, alphaMask); - amask_ren_type r(pfa); - amask_aa_renderer_type ren(r, span_alloc, span_gen); - agg::render_scanlines(theRasterizer, scanlineAlphaMask, ren); - } else { - agg::render_scanlines_aa(theRasterizer, slineP8, rendererBase, span_alloc, span_gen); - } -} - -template <class PointArray, class ColorArray> -inline void RendererAgg::draw_gouraud_triangle(GCAgg &gc, - PointArray &points, - ColorArray &colors, - agg::trans_affine &trans) -{ - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - set_clipbox(gc.cliprect, theRasterizer); - bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - - _draw_gouraud_triangle(points, colors, trans, has_clippath); -} - -template <class PointArray, class ColorArray> -inline void RendererAgg::draw_gouraud_triangles(GCAgg &gc, - PointArray &points, - ColorArray &colors, - agg::trans_affine &trans) -{ - theRasterizer.reset_clipping(); - rendererBase.reset_clipping(true); - set_clipbox(gc.cliprect, theRasterizer); - bool has_clippath = render_clippath(gc.clippath.path, gc.clippath.trans, gc.snap_mode); - - for (int i = 0; i < points.dim(0); ++i) { - typename PointArray::sub_t point = points.subarray(i); - typename ColorArray::sub_t color = colors.subarray(i); - - _draw_gouraud_triangle(point, color, trans, has_clippath); - } -} - -template <class R> -void RendererAgg::set_clipbox(const agg::rect_d &cliprect, R &rasterizer) -{ - // set the clip rectangle from the gc - - if (cliprect.x1 != 0.0 || cliprect.y1 != 0.0 || cliprect.x2 != 0.0 || cliprect.y2 != 0.0) { - rasterizer.clip_box(std::max(int(floor(cliprect.x1 + 0.5)), 0), - std::max(int(floor(height - cliprect.y1 + 0.5)), 0), - std::min(int(floor(cliprect.x2 + 0.5)), int(width)), - std::min(int(floor(height - cliprect.y2 + 0.5)), int(height))); - } else { - rasterizer.clip_box(0, 0, width, height); - } -} - -#endif diff --git a/contrib/python/matplotlib/py3/src/_backend_agg_basic_types.h b/contrib/python/matplotlib/py3/src/_backend_agg_basic_types.h deleted file mode 100644 index 21a84bb6a5..0000000000 --- a/contrib/python/matplotlib/py3/src/_backend_agg_basic_types.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef MPL_BACKEND_AGG_BASIC_TYPES_H -#define MPL_BACKEND_AGG_BASIC_TYPES_H - -/* Contains some simple types from the Agg backend that are also used - by other modules */ - -#include <vector> - -#include "agg_color_rgba.h" -#include "agg_math_stroke.h" -#include "path_converters.h" - -#include "py_adaptors.h" - -struct ClipPath -{ - py::PathIterator path; - agg::trans_affine trans; -}; - -struct SketchParams -{ - double scale; - double length; - double randomness; -}; - -class Dashes -{ - typedef std::vector<std::pair<double, double> > dash_t; - double dash_offset; - dash_t dashes; - - public: - double get_dash_offset() const - { - return dash_offset; - } - void set_dash_offset(double x) - { - dash_offset = x; - } - void add_dash_pair(double length, double skip) - { - dashes.push_back(std::make_pair(length, skip)); - } - size_t size() const - { - return dashes.size(); - } - - template <class T> - void dash_to_stroke(T &stroke, double dpi, bool isaa) - { - double scaleddpi = dpi / 72.0; - for (dash_t::const_iterator i = dashes.begin(); i != dashes.end(); ++i) { - double val0 = i->first; - double val1 = i->second; - val0 = val0 * scaleddpi; - val1 = val1 * scaleddpi; - if (!isaa) { - val0 = (int)val0 + 0.5; - val1 = (int)val1 + 0.5; - } - stroke.add_dash(val0, val1); - } - stroke.dash_start(get_dash_offset() * scaleddpi); - } -}; - -typedef std::vector<Dashes> DashesVector; - -class GCAgg -{ - public: - GCAgg() - : linewidth(1.0), - alpha(1.0), - cap(agg::butt_cap), - join(agg::round_join), - snap_mode(SNAP_FALSE) - { - } - - ~GCAgg() - { - } - - double linewidth; - double alpha; - bool forced_alpha; - agg::rgba color; - bool isaa; - - agg::line_cap_e cap; - agg::line_join_e join; - - agg::rect_d cliprect; - - ClipPath clippath; - - Dashes dashes; - - e_snap_mode snap_mode; - - py::PathIterator hatchpath; - agg::rgba hatch_color; - double hatch_linewidth; - - SketchParams sketch; - - bool has_hatchpath() - { - return hatchpath.total_vertices() != 0; - } - - private: - // prevent copying - GCAgg(const GCAgg &); - GCAgg &operator=(const GCAgg &); -}; - -#endif diff --git a/contrib/python/matplotlib/py3/src/_backend_agg_wrapper.cpp b/contrib/python/matplotlib/py3/src/_backend_agg_wrapper.cpp deleted file mode 100644 index ee69729be7..0000000000 --- a/contrib/python/matplotlib/py3/src/_backend_agg_wrapper.cpp +++ /dev/null @@ -1,646 +0,0 @@ -#include "mplutils.h" -#include "numpy_cpp.h" -#include "py_converters.h" -#include "_backend_agg.h" - -typedef struct -{ - PyObject_HEAD - RendererAgg *x; - Py_ssize_t shape[3]; - Py_ssize_t strides[3]; - Py_ssize_t suboffsets[3]; -} PyRendererAgg; - -static PyTypeObject PyRendererAggType; - -typedef struct -{ - PyObject_HEAD - BufferRegion *x; - Py_ssize_t shape[3]; - Py_ssize_t strides[3]; - Py_ssize_t suboffsets[3]; -} PyBufferRegion; - -static PyTypeObject PyBufferRegionType; - - -/********************************************************************** - * BufferRegion - * */ - -static PyObject *PyBufferRegion_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyBufferRegion *self; - self = (PyBufferRegion *)type->tp_alloc(type, 0); - self->x = NULL; - return (PyObject *)self; -} - -static void PyBufferRegion_dealloc(PyBufferRegion *self) -{ - delete self->x; - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *PyBufferRegion_to_string(PyBufferRegion *self, PyObject *args) -{ - char const* msg = - "BufferRegion.to_string is deprecated since Matplotlib 3.7 and will " - "be removed two minor releases later; use np.asarray(region) instead."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - return PyBytes_FromStringAndSize((const char *)self->x->get_data(), - (Py_ssize_t) self->x->get_height() * self->x->get_stride()); -} - -/* TODO: This doesn't seem to be used internally. Remove? */ - -static PyObject *PyBufferRegion_set_x(PyBufferRegion *self, PyObject *args) -{ - int x; - if (!PyArg_ParseTuple(args, "i:set_x", &x)) { - return NULL; - } - self->x->get_rect().x1 = x; - - Py_RETURN_NONE; -} - -static PyObject *PyBufferRegion_set_y(PyBufferRegion *self, PyObject *args) -{ - int y; - if (!PyArg_ParseTuple(args, "i:set_y", &y)) { - return NULL; - } - self->x->get_rect().y1 = y; - - Py_RETURN_NONE; -} - -static PyObject *PyBufferRegion_get_extents(PyBufferRegion *self, PyObject *args) -{ - agg::rect_i rect = self->x->get_rect(); - - return Py_BuildValue("IIII", rect.x1, rect.y1, rect.x2, rect.y2); -} - -static PyObject *PyBufferRegion_to_string_argb(PyBufferRegion *self, PyObject *args) -{ - char const* msg = - "BufferRegion.to_string_argb is deprecated since Matplotlib 3.7 and " - "will be removed two minor releases later; use " - "np.take(region, [2, 1, 0, 3], axis=2) instead."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - PyObject *bufobj; - uint8_t *buf; - Py_ssize_t height, stride; - height = self->x->get_height(); - stride = self->x->get_stride(); - bufobj = PyBytes_FromStringAndSize(NULL, height * stride); - buf = (uint8_t *)PyBytes_AS_STRING(bufobj); - - CALL_CPP_CLEANUP("to_string_argb", (self->x->to_string_argb(buf)), Py_DECREF(bufobj)); - - return bufobj; -} - -int PyBufferRegion_get_buffer(PyBufferRegion *self, Py_buffer *buf, int flags) -{ - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = self->x->get_data(); - buf->len = (Py_ssize_t)self->x->get_width() * (Py_ssize_t)self->x->get_height() * 4; - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 3; - self->shape[0] = self->x->get_height(); - self->shape[1] = self->x->get_width(); - self->shape[2] = 4; - buf->shape = self->shape; - self->strides[0] = self->x->get_width() * 4; - self->strides[1] = 4; - self->strides[2] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; -} - -static PyTypeObject *PyBufferRegion_init_type() -{ - static PyMethodDef methods[] = { - { "to_string", (PyCFunction)PyBufferRegion_to_string, METH_NOARGS, NULL }, - { "to_string_argb", (PyCFunction)PyBufferRegion_to_string_argb, METH_NOARGS, NULL }, - { "set_x", (PyCFunction)PyBufferRegion_set_x, METH_VARARGS, NULL }, - { "set_y", (PyCFunction)PyBufferRegion_set_y, METH_VARARGS, NULL }, - { "get_extents", (PyCFunction)PyBufferRegion_get_extents, METH_NOARGS, NULL }, - { NULL } - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyBufferRegion_get_buffer; - - PyBufferRegionType.tp_name = "matplotlib.backends._backend_agg.BufferRegion"; - PyBufferRegionType.tp_basicsize = sizeof(PyBufferRegion); - PyBufferRegionType.tp_dealloc = (destructor)PyBufferRegion_dealloc; - PyBufferRegionType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyBufferRegionType.tp_methods = methods; - PyBufferRegionType.tp_new = PyBufferRegion_new; - PyBufferRegionType.tp_as_buffer = &buffer_procs; - - return &PyBufferRegionType; -} - -/********************************************************************** - * RendererAgg - * */ - -static PyObject *PyRendererAgg_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyRendererAgg *self; - self = (PyRendererAgg *)type->tp_alloc(type, 0); - self->x = NULL; - return (PyObject *)self; -} - -static int PyRendererAgg_init(PyRendererAgg *self, PyObject *args, PyObject *kwds) -{ - unsigned int width; - unsigned int height; - double dpi; - int debug = 0; - - if (!PyArg_ParseTuple(args, "IId|i:RendererAgg", &width, &height, &dpi, &debug)) { - return -1; - } - - if (dpi <= 0.0) { - PyErr_SetString(PyExc_ValueError, "dpi must be positive"); - return -1; - } - - if (width >= 1 << 16 || height >= 1 << 16) { - PyErr_Format( - PyExc_ValueError, - "Image size of %dx%d pixels is too large. " - "It must be less than 2^16 in each direction.", - width, height); - return -1; - } - - CALL_CPP_INIT("RendererAgg", self->x = new RendererAgg(width, height, dpi)) - - return 0; -} - -static void PyRendererAgg_dealloc(PyRendererAgg *self) -{ - delete self->x; - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *PyRendererAgg_draw_path(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - py::PathIterator path; - agg::trans_affine trans; - PyObject *faceobj = NULL; - agg::rgba face; - - if (!PyArg_ParseTuple(args, - "O&O&O&|O:draw_path", - &convert_gcagg, - &gc, - &convert_path, - &path, - &convert_trans_affine, - &trans, - &faceobj)) { - return NULL; - } - - if (!convert_face(faceobj, gc, &face)) { - return NULL; - } - - CALL_CPP("draw_path", (self->x->draw_path(gc, path, trans, face))); - - Py_RETURN_NONE; -} - -static PyObject *PyRendererAgg_draw_text_image(PyRendererAgg *self, PyObject *args) -{ - numpy::array_view<agg::int8u, 2> image; - double x; - double y; - double angle; - GCAgg gc; - - if (!PyArg_ParseTuple(args, - "O&dddO&:draw_text_image", - &image.converter_contiguous, - &image, - &x, - &y, - &angle, - &convert_gcagg, - &gc)) { - return NULL; - } - - CALL_CPP("draw_text_image", (self->x->draw_text_image(gc, image, x, y, angle))); - - Py_RETURN_NONE; -} - -PyObject *PyRendererAgg_draw_markers(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - py::PathIterator marker_path; - agg::trans_affine marker_path_trans; - py::PathIterator path; - agg::trans_affine trans; - PyObject *faceobj = NULL; - agg::rgba face; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&O&|O:draw_markers", - &convert_gcagg, - &gc, - &convert_path, - &marker_path, - &convert_trans_affine, - &marker_path_trans, - &convert_path, - &path, - &convert_trans_affine, - &trans, - &faceobj)) { - return NULL; - } - - if (!convert_face(faceobj, gc, &face)) { - return NULL; - } - - CALL_CPP("draw_markers", - (self->x->draw_markers(gc, marker_path, marker_path_trans, path, trans, face))); - - Py_RETURN_NONE; -} - -static PyObject *PyRendererAgg_draw_image(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - double x; - double y; - numpy::array_view<agg::int8u, 3> image; - - if (!PyArg_ParseTuple(args, - "O&ddO&:draw_image", - &convert_gcagg, - &gc, - &x, - &y, - &image.converter_contiguous, - &image)) { - return NULL; - } - - x = mpl_round(x); - y = mpl_round(y); - - gc.alpha = 1.0; - CALL_CPP("draw_image", (self->x->draw_image(gc, x, y, image))); - - Py_RETURN_NONE; -} - -static PyObject * -PyRendererAgg_draw_path_collection(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - agg::trans_affine master_transform; - py::PathGenerator paths; - numpy::array_view<const double, 3> transforms; - numpy::array_view<const double, 2> offsets; - agg::trans_affine offset_trans; - numpy::array_view<const double, 2> facecolors; - numpy::array_view<const double, 2> edgecolors; - numpy::array_view<const double, 1> linewidths; - DashesVector dashes; - numpy::array_view<const uint8_t, 1> antialiaseds; - PyObject *ignored; - PyObject *offset_position; // offset position is no longer used - - if (!PyArg_ParseTuple(args, - "O&O&O&O&O&O&O&O&O&O&O&OO:draw_path_collection", - &convert_gcagg, - &gc, - &convert_trans_affine, - &master_transform, - &convert_pathgen, - &paths, - &convert_transforms, - &transforms, - &convert_points, - &offsets, - &convert_trans_affine, - &offset_trans, - &convert_colors, - &facecolors, - &convert_colors, - &edgecolors, - &linewidths.converter, - &linewidths, - &convert_dashes_vector, - &dashes, - &antialiaseds.converter, - &antialiaseds, - &ignored, - &offset_position)) { - return NULL; - } - - CALL_CPP("draw_path_collection", - (self->x->draw_path_collection(gc, - master_transform, - paths, - transforms, - offsets, - offset_trans, - facecolors, - edgecolors, - linewidths, - dashes, - antialiaseds))); - - Py_RETURN_NONE; -} - -static PyObject *PyRendererAgg_draw_quad_mesh(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - agg::trans_affine master_transform; - unsigned int mesh_width; - unsigned int mesh_height; - numpy::array_view<const double, 3> coordinates; - numpy::array_view<const double, 2> offsets; - agg::trans_affine offset_trans; - numpy::array_view<const double, 2> facecolors; - bool antialiased; - numpy::array_view<const double, 2> edgecolors; - - if (!PyArg_ParseTuple(args, - "O&O&IIO&O&O&O&O&O&:draw_quad_mesh", - &convert_gcagg, - &gc, - &convert_trans_affine, - &master_transform, - &mesh_width, - &mesh_height, - &coordinates.converter, - &coordinates, - &convert_points, - &offsets, - &convert_trans_affine, - &offset_trans, - &convert_colors, - &facecolors, - &convert_bool, - &antialiased, - &convert_colors, - &edgecolors)) { - return NULL; - } - - CALL_CPP("draw_quad_mesh", - (self->x->draw_quad_mesh(gc, - master_transform, - mesh_width, - mesh_height, - coordinates, - offsets, - offset_trans, - facecolors, - antialiased, - edgecolors))); - - Py_RETURN_NONE; -} - -static PyObject * -PyRendererAgg_draw_gouraud_triangle(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - numpy::array_view<const double, 2> points; - numpy::array_view<const double, 2> colors; - agg::trans_affine trans; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&|O:draw_gouraud_triangle", - &convert_gcagg, - &gc, - &points.converter, - &points, - &colors.converter, - &colors, - &convert_trans_affine, - &trans)) { - return NULL; - } - - if (points.dim(0) != 3 || points.dim(1) != 2) { - PyErr_Format(PyExc_ValueError, - "points must have shape (3, 2), " - "got (%" NPY_INTP_FMT ", %" NPY_INTP_FMT ")", - points.dim(0), points.dim(1)); - return NULL; - } - - if (colors.dim(0) != 3 || colors.dim(1) != 4) { - PyErr_Format(PyExc_ValueError, - "colors must have shape (3, 4), " - "got (%" NPY_INTP_FMT ", %" NPY_INTP_FMT ")", - colors.dim(0), colors.dim(1)); - return NULL; - } - - - CALL_CPP("draw_gouraud_triangle", (self->x->draw_gouraud_triangle(gc, points, colors, trans))); - - Py_RETURN_NONE; -} - -static PyObject * -PyRendererAgg_draw_gouraud_triangles(PyRendererAgg *self, PyObject *args) -{ - GCAgg gc; - numpy::array_view<const double, 3> points; - numpy::array_view<const double, 3> colors; - agg::trans_affine trans; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&|O:draw_gouraud_triangles", - &convert_gcagg, - &gc, - &points.converter, - &points, - &colors.converter, - &colors, - &convert_trans_affine, - &trans)) { - return NULL; - } - if (points.size() && !check_trailing_shape(points, "points", 3, 2)) { - return NULL; - } - if (colors.size() && !check_trailing_shape(colors, "colors", 3, 4)) { - return NULL; - } - if (points.size() != colors.size()) { - PyErr_Format(PyExc_ValueError, - "points and colors arrays must be the same length, got " - "%" NPY_INTP_FMT " points and %" NPY_INTP_FMT "colors", - points.dim(0), colors.dim(0)); - return NULL; - } - - CALL_CPP("draw_gouraud_triangles", self->x->draw_gouraud_triangles(gc, points, colors, trans)); - - Py_RETURN_NONE; -} - -int PyRendererAgg_get_buffer(PyRendererAgg *self, Py_buffer *buf, int flags) -{ - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = self->x->pixBuffer; - buf->len = (Py_ssize_t)self->x->get_width() * (Py_ssize_t)self->x->get_height() * 4; - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 3; - self->shape[0] = self->x->get_height(); - self->shape[1] = self->x->get_width(); - self->shape[2] = 4; - buf->shape = self->shape; - self->strides[0] = self->x->get_width() * 4; - self->strides[1] = 4; - self->strides[2] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; -} - -static PyObject *PyRendererAgg_clear(PyRendererAgg *self, PyObject *args) -{ - CALL_CPP("clear", self->x->clear()); - - Py_RETURN_NONE; -} - -static PyObject *PyRendererAgg_copy_from_bbox(PyRendererAgg *self, PyObject *args) -{ - agg::rect_d bbox; - BufferRegion *reg; - PyObject *regobj; - - if (!PyArg_ParseTuple(args, "O&:copy_from_bbox", &convert_rect, &bbox)) { - return 0; - } - - CALL_CPP("copy_from_bbox", (reg = self->x->copy_from_bbox(bbox))); - - regobj = PyBufferRegion_new(&PyBufferRegionType, NULL, NULL); - ((PyBufferRegion *)regobj)->x = reg; - - return regobj; -} - -static PyObject *PyRendererAgg_restore_region(PyRendererAgg *self, PyObject *args) -{ - PyBufferRegion *regobj; - int xx1 = 0, yy1 = 0, xx2 = 0, yy2 = 0, x = 0, y = 0; - - if (!PyArg_ParseTuple(args, - "O!|iiiiii:restore_region", - &PyBufferRegionType, - ®obj, - &xx1, - &yy1, - &xx2, - &yy2, - &x, - &y)) { - return 0; - } - - if (PySequence_Size(args) == 1) { - CALL_CPP("restore_region", self->x->restore_region(*(regobj->x))); - } else { - CALL_CPP("restore_region", self->x->restore_region(*(regobj->x), xx1, yy1, xx2, yy2, x, y)); - } - - Py_RETURN_NONE; -} - -static PyTypeObject *PyRendererAgg_init_type() -{ - static PyMethodDef methods[] = { - {"draw_path", (PyCFunction)PyRendererAgg_draw_path, METH_VARARGS, NULL}, - {"draw_markers", (PyCFunction)PyRendererAgg_draw_markers, METH_VARARGS, NULL}, - {"draw_text_image", (PyCFunction)PyRendererAgg_draw_text_image, METH_VARARGS, NULL}, - {"draw_image", (PyCFunction)PyRendererAgg_draw_image, METH_VARARGS, NULL}, - {"draw_path_collection", (PyCFunction)PyRendererAgg_draw_path_collection, METH_VARARGS, NULL}, - {"draw_quad_mesh", (PyCFunction)PyRendererAgg_draw_quad_mesh, METH_VARARGS, NULL}, - {"draw_gouraud_triangle", (PyCFunction)PyRendererAgg_draw_gouraud_triangle, METH_VARARGS, NULL}, - {"draw_gouraud_triangles", (PyCFunction)PyRendererAgg_draw_gouraud_triangles, METH_VARARGS, NULL}, - - {"clear", (PyCFunction)PyRendererAgg_clear, METH_NOARGS, NULL}, - - {"copy_from_bbox", (PyCFunction)PyRendererAgg_copy_from_bbox, METH_VARARGS, NULL}, - {"restore_region", (PyCFunction)PyRendererAgg_restore_region, METH_VARARGS, NULL}, - {NULL} - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyRendererAgg_get_buffer; - - PyRendererAggType.tp_name = "matplotlib.backends._backend_agg.RendererAgg"; - PyRendererAggType.tp_basicsize = sizeof(PyRendererAgg); - PyRendererAggType.tp_dealloc = (destructor)PyRendererAgg_dealloc; - PyRendererAggType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyRendererAggType.tp_methods = methods; - PyRendererAggType.tp_init = (initproc)PyRendererAgg_init; - PyRendererAggType.tp_new = PyRendererAgg_new; - PyRendererAggType.tp_as_buffer = &buffer_procs; - - return &PyRendererAggType; -} - -static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "_backend_agg" }; - -PyMODINIT_FUNC PyInit__backend_agg(void) -{ - import_array(); - PyObject *m; - if (!(m = PyModule_Create(&moduledef)) - || prepare_and_add_type(PyRendererAgg_init_type(), m) - // BufferRegion is not constructible from Python, thus not added to the module. - || PyType_Ready(PyBufferRegion_init_type()) - ) { - Py_XDECREF(m); - return NULL; - } - return m; -} diff --git a/contrib/python/matplotlib/py3/src/_c_internal_utils.c b/contrib/python/matplotlib/py3/src/_c_internal_utils.c deleted file mode 100644 index f1bd22a42c..0000000000 --- a/contrib/python/matplotlib/py3/src/_c_internal_utils.c +++ /dev/null @@ -1,211 +0,0 @@ -#define PY_SSIZE_T_CLEAN -#include <Python.h> -#ifdef __linux__ -#include <dlfcn.h> -#endif -#ifdef _WIN32 -#include <Objbase.h> -#include <Shobjidl.h> -#include <Windows.h> -#endif - -static PyObject* -mpl_display_is_valid(PyObject* module) -{ -#ifdef __linux__ - void* libX11; - // The getenv check is redundant but helps performance as it is much faster - // than dlopen(). - if (getenv("DISPLAY") - && (libX11 = dlopen("libX11.so.6", RTLD_LAZY))) { - struct Display* display = NULL; - struct Display* (* XOpenDisplay)(char const*) = - dlsym(libX11, "XOpenDisplay"); - int (* XCloseDisplay)(struct Display*) = - dlsym(libX11, "XCloseDisplay"); - if (XOpenDisplay && XCloseDisplay - && (display = XOpenDisplay(NULL))) { - XCloseDisplay(display); - } - if (dlclose(libX11)) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - return NULL; - } - if (display) { - Py_RETURN_TRUE; - } - } - void* libwayland_client; - if (getenv("WAYLAND_DISPLAY") - && (libwayland_client = dlopen("libwayland-client.so.0", RTLD_LAZY))) { - struct wl_display* display = NULL; - struct wl_display* (* wl_display_connect)(char const*) = - dlsym(libwayland_client, "wl_display_connect"); - void (* wl_display_disconnect)(struct wl_display*) = - dlsym(libwayland_client, "wl_display_disconnect"); - if (wl_display_connect && wl_display_disconnect - && (display = wl_display_connect(NULL))) { - wl_display_disconnect(display); - } - if (dlclose(libwayland_client)) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - return NULL; - } - if (display) { - Py_RETURN_TRUE; - } - } - Py_RETURN_FALSE; -#else - Py_RETURN_TRUE; -#endif -} - -static PyObject* -mpl_GetCurrentProcessExplicitAppUserModelID(PyObject* module) -{ -#ifdef _WIN32 - wchar_t* appid = NULL; - HRESULT hr = GetCurrentProcessExplicitAppUserModelID(&appid); - if (FAILED(hr)) { - return PyErr_SetFromWindowsErr(hr); - } - PyObject* py_appid = PyUnicode_FromWideChar(appid, -1); - CoTaskMemFree(appid); - return py_appid; -#else - Py_RETURN_NONE; -#endif -} - -static PyObject* -mpl_SetCurrentProcessExplicitAppUserModelID(PyObject* module, PyObject* arg) -{ -#ifdef _WIN32 - wchar_t* appid = PyUnicode_AsWideCharString(arg, NULL); - if (!appid) { - return NULL; - } - HRESULT hr = SetCurrentProcessExplicitAppUserModelID(appid); - PyMem_Free(appid); - if (FAILED(hr)) { - return PyErr_SetFromWindowsErr(hr); - } - Py_RETURN_NONE; -#else - Py_RETURN_NONE; -#endif -} - -static PyObject* -mpl_GetForegroundWindow(PyObject* module) -{ -#ifdef _WIN32 - return PyLong_FromVoidPtr(GetForegroundWindow()); -#else - Py_RETURN_NONE; -#endif -} - -static PyObject* -mpl_SetForegroundWindow(PyObject* module, PyObject *arg) -{ -#ifdef _WIN32 - HWND handle = PyLong_AsVoidPtr(arg); - if (PyErr_Occurred()) { - return NULL; - } - if (!SetForegroundWindow(handle)) { - return PyErr_Format(PyExc_RuntimeError, "Error setting window"); - } - Py_RETURN_NONE; -#else - Py_RETURN_NONE; -#endif -} - -static PyObject* -mpl_SetProcessDpiAwareness_max(PyObject* module) -{ -#ifdef _WIN32 -#ifdef _DPI_AWARENESS_CONTEXTS_ - // These functions and options were added in later Windows 10 updates, so - // must be loaded dynamically. - typedef BOOL (WINAPI *IsValidDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT); - typedef BOOL (WINAPI *SetProcessDpiAwarenessContext_t)(DPI_AWARENESS_CONTEXT); - - HMODULE user32 = LoadLibrary("user32.dll"); - IsValidDpiAwarenessContext_t IsValidDpiAwarenessContextPtr = - (IsValidDpiAwarenessContext_t)GetProcAddress( - user32, "IsValidDpiAwarenessContext"); - SetProcessDpiAwarenessContext_t SetProcessDpiAwarenessContextPtr = - (SetProcessDpiAwarenessContext_t)GetProcAddress( - user32, "SetProcessDpiAwarenessContext"); - DPI_AWARENESS_CONTEXT ctxs[3] = { - DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2, // Win10 Creators Update - DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE, // Win10 - DPI_AWARENESS_CONTEXT_SYSTEM_AWARE}; // Win10 - if (IsValidDpiAwarenessContextPtr != NULL - && SetProcessDpiAwarenessContextPtr != NULL) { - for (int i = 0; i < sizeof(ctxs) / sizeof(DPI_AWARENESS_CONTEXT); ++i) { - if (IsValidDpiAwarenessContextPtr(ctxs[i])) { - SetProcessDpiAwarenessContextPtr(ctxs[i]); - break; - } - } - } else { - // Added in Windows Vista. - SetProcessDPIAware(); - } - FreeLibrary(user32); -#else - // Added in Windows Vista. - SetProcessDPIAware(); -#endif -#endif - Py_RETURN_NONE; -} - -static PyMethodDef functions[] = { - {"display_is_valid", (PyCFunction)mpl_display_is_valid, METH_NOARGS, - "display_is_valid()\n--\n\n" - "Check whether the current X11 or Wayland display is valid.\n\n" - "On Linux, returns True if either $DISPLAY is set and XOpenDisplay(NULL)\n" - "succeeds, or $WAYLAND_DISPLAY is set and wl_display_connect(NULL)\n" - "succeeds.\n\n" - "On other platforms, always returns True."}, - {"Win32_GetCurrentProcessExplicitAppUserModelID", - (PyCFunction)mpl_GetCurrentProcessExplicitAppUserModelID, METH_NOARGS, - "Win32_GetCurrentProcessExplicitAppUserModelID()\n--\n\n" - "Wrapper for Windows's GetCurrentProcessExplicitAppUserModelID.\n\n" - "On non-Windows platforms, always returns None."}, - {"Win32_SetCurrentProcessExplicitAppUserModelID", - (PyCFunction)mpl_SetCurrentProcessExplicitAppUserModelID, METH_O, - "Win32_SetCurrentProcessExplicitAppUserModelID(appid, /)\n--\n\n" - "Wrapper for Windows's SetCurrentProcessExplicitAppUserModelID.\n\n" - "On non-Windows platforms, does nothing."}, - {"Win32_GetForegroundWindow", - (PyCFunction)mpl_GetForegroundWindow, METH_NOARGS, - "Win32_GetForegroundWindow()\n--\n\n" - "Wrapper for Windows' GetForegroundWindow.\n\n" - "On non-Windows platforms, always returns None."}, - {"Win32_SetForegroundWindow", - (PyCFunction)mpl_SetForegroundWindow, METH_O, - "Win32_SetForegroundWindow(hwnd, /)\n--\n\n" - "Wrapper for Windows' SetForegroundWindow.\n\n" - "On non-Windows platforms, does nothing."}, - {"Win32_SetProcessDpiAwareness_max", - (PyCFunction)mpl_SetProcessDpiAwareness_max, METH_NOARGS, - "Win32_SetProcessDpiAwareness_max()\n--\n\n" - "Set Windows' process DPI awareness to best option available.\n\n" - "On non-Windows platforms, does nothing."}, - {NULL, NULL}}; // sentinel. -static PyModuleDef util_module = { - PyModuleDef_HEAD_INIT, "_c_internal_utils", NULL, 0, functions -}; - -#pragma GCC visibility push(default) -PyMODINIT_FUNC PyInit__c_internal_utils(void) -{ - return PyModule_Create(&util_module); -} diff --git a/contrib/python/matplotlib/py3/src/_image_resample.h b/contrib/python/matplotlib/py3/src/_image_resample.h deleted file mode 100644 index 2c91da4087..0000000000 --- a/contrib/python/matplotlib/py3/src/_image_resample.h +++ /dev/null @@ -1,834 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_RESAMPLE_H -#define MPL_RESAMPLE_H - -#include "agg_image_accessors.h" -#include "agg_path_storage.h" -#include "agg_pixfmt_gray.h" -#include "agg_pixfmt_rgb.h" -#include "agg_pixfmt_rgba.h" -#include "agg_renderer_base.h" -#include "agg_renderer_scanline.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_scanline_u.h" -#include "agg_span_allocator.h" -#include "agg_span_converter.h" -#include "agg_span_image_filter_gray.h" -#include "agg_span_image_filter_rgba.h" -#include "agg_span_interpolator_adaptor.h" -#include "agg_span_interpolator_linear.h" - -#include "agg_workaround.h" - -#include <type_traits> - -// Based on: - -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.4 -// Copyright (C) 2002-2005 Maxim Shemanarev (http://antigrain.com/) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://antigrain.com/ -//---------------------------------------------------------------------------- -// -// Adaptation for high precision colors has been sponsored by -// Liberty Technology Systems, Inc., visit http://lib-sys.com -// -// Liberty Technology Systems, Inc. is the provider of -// PostScript and PDF technology for software developers. -// - -//===================================================================gray64 -namespace agg -{ - struct gray64 - { - typedef double value_type; - typedef double calc_type; - typedef double long_type; - typedef gray64 self_type; - - value_type v; - value_type a; - - //-------------------------------------------------------------------- - gray64() {} - - //-------------------------------------------------------------------- - explicit gray64(value_type v_, value_type a_ = 1) : - v(v_), a(a_) {} - - //-------------------------------------------------------------------- - gray64(const self_type& c, value_type a_) : - v(c.v), a(a_) {} - - //-------------------------------------------------------------------- - gray64(const gray64& c) : - v(c.v), - a(c.a) {} - - //-------------------------------------------------------------------- - static AGG_INLINE double to_double(value_type a) - { - return a; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type from_double(double a) - { - return value_type(a); - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type empty_value() - { - return 0; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type full_value() - { - return 1; - } - - //-------------------------------------------------------------------- - AGG_INLINE bool is_transparent() const - { - return a <= 0; - } - - //-------------------------------------------------------------------- - AGG_INLINE bool is_opaque() const - { - return a >= 1; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type invert(value_type x) - { - return 1 - x; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type multiply(value_type a, value_type b) - { - return value_type(a * b); - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type demultiply(value_type a, value_type b) - { - return (b == 0) ? 0 : value_type(a / b); - } - - //-------------------------------------------------------------------- - template<typename T> - static AGG_INLINE T downscale(T a) - { - return a; - } - - //-------------------------------------------------------------------- - template<typename T> - static AGG_INLINE T downshift(T a, unsigned n) - { - return n > 0 ? a / (1 << n) : a; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type mult_cover(value_type a, cover_type b) - { - return value_type(a * b / cover_mask); - } - - //-------------------------------------------------------------------- - static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) - { - return cover_type(uround(a * b)); - } - - //-------------------------------------------------------------------- - // Interpolate p to q by a, assuming q is premultiplied by a. - static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) - { - return (1 - a) * p + q; // more accurate than "p + q - p * a" - } - - //-------------------------------------------------------------------- - // Interpolate p to q by a. - static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) - { - // The form "p + a * (q - p)" avoids a multiplication, but may produce an - // inaccurate result. For example, "p + (q - p)" may not be exactly equal - // to q. Therefore, stick to the basic expression, which at least produces - // the correct result at either extreme. - return (1 - a) * p + a * q; - } - - //-------------------------------------------------------------------- - self_type& clear() - { - v = a = 0; - return *this; - } - - //-------------------------------------------------------------------- - self_type& transparent() - { - a = 0; - return *this; - } - - //-------------------------------------------------------------------- - self_type& opacity(double a_) - { - if (a_ < 0) a = 0; - else if (a_ > 1) a = 1; - else a = value_type(a_); - return *this; - } - - //-------------------------------------------------------------------- - double opacity() const - { - return a; - } - - - //-------------------------------------------------------------------- - self_type& premultiply() - { - if (a < 0) v = 0; - else if(a < 1) v *= a; - return *this; - } - - //-------------------------------------------------------------------- - self_type& demultiply() - { - if (a < 0) v = 0; - else if (a < 1) v /= a; - return *this; - } - - //-------------------------------------------------------------------- - self_type gradient(self_type c, double k) const - { - return self_type( - value_type(v + (c.v - v) * k), - value_type(a + (c.a - a) * k)); - } - - //-------------------------------------------------------------------- - static self_type no_color() { return self_type(0,0); } - }; - - - //====================================================================rgba32 - struct rgba64 - { - typedef double value_type; - typedef double calc_type; - typedef double long_type; - typedef rgba64 self_type; - - value_type r; - value_type g; - value_type b; - value_type a; - - //-------------------------------------------------------------------- - rgba64() {} - - //-------------------------------------------------------------------- - rgba64(value_type r_, value_type g_, value_type b_, value_type a_= 1) : - r(r_), g(g_), b(b_), a(a_) {} - - //-------------------------------------------------------------------- - rgba64(const self_type& c, float a_) : - r(c.r), g(c.g), b(c.b), a(a_) {} - - //-------------------------------------------------------------------- - rgba64(const rgba& c) : - r(value_type(c.r)), g(value_type(c.g)), b(value_type(c.b)), a(value_type(c.a)) {} - - //-------------------------------------------------------------------- - operator rgba() const - { - return rgba(r, g, b, a); - } - - //-------------------------------------------------------------------- - static AGG_INLINE double to_double(value_type a) - { - return a; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type from_double(double a) - { - return value_type(a); - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type empty_value() - { - return 0; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type full_value() - { - return 1; - } - - //-------------------------------------------------------------------- - AGG_INLINE bool is_transparent() const - { - return a <= 0; - } - - //-------------------------------------------------------------------- - AGG_INLINE bool is_opaque() const - { - return a >= 1; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type invert(value_type x) - { - return 1 - x; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type multiply(value_type a, value_type b) - { - return value_type(a * b); - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type demultiply(value_type a, value_type b) - { - return (b == 0) ? 0 : value_type(a / b); - } - - //-------------------------------------------------------------------- - template<typename T> - static AGG_INLINE T downscale(T a) - { - return a; - } - - //-------------------------------------------------------------------- - template<typename T> - static AGG_INLINE T downshift(T a, unsigned n) - { - return n > 0 ? a / (1 << n) : a; - } - - //-------------------------------------------------------------------- - static AGG_INLINE value_type mult_cover(value_type a, cover_type b) - { - return value_type(a * b / cover_mask); - } - - //-------------------------------------------------------------------- - static AGG_INLINE cover_type scale_cover(cover_type a, value_type b) - { - return cover_type(uround(a * b)); - } - - //-------------------------------------------------------------------- - // Interpolate p to q by a, assuming q is premultiplied by a. - static AGG_INLINE value_type prelerp(value_type p, value_type q, value_type a) - { - return (1 - a) * p + q; // more accurate than "p + q - p * a" - } - - //-------------------------------------------------------------------- - // Interpolate p to q by a. - static AGG_INLINE value_type lerp(value_type p, value_type q, value_type a) - { - // The form "p + a * (q - p)" avoids a multiplication, but may produce an - // inaccurate result. For example, "p + (q - p)" may not be exactly equal - // to q. Therefore, stick to the basic expression, which at least produces - // the correct result at either extreme. - return (1 - a) * p + a * q; - } - - //-------------------------------------------------------------------- - self_type& clear() - { - r = g = b = a = 0; - return *this; - } - - //-------------------------------------------------------------------- - self_type& transparent() - { - a = 0; - return *this; - } - - //-------------------------------------------------------------------- - AGG_INLINE self_type& opacity(double a_) - { - if (a_ < 0) a = 0; - else if (a_ > 1) a = 1; - else a = value_type(a_); - return *this; - } - - //-------------------------------------------------------------------- - double opacity() const - { - return a; - } - - //-------------------------------------------------------------------- - AGG_INLINE self_type& premultiply() - { - if (a < 1) - { - if (a <= 0) - { - r = g = b = 0; - } - else - { - r *= a; - g *= a; - b *= a; - } - } - return *this; - } - - //-------------------------------------------------------------------- - AGG_INLINE self_type& demultiply() - { - if (a < 1) - { - if (a <= 0) - { - r = g = b = 0; - } - else - { - r /= a; - g /= a; - b /= a; - } - } - return *this; - } - - //-------------------------------------------------------------------- - AGG_INLINE self_type gradient(const self_type& c, double k) const - { - self_type ret; - ret.r = value_type(r + (c.r - r) * k); - ret.g = value_type(g + (c.g - g) * k); - ret.b = value_type(b + (c.b - b) * k); - ret.a = value_type(a + (c.a - a) * k); - return ret; - } - - //-------------------------------------------------------------------- - AGG_INLINE void add(const self_type& c, unsigned cover) - { - if (cover == cover_mask) - { - if (c.is_opaque()) - { - *this = c; - return; - } - else - { - r += c.r; - g += c.g; - b += c.b; - a += c.a; - } - } - else - { - r += mult_cover(c.r, cover); - g += mult_cover(c.g, cover); - b += mult_cover(c.b, cover); - a += mult_cover(c.a, cover); - } - if (a > 1) a = 1; - if (r > a) r = a; - if (g > a) g = a; - if (b > a) b = a; - } - - //-------------------------------------------------------------------- - static self_type no_color() { return self_type(0,0,0,0); } - }; -} - - -typedef enum { - NEAREST, - BILINEAR, - BICUBIC, - SPLINE16, - SPLINE36, - HANNING, - HAMMING, - HERMITE, - KAISER, - QUADRIC, - CATROM, - GAUSSIAN, - BESSEL, - MITCHELL, - SINC, - LANCZOS, - BLACKMAN, - _n_interpolation -} interpolation_e; - - -// T is rgba if and only if it has an T::r field. -template<typename T, typename = void> struct is_grayscale : std::true_type {}; -template<typename T> struct is_grayscale<T, decltype(T::r, void())> : std::false_type {}; - - -template<typename color_type> -struct type_mapping -{ - using blender_type = typename std::conditional< - is_grayscale<color_type>::value, - agg::blender_gray<color_type>, - typename std::conditional< - std::is_same<color_type, agg::rgba8>::value, - fixed_blender_rgba_plain<color_type, agg::order_rgba>, - agg::blender_rgba_plain<color_type, agg::order_rgba> - >::type - >::type; - using pixfmt_type = typename std::conditional< - is_grayscale<color_type>::value, - agg::pixfmt_alpha_blend_gray<blender_type, agg::rendering_buffer>, - agg::pixfmt_alpha_blend_rgba<blender_type, agg::rendering_buffer> - >::type; - using pixfmt_pre_type = typename std::conditional< - is_grayscale<color_type>::value, - pixfmt_type, - agg::pixfmt_alpha_blend_rgba< - typename std::conditional< - std::is_same<color_type, agg::rgba8>::value, - fixed_blender_rgba_pre<color_type, agg::order_rgba>, - agg::blender_rgba_pre<color_type, agg::order_rgba> - >::type, - agg::rendering_buffer> - >::type; - template<typename A> using span_gen_affine_type = typename std::conditional< - is_grayscale<color_type>::value, - agg::span_image_resample_gray_affine<A>, - agg::span_image_resample_rgba_affine<A> - >::type; - template<typename A, typename B> using span_gen_filter_type = typename std::conditional< - is_grayscale<color_type>::value, - agg::span_image_filter_gray<A, B>, - agg::span_image_filter_rgba<A, B> - >::type; - template<typename A, typename B> using span_gen_nn_type = typename std::conditional< - is_grayscale<color_type>::value, - agg::span_image_filter_gray_nn<A, B>, - agg::span_image_filter_rgba_nn<A, B> - >::type; -}; - - -template<typename color_type> -class span_conv_alpha -{ -public: - span_conv_alpha(const double alpha) : - m_alpha(alpha) - { - } - - void prepare() {} - - void generate(color_type* span, int x, int y, unsigned len) const - { - if (m_alpha != 1.0) { - do { - span->a *= m_alpha; - ++span; - } while (--len); - } - } -private: - - const double m_alpha; -}; - - -/* A class to use a lookup table for a transformation */ -class lookup_distortion -{ -public: - lookup_distortion(const double *mesh, int in_width, int in_height, - int out_width, int out_height) : - m_mesh(mesh), - m_in_width(in_width), - m_in_height(in_height), - m_out_width(out_width), - m_out_height(out_height) - {} - - void calculate(int* x, int* y) { - if (m_mesh) { - double dx = double(*x) / agg::image_subpixel_scale; - double dy = double(*y) / agg::image_subpixel_scale; - if (dx >= 0 && dx < m_out_width && - dy >= 0 && dy < m_out_height) { - const double *coord = m_mesh + (int(dy) * m_out_width + int(dx)) * 2; - *x = int(coord[0] * agg::image_subpixel_scale); - *y = int(coord[1] * agg::image_subpixel_scale); - } - } - } - -protected: - const double *m_mesh; - int m_in_width; - int m_in_height; - int m_out_width; - int m_out_height; -}; - - -struct resample_params_t { - interpolation_e interpolation; - bool is_affine; - agg::trans_affine affine; - const double *transform_mesh; - bool resample; - bool norm; - double radius; - double alpha; -}; - - -static void get_filter(const resample_params_t ¶ms, - agg::image_filter_lut &filter) -{ - switch (params.interpolation) { - case NEAREST: - case _n_interpolation: - // Never should get here. Here to silence compiler warnings. - break; - - case HANNING: - filter.calculate(agg::image_filter_hanning(), params.norm); - break; - - case HAMMING: - filter.calculate(agg::image_filter_hamming(), params.norm); - break; - - case HERMITE: - filter.calculate(agg::image_filter_hermite(), params.norm); - break; - - case BILINEAR: - filter.calculate(agg::image_filter_bilinear(), params.norm); - break; - - case BICUBIC: - filter.calculate(agg::image_filter_bicubic(), params.norm); - break; - - case SPLINE16: - filter.calculate(agg::image_filter_spline16(), params.norm); - break; - - case SPLINE36: - filter.calculate(agg::image_filter_spline36(), params.norm); - break; - - case KAISER: - filter.calculate(agg::image_filter_kaiser(), params.norm); - break; - - case QUADRIC: - filter.calculate(agg::image_filter_quadric(), params.norm); - break; - - case CATROM: - filter.calculate(agg::image_filter_catrom(), params.norm); - break; - - case GAUSSIAN: - filter.calculate(agg::image_filter_gaussian(), params.norm); - break; - - case BESSEL: - filter.calculate(agg::image_filter_bessel(), params.norm); - break; - - case MITCHELL: - filter.calculate(agg::image_filter_mitchell(), params.norm); - break; - - case SINC: - filter.calculate(agg::image_filter_sinc(params.radius), params.norm); - break; - - case LANCZOS: - filter.calculate(agg::image_filter_lanczos(params.radius), params.norm); - break; - - case BLACKMAN: - filter.calculate(agg::image_filter_blackman(params.radius), params.norm); - break; - } -} - - -template<typename color_type> -void resample( - const void *input, int in_width, int in_height, - void *output, int out_width, int out_height, - resample_params_t ¶ms) -{ - using type_mapping_t = type_mapping<color_type>; - - using input_pixfmt_t = typename type_mapping_t::pixfmt_type; - using output_pixfmt_t = typename type_mapping_t::pixfmt_type; - - using renderer_t = agg::renderer_base<output_pixfmt_t>; - using rasterizer_t = agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl>; - - using reflect_t = agg::wrap_mode_reflect; - using image_accessor_t = agg::image_accessor_wrap<input_pixfmt_t, reflect_t, reflect_t>; - - using span_alloc_t = agg::span_allocator<color_type>; - using span_conv_alpha_t = span_conv_alpha<color_type>; - - using affine_interpolator_t = agg::span_interpolator_linear<>; - using arbitrary_interpolator_t = - agg::span_interpolator_adaptor<agg::span_interpolator_linear<>, lookup_distortion>; - - size_t itemsize = sizeof(color_type); - if (is_grayscale<color_type>::value) { - itemsize /= 2; // agg::grayXX includes an alpha channel which we don't have. - } - - if (params.interpolation != NEAREST && - params.is_affine && - fabs(params.affine.sx) == 1.0 && - fabs(params.affine.sy) == 1.0 && - params.affine.shx == 0.0 && - params.affine.shy == 0.0) { - params.interpolation = NEAREST; - } - - span_alloc_t span_alloc; - rasterizer_t rasterizer; - agg::scanline_u8 scanline; - - span_conv_alpha_t conv_alpha(params.alpha); - - agg::rendering_buffer input_buffer; - input_buffer.attach( - (unsigned char *)input, in_width, in_height, in_width * itemsize); - input_pixfmt_t input_pixfmt(input_buffer); - image_accessor_t input_accessor(input_pixfmt); - - agg::rendering_buffer output_buffer; - output_buffer.attach( - (unsigned char *)output, out_width, out_height, out_width * itemsize); - output_pixfmt_t output_pixfmt(output_buffer); - renderer_t renderer(output_pixfmt); - - agg::trans_affine inverted = params.affine; - inverted.invert(); - - rasterizer.clip_box(0, 0, out_width, out_height); - - agg::path_storage path; - if (params.is_affine) { - path.move_to(0, 0); - path.line_to(in_width, 0); - path.line_to(in_width, in_height); - path.line_to(0, in_height); - path.close_polygon(); - agg::conv_transform<agg::path_storage> rectangle(path, params.affine); - rasterizer.add_path(rectangle); - } else { - path.move_to(0, 0); - path.line_to(out_width, 0); - path.line_to(out_width, out_height); - path.line_to(0, out_height); - path.close_polygon(); - rasterizer.add_path(path); - } - - if (params.interpolation == NEAREST) { - if (params.is_affine) { - using span_gen_t = typename type_mapping_t::template span_gen_nn_type<image_accessor_t, affine_interpolator_t>; - using span_conv_t = agg::span_converter<span_gen_t, span_conv_alpha_t>; - using nn_renderer_t = agg::renderer_scanline_aa<renderer_t, span_alloc_t, span_conv_t>; - affine_interpolator_t interpolator(inverted); - span_gen_t span_gen(input_accessor, interpolator); - span_conv_t span_conv(span_gen, conv_alpha); - nn_renderer_t nn_renderer(renderer, span_alloc, span_conv); - agg::render_scanlines(rasterizer, scanline, nn_renderer); - } else { - using span_gen_t = typename type_mapping_t::template span_gen_nn_type<image_accessor_t, arbitrary_interpolator_t>; - using span_conv_t = agg::span_converter<span_gen_t, span_conv_alpha_t>; - using nn_renderer_t = agg::renderer_scanline_aa<renderer_t, span_alloc_t, span_conv_t>; - lookup_distortion dist( - params.transform_mesh, in_width, in_height, out_width, out_height); - arbitrary_interpolator_t interpolator(inverted, dist); - span_gen_t span_gen(input_accessor, interpolator); - span_conv_t span_conv(span_gen, conv_alpha); - nn_renderer_t nn_renderer(renderer, span_alloc, span_conv); - agg::render_scanlines(rasterizer, scanline, nn_renderer); - } - } else { - agg::image_filter_lut filter; - get_filter(params, filter); - - if (params.is_affine && params.resample) { - using span_gen_t = typename type_mapping_t::template span_gen_affine_type<image_accessor_t>; - using span_conv_t = agg::span_converter<span_gen_t, span_conv_alpha_t>; - using int_renderer_t = agg::renderer_scanline_aa<renderer_t, span_alloc_t, span_conv_t>; - affine_interpolator_t interpolator(inverted); - span_gen_t span_gen(input_accessor, interpolator, filter); - span_conv_t span_conv(span_gen, conv_alpha); - int_renderer_t int_renderer(renderer, span_alloc, span_conv); - agg::render_scanlines(rasterizer, scanline, int_renderer); - } else { - using span_gen_t = typename type_mapping_t::template span_gen_filter_type<image_accessor_t, arbitrary_interpolator_t>; - using span_conv_t = agg::span_converter<span_gen_t, span_conv_alpha_t>; - using int_renderer_t = agg::renderer_scanline_aa<renderer_t, span_alloc_t, span_conv_t>; - lookup_distortion dist( - params.transform_mesh, in_width, in_height, out_width, out_height); - arbitrary_interpolator_t interpolator(inverted, dist); - span_gen_t span_gen(input_accessor, interpolator, filter); - span_conv_t span_conv(span_gen, conv_alpha); - int_renderer_t int_renderer(renderer, span_alloc, span_conv); - agg::render_scanlines(rasterizer, scanline, int_renderer); - } - } -} - -#endif /* MPL_RESAMPLE_H */ diff --git a/contrib/python/matplotlib/py3/src/_image_wrapper.cpp b/contrib/python/matplotlib/py3/src/_image_wrapper.cpp deleted file mode 100644 index ca6ae8b222..0000000000 --- a/contrib/python/matplotlib/py3/src/_image_wrapper.cpp +++ /dev/null @@ -1,297 +0,0 @@ -#include "mplutils.h" -#include "_image_resample.h" -#include "numpy_cpp.h" -#include "py_converters.h" - - -/********************************************************************** - * Free functions - * */ - -const char* image_resample__doc__ = -"resample(input_array, output_array, transform, interpolation=NEAREST, resample=False, alpha=1.0, norm=False, radius=1.0)\n" -"--\n\n" - -"Resample input_array, blending it in-place into output_array, using an\n" -"affine transformation.\n\n" - -"Parameters\n" -"----------\n" -"input_array : 2-d or 3-d NumPy array of float, double or `numpy.uint8`\n" -" If 2-d, the image is grayscale. If 3-d, the image must be of size\n" -" 4 in the last dimension and represents RGBA data.\n\n" - -"output_array : 2-d or 3-d NumPy array of float, double or `numpy.uint8`\n" -" The dtype and number of dimensions must match `input_array`.\n\n" - -"transform : matplotlib.transforms.Transform instance\n" -" The transformation from the input array to the output array.\n\n" - -"interpolation : int, default: NEAREST\n" -" The interpolation method. Must be one of the following constants\n" -" defined in this module:\n\n" - -" NEAREST, BILINEAR, BICUBIC, SPLINE16, SPLINE36,\n" -" HANNING, HAMMING, HERMITE, KAISER, QUADRIC, CATROM, GAUSSIAN,\n" -" BESSEL, MITCHELL, SINC, LANCZOS, BLACKMAN\n\n" - -"resample : bool, optional\n" -" When `True`, use a full resampling method. When `False`, only\n" -" resample when the output image is larger than the input image.\n\n" - -"alpha : float, default: 1\n" -" The transparency level, from 0 (transparent) to 1 (opaque).\n\n" - -"norm : bool, default: False\n" -" Whether to norm the interpolation function.\n\n" - -"radius: float, default: 1\n" -" The radius of the kernel, if method is SINC, LANCZOS or BLACKMAN.\n"; - - -static PyArrayObject * -_get_transform_mesh(PyObject *py_affine, npy_intp *dims) -{ - /* TODO: Could we get away with float, rather than double, arrays here? */ - - /* Given a non-affine transform object, create a mesh that maps - every pixel in the output image to the input image. This is used - as a lookup table during the actual resampling. */ - - PyObject *py_inverse = NULL; - npy_intp out_dims[3]; - - out_dims[0] = dims[0] * dims[1]; - out_dims[1] = 2; - - py_inverse = PyObject_CallMethod(py_affine, "inverted", NULL); - if (py_inverse == NULL) { - return NULL; - } - - numpy::array_view<double, 2> input_mesh(out_dims); - double *p = (double *)input_mesh.data(); - - for (npy_intp y = 0; y < dims[0]; ++y) { - for (npy_intp x = 0; x < dims[1]; ++x) { - *p++ = (double)x; - *p++ = (double)y; - } - } - - PyObject *output_mesh = PyObject_CallMethod( - py_inverse, "transform", "O", input_mesh.pyobj_steal()); - - Py_DECREF(py_inverse); - - if (output_mesh == NULL) { - return NULL; - } - - PyArrayObject *output_mesh_array = - (PyArrayObject *)PyArray_ContiguousFromAny( - output_mesh, NPY_DOUBLE, 2, 2); - - Py_DECREF(output_mesh); - - if (output_mesh_array == NULL) { - return NULL; - } - - return output_mesh_array; -} - - -static PyObject * -image_resample(PyObject *self, PyObject* args, PyObject *kwargs) -{ - PyObject *py_input = NULL; - PyObject *py_output = NULL; - PyObject *py_transform = NULL; - resample_params_t params; - - PyArrayObject *input = NULL; - PyArrayObject *output = NULL; - PyArrayObject *transform_mesh = NULL; - int ndim; - int type; - - params.interpolation = NEAREST; - params.transform_mesh = NULL; - params.resample = false; - params.norm = false; - params.radius = 1.0; - params.alpha = 1.0; - - const char *kwlist[] = { - "input_array", "output_array", "transform", "interpolation", - "resample", "alpha", "norm", "radius", NULL }; - - if (!PyArg_ParseTupleAndKeywords( - args, kwargs, "OOO|iO&dO&d:resample", (char **)kwlist, - &py_input, &py_output, &py_transform, - ¶ms.interpolation, &convert_bool, ¶ms.resample, - ¶ms.alpha, &convert_bool, ¶ms.norm, ¶ms.radius)) { - return NULL; - } - - if (params.interpolation < 0 || params.interpolation >= _n_interpolation) { - PyErr_Format(PyExc_ValueError, "Invalid interpolation value %d", - params.interpolation); - goto error; - } - - input = (PyArrayObject *)PyArray_FromAny( - py_input, NULL, 2, 3, NPY_ARRAY_C_CONTIGUOUS, NULL); - if (!input) { - goto error; - } - ndim = PyArray_NDIM(input); - type = PyArray_TYPE(input); - - if (!PyArray_Check(py_output)) { - PyErr_SetString(PyExc_ValueError, "Output array must be a NumPy array"); - goto error; - } - output = (PyArrayObject *)py_output; - if (PyArray_NDIM(output) != ndim) { - PyErr_Format( - PyExc_ValueError, - "Input (%dD) and output (%dD) have different dimensionalities.", - ndim, PyArray_NDIM(output)); - goto error; - } - // PyArray_FromAny above checks that input is 2D or 3D. - if (ndim == 3 && (PyArray_DIM(input, 2) != 4 || PyArray_DIM(output, 2) != 4)) { - PyErr_Format( - PyExc_ValueError, - "If 3D, input and output arrays must be RGBA with shape (M, N, 4); " - "got trailing dimensions of %" NPY_INTP_FMT " and %" NPY_INTP_FMT - " respectively", PyArray_DIM(input, 2), PyArray_DIM(output, 2)); - goto error; - } - if (PyArray_TYPE(output) != type) { - PyErr_SetString(PyExc_ValueError, "Mismatched types"); - goto error; - } - if (!PyArray_IS_C_CONTIGUOUS(output)) { - PyErr_SetString(PyExc_ValueError, "Output array must be C-contiguous"); - goto error; - } - - if (py_transform == NULL || py_transform == Py_None) { - params.is_affine = true; - } else { - PyObject *py_is_affine; - int py_is_affine2; - py_is_affine = PyObject_GetAttrString(py_transform, "is_affine"); - if (!py_is_affine) { - goto error; - } - - py_is_affine2 = PyObject_IsTrue(py_is_affine); - Py_DECREF(py_is_affine); - - if (py_is_affine2 == -1) { - goto error; - } else if (py_is_affine2) { - if (!convert_trans_affine(py_transform, ¶ms.affine)) { - goto error; - } - params.is_affine = true; - } else { - transform_mesh = _get_transform_mesh( - py_transform, PyArray_DIMS(output)); - if (!transform_mesh) { - goto error; - } - params.transform_mesh = (double *)PyArray_DATA(transform_mesh); - params.is_affine = false; - } - } - - if (auto resampler = - (ndim == 2) ? ( - (type == NPY_UINT8) ? resample<agg::gray8> : - (type == NPY_INT8) ? resample<agg::gray8> : - (type == NPY_UINT16) ? resample<agg::gray16> : - (type == NPY_INT16) ? resample<agg::gray16> : - (type == NPY_FLOAT32) ? resample<agg::gray32> : - (type == NPY_FLOAT64) ? resample<agg::gray64> : - nullptr) : ( - // ndim == 3 - (type == NPY_UINT8) ? resample<agg::rgba8> : - (type == NPY_INT8) ? resample<agg::rgba8> : - (type == NPY_UINT16) ? resample<agg::rgba16> : - (type == NPY_INT16) ? resample<agg::rgba16> : - (type == NPY_FLOAT32) ? resample<agg::rgba32> : - (type == NPY_FLOAT64) ? resample<agg::rgba64> : - nullptr)) { - Py_BEGIN_ALLOW_THREADS - resampler( - PyArray_DATA(input), PyArray_DIM(input, 1), PyArray_DIM(input, 0), - PyArray_DATA(output), PyArray_DIM(output, 1), PyArray_DIM(output, 0), - params); - Py_END_ALLOW_THREADS - } else { - PyErr_SetString( - PyExc_ValueError, - "arrays must be of dtype byte, short, float32 or float64"); - goto error; - } - - Py_DECREF(input); - Py_XDECREF(transform_mesh); - Py_RETURN_NONE; - - error: - Py_XDECREF(input); - Py_XDECREF(transform_mesh); - return NULL; -} - -static PyMethodDef module_functions[] = { - {"resample", (PyCFunction)image_resample, METH_VARARGS|METH_KEYWORDS, image_resample__doc__}, - {NULL} -}; - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, "_image", NULL, 0, module_functions, -}; - -PyMODINIT_FUNC PyInit__image(void) -{ - PyObject *m; - - import_array(); - - m = PyModule_Create(&moduledef); - - if (m == NULL) { - return NULL; - } - - if (PyModule_AddIntConstant(m, "NEAREST", NEAREST) || - PyModule_AddIntConstant(m, "BILINEAR", BILINEAR) || - PyModule_AddIntConstant(m, "BICUBIC", BICUBIC) || - PyModule_AddIntConstant(m, "SPLINE16", SPLINE16) || - PyModule_AddIntConstant(m, "SPLINE36", SPLINE36) || - PyModule_AddIntConstant(m, "HANNING", HANNING) || - PyModule_AddIntConstant(m, "HAMMING", HAMMING) || - PyModule_AddIntConstant(m, "HERMITE", HERMITE) || - PyModule_AddIntConstant(m, "KAISER", KAISER) || - PyModule_AddIntConstant(m, "QUADRIC", QUADRIC) || - PyModule_AddIntConstant(m, "CATROM", CATROM) || - PyModule_AddIntConstant(m, "GAUSSIAN", GAUSSIAN) || - PyModule_AddIntConstant(m, "BESSEL", BESSEL) || - PyModule_AddIntConstant(m, "MITCHELL", MITCHELL) || - PyModule_AddIntConstant(m, "SINC", SINC) || - PyModule_AddIntConstant(m, "LANCZOS", LANCZOS) || - PyModule_AddIntConstant(m, "BLACKMAN", BLACKMAN) || - PyModule_AddIntConstant(m, "_n_interpolation", _n_interpolation)) { - Py_DECREF(m); - return NULL; - } - - return m; -} diff --git a/contrib/python/matplotlib/py3/src/_path.h b/contrib/python/matplotlib/py3/src/_path.h deleted file mode 100644 index 61c4ed07d0..0000000000 --- a/contrib/python/matplotlib/py3/src/_path.h +++ /dev/null @@ -1,1251 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_PATH_H -#define MPL_PATH_H - -#include <limits> -#include <math.h> -#include <vector> -#include <cmath> -#include <algorithm> -#include <string> - -#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_codes()); - 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 result[0] != 0; -} - -template <class PathIterator> -inline bool point_on_path( - double x, double y, const double r, PathIterator &path, agg::trans_affine &trans) -{ - 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; - - 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; - - transformed_path_t trans_path(path, trans); - no_nans_t nan_removed_path(trans_path, true, path.has_codes()); - 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); - return result[0] != 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_codes()); - - 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 have shape (N, 2)"); - } - - 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, - 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); - 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_codes()); - 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: - - https://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 isclose(double a, double b) -{ - // relative and absolute tolerance values are chosen empirically - // it looks the atol value matters here because of round-off errors - const double rtol = 1e-10; - const double atol = 1e-13; - - // as per python's math.isclose - return fabs(a-b) <= fmax(rtol * fmax(fabs(a), fabs(b)), atol); -} - - -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) -{ - // determinant - double den = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1)); - - // If den == 0 we have two possibilities: - if (isclose(den, 0.0)) { - double t_area = (x2*y3 - x3*y2) - x1*(y3 - y2) + y1*(x3 - x2); - // 1 - If the area of the triangle made by the 3 first points (2 from the first segment - // plus one from the second) is zero, they are collinear - if (isclose(t_area, 0.0)) { - if (x1 == x2 && x2 == x3) { // segments have infinite slope (vertical lines) - // and lie on the same line - return (fmin(y1, y2) <= fmin(y3, y4) && fmin(y3, y4) <= fmax(y1, y2)) || - (fmin(y3, y4) <= fmin(y1, y2) && fmin(y1, y2) <= fmax(y3, y4)); - } - else { - return (fmin(x1, x2) <= fmin(x3, x4) && fmin(x3, x4) <= fmax(x1, x2)) || - (fmin(x3, x4) <= fmin(x1, x2) && fmin(x1, x2) <= fmax(x3, x4)); - } - } - // 2 - If t_area is not zero, the segments are parallel, but not collinear - else { - return false; - } - } - - const double n1 = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3)); - const double n2 = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3)); - - const double u1 = n1 / den; - const double u2 = n2 / den; - - return ((u1 > 0.0 || isclose(u1, 0.0)) && - (u1 < 1.0 || isclose(u1, 1.0)) && - (u2 > 0.0 || isclose(u2, 0.0)) && - (u2 < 1.0 || isclose(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_codes()); - no_nans_t n2(p2, true, p2.has_codes()); - - 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) { - // if the segment in path 1 is (almost) 0 length, skip to next vertex - if ((isclose((x11 - x12) * (x11 - x12) + (y11 - y12) * (y11 - y12), 0))){ - continue; - } - c2.rewind(0); - c2.vertex(&x21, &y21); - - while (c2.vertex(&x22, &y22) != agg::path_cmd_stop) { - // if the segment in path 2 is (almost) 0 length, skip to next vertex - if ((isclose((x21 - x22) * (x21 - x22) + (y21 - y22) * (y21 - y22), 0))){ - continue; - } - - 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_codes()); - 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_codes()); - clipped_t clipped(nan_removed, do_clip, 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_codes()); - clipped_t clipped(nan_removed, do_clip, 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; -} - - -void __add_number(double val, char format_code, int precision, - std::string& buffer) -{ - if (precision == -1) { - // Special-case for compat with old ttconv code, which *truncated* - // values with a cast to int instead of rounding them as printf - // would do. The only point where non-integer values arise is from - // quad2cubic conversion (as we already perform a first truncation - // on Python's side), which can introduce additional floating point - // error (by adding 2/3 delta-x and then 1/3 delta-x), so compensate by - // first rounding to the closest 1/3 and then truncating. - char str[255]; - PyOS_snprintf(str, 255, "%d", (int)(round(val * 3)) / 3); - buffer += str; - } else { - char *str = PyOS_double_to_string( - val, format_code, precision, Py_DTSF_ADD_DOT_0, NULL); - // Delete trailing zeros and decimal point - char *c = str + strlen(str) - 1; // Start at last character. - // Rewind through all the zeros and, if present, the trailing decimal - // point. Py_DTSF_ADD_DOT_0 ensures we won't go past the start of str. - while (*c == '0') { - --c; - } - if (*c == '.') { - --c; - } - try { - buffer.append(str, c + 1); - } catch (std::bad_alloc& e) { - PyMem_Free(str); - throw e; - } - PyMem_Free(str); - } -} - - -template <class PathIterator> -bool __convert_to_string(PathIterator &path, - int precision, - char **codes, - bool postfix, - std::string& buffer) -{ - const char format_code = 'f'; - - double x[3]; - double y[3]; - double last_x = 0.0; - double last_y = 0.0; - - unsigned code; - - while ((code = path.vertex(&x[0], &y[0])) != agg::path_cmd_stop) { - if (code == CLOSEPOLY) { - buffer += codes[4]; - } else if (code < 5) { - size_t size = NUM_VERTICES[code]; - - for (size_t i = 1; i < size; ++i) { - unsigned subcode = path.vertex(&x[i], &y[i]); - if (subcode != code) { - return false; - } - } - - /* 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) { - buffer += codes[code - 1]; - buffer += ' '; - } - - for (size_t i = 0; i < size; ++i) { - __add_number(x[i], format_code, precision, buffer); - buffer += ' '; - __add_number(y[i], format_code, precision, buffer); - buffer += ' '; - } - - if (postfix) { - buffer += codes[code - 1]; - } - - last_x = x[size - 1]; - last_y = y[size - 1]; - } else { - // Unknown code value - return false; - } - - buffer += '\n'; - } - - return true; -} - -template <class PathIterator> -bool 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, - std::string& 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_codes()); - clipped_t clipped(nan_removed, do_clip, clip_rect); - simplify_t simplified(clipped, simplify, path.simplify_threshold()); - - buffersize = (size_t) path.total_vertices() * (precision + 5) * 4; - if (buffersize == 0) { - return true; - } - - if (sketch_params.scale != 0.0) { - buffersize *= 10; - } - - buffer.reserve(buffersize); - - if (sketch_params.scale == 0.0) { - return __convert_to_string(simplified, precision, codes, postfix, buffer); - } 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); - } - -} - -template<class T> -bool is_sorted_and_has_non_nan(PyArrayObject *array) -{ - char* ptr = PyArray_BYTES(array); - npy_intp size = PyArray_DIM(array, 0), - stride = PyArray_STRIDE(array, 0); - using limits = std::numeric_limits<T>; - T last = limits::has_infinity ? -limits::infinity() : limits::min(); - bool found_non_nan = false; - - for (npy_intp i = 0; i < size; ++i, ptr += stride) { - T current = *(T*)ptr; - // The following tests !isnan(current), but also works for integral - // types. (The isnan(IntegralType) overload is absent on MSVC.) - if (current == current) { - found_non_nan = true; - if (current < last) { - return false; - } - last = current; - } - } - return found_non_nan; -}; - - -#endif diff --git a/contrib/python/matplotlib/py3/src/_path_wrapper.cpp b/contrib/python/matplotlib/py3/src/_path_wrapper.cpp deleted file mode 100644 index 369d9e0308..0000000000 --- a/contrib/python/matplotlib/py3/src/_path_wrapper.cpp +++ /dev/null @@ -1,772 +0,0 @@ -#include "numpy_cpp.h" - -#include "_path.h" - -#include "py_converters.h" -#include "py_adaptors.h" - -PyObject *convert_polygon_vector(std::vector<Polygon> &polygons) -{ - PyObject *pyresult = PyList_New(polygons.size()); - - for (size_t i = 0; i < polygons.size(); ++i) { - Polygon poly = polygons[i]; - npy_intp dims[2]; - dims[1] = 2; - - dims[0] = (npy_intp)poly.size(); - - numpy::array_view<double, 2> subresult(dims); - memcpy(subresult.data(), &poly[0], sizeof(double) * poly.size() * 2); - - if (PyList_SetItem(pyresult, i, subresult.pyobj())) { - Py_DECREF(pyresult); - return NULL; - } - } - - return pyresult; -} - -const char *Py_point_in_path__doc__ = - "point_in_path(x, y, radius, path, trans)\n" - "--\n\n"; - -static PyObject *Py_point_in_path(PyObject *self, PyObject *args) -{ - double x, y, r; - py::PathIterator path; - agg::trans_affine trans; - bool result; - - if (!PyArg_ParseTuple(args, - "dddO&O&:point_in_path", - &x, - &y, - &r, - &convert_path, - &path, - &convert_trans_affine, - &trans)) { - return NULL; - } - - CALL_CPP("point_in_path", (result = point_in_path(x, y, r, path, trans))); - - if (result) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -const char *Py_points_in_path__doc__ = - "points_in_path(points, radius, path, trans)\n" - "--\n\n"; - -static PyObject *Py_points_in_path(PyObject *self, PyObject *args) -{ - numpy::array_view<const double, 2> points; - double r; - py::PathIterator path; - agg::trans_affine trans; - - if (!PyArg_ParseTuple(args, - "O&dO&O&:points_in_path", - &convert_points, - &points, - &r, - &convert_path, - &path, - &convert_trans_affine, - &trans)) { - return NULL; - } - - npy_intp dims[] = { (npy_intp)points.size() }; - numpy::array_view<uint8_t, 1> results(dims); - - CALL_CPP("points_in_path", (points_in_path(points, r, path, trans, results))); - - return results.pyobj(); -} - -const char *Py_update_path_extents__doc__ = - "update_path_extents(path, trans, rect, minpos, ignore)\n" - "--\n\n"; - -static PyObject *Py_update_path_extents(PyObject *self, PyObject *args) -{ - py::PathIterator path; - agg::trans_affine trans; - agg::rect_d rect; - numpy::array_view<double, 1> minpos; - int ignore; - int changed; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&i:update_path_extents", - &convert_path, - &path, - &convert_trans_affine, - &trans, - &convert_rect, - &rect, - &minpos.converter, - &minpos, - &ignore)) { - return NULL; - } - - if (minpos.dim(0) != 2) { - PyErr_Format(PyExc_ValueError, - "minpos must be of length 2, got %" NPY_INTP_FMT, - minpos.dim(0)); - return NULL; - } - - extent_limits e; - - if (ignore) { - CALL_CPP("update_path_extents", reset_limits(e)); - } else { - if (rect.x1 > rect.x2) { - e.x0 = std::numeric_limits<double>::infinity(); - e.x1 = -std::numeric_limits<double>::infinity(); - } else { - e.x0 = rect.x1; - e.x1 = rect.x2; - } - if (rect.y1 > rect.y2) { - e.y0 = std::numeric_limits<double>::infinity(); - e.y1 = -std::numeric_limits<double>::infinity(); - } else { - e.y0 = rect.y1; - e.y1 = rect.y2; - } - e.xm = minpos(0); - e.ym = minpos(1); - } - - CALL_CPP("update_path_extents", (update_path_extents(path, trans, e))); - - changed = (e.x0 != rect.x1 || e.y0 != rect.y1 || e.x1 != rect.x2 || e.y1 != rect.y2 || - e.xm != minpos(0) || e.ym != minpos(1)); - - npy_intp extentsdims[] = { 2, 2 }; - numpy::array_view<double, 2> outextents(extentsdims); - outextents(0, 0) = e.x0; - outextents(0, 1) = e.y0; - outextents(1, 0) = e.x1; - outextents(1, 1) = e.y1; - - npy_intp minposdims[] = { 2 }; - numpy::array_view<double, 1> outminpos(minposdims); - outminpos(0) = e.xm; - outminpos(1) = e.ym; - - return Py_BuildValue( - "NNi", outextents.pyobj(), outminpos.pyobj(), changed); -} - -const char *Py_get_path_collection_extents__doc__ = - "get_path_collection_extents(" - "master_transform, paths, transforms, offsets, offset_transform)\n" - "--\n\n"; - -static PyObject *Py_get_path_collection_extents(PyObject *self, PyObject *args) -{ - agg::trans_affine master_transform; - py::PathGenerator paths; - numpy::array_view<const double, 3> transforms; - numpy::array_view<const double, 2> offsets; - agg::trans_affine offset_trans; - extent_limits e; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&O&:get_path_collection_extents", - &convert_trans_affine, - &master_transform, - &convert_pathgen, - &paths, - &convert_transforms, - &transforms, - &convert_points, - &offsets, - &convert_trans_affine, - &offset_trans)) { - return NULL; - } - - CALL_CPP("get_path_collection_extents", - (get_path_collection_extents( - master_transform, paths, transforms, offsets, offset_trans, e))); - - npy_intp dims[] = { 2, 2 }; - numpy::array_view<double, 2> extents(dims); - extents(0, 0) = e.x0; - extents(0, 1) = e.y0; - extents(1, 0) = e.x1; - extents(1, 1) = e.y1; - - npy_intp minposdims[] = { 2 }; - numpy::array_view<double, 1> minpos(minposdims); - minpos(0) = e.xm; - minpos(1) = e.ym; - - return Py_BuildValue("NN", extents.pyobj(), minpos.pyobj()); -} - -const char *Py_point_in_path_collection__doc__ = - "point_in_path_collection(" - "x, y, radius, master_transform, paths, transforms, offsets, " - "offset_trans, filled)\n" - "--\n\n"; - -static PyObject *Py_point_in_path_collection(PyObject *self, PyObject *args) -{ - double x, y, radius; - agg::trans_affine master_transform; - py::PathGenerator paths; - numpy::array_view<const double, 3> transforms; - numpy::array_view<const double, 2> offsets; - agg::trans_affine offset_trans; - bool filled; - std::vector<int> result; - - if (!PyArg_ParseTuple(args, - "dddO&O&O&O&O&O&:point_in_path_collection", - &x, - &y, - &radius, - &convert_trans_affine, - &master_transform, - &convert_pathgen, - &paths, - &convert_transforms, - &transforms, - &convert_points, - &offsets, - &convert_trans_affine, - &offset_trans, - &convert_bool, - &filled)) { - return NULL; - } - - CALL_CPP("point_in_path_collection", - (point_in_path_collection(x, - y, - radius, - master_transform, - paths, - transforms, - offsets, - offset_trans, - filled, - result))); - - npy_intp dims[] = {(npy_intp)result.size() }; - numpy::array_view<int, 1> pyresult(dims); - if (result.size() > 0) { - memcpy(pyresult.data(), &result[0], result.size() * sizeof(int)); - } - return pyresult.pyobj(); -} - -const char *Py_path_in_path__doc__ = - "path_in_path(path_a, trans_a, path_b, trans_b)\n" - "--\n\n"; - -static PyObject *Py_path_in_path(PyObject *self, PyObject *args) -{ - py::PathIterator a; - agg::trans_affine atrans; - py::PathIterator b; - agg::trans_affine btrans; - bool result; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&:path_in_path", - &convert_path, - &a, - &convert_trans_affine, - &atrans, - &convert_path, - &b, - &convert_trans_affine, - &btrans)) { - return NULL; - } - - CALL_CPP("path_in_path", (result = path_in_path(a, atrans, b, btrans))); - - if (result) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -const char *Py_clip_path_to_rect__doc__ = - "clip_path_to_rect(path, rect, inside)\n" - "--\n\n"; - -static PyObject *Py_clip_path_to_rect(PyObject *self, PyObject *args) -{ - py::PathIterator path; - agg::rect_d rect; - bool inside; - std::vector<Polygon> result; - - if (!PyArg_ParseTuple(args, - "O&O&O&:clip_path_to_rect", - &convert_path, - &path, - &convert_rect, - &rect, - &convert_bool, - &inside)) { - return NULL; - } - - CALL_CPP("clip_path_to_rect", (clip_path_to_rect(path, rect, inside, result))); - - return convert_polygon_vector(result); -} - -const char *Py_affine_transform__doc__ = - "affine_transform(points, trans)\n" - "--\n\n"; - -static PyObject *Py_affine_transform(PyObject *self, PyObject *args) -{ - PyObject *vertices_obj; - agg::trans_affine trans; - - if (!PyArg_ParseTuple(args, - "OO&:affine_transform", - &vertices_obj, - &convert_trans_affine, - &trans)) { - return NULL; - } - - PyArrayObject* vertices_arr = (PyArrayObject *)PyArray_ContiguousFromAny(vertices_obj, NPY_DOUBLE, 1, 2); - if (vertices_arr == NULL) { - return NULL; - } - - if (PyArray_NDIM(vertices_arr) == 2) { - numpy::array_view<double, 2> vertices(vertices_arr); - Py_DECREF(vertices_arr); - - npy_intp dims[] = { (npy_intp)vertices.size(), 2 }; - numpy::array_view<double, 2> result(dims); - CALL_CPP("affine_transform", (affine_transform_2d(vertices, trans, result))); - return result.pyobj(); - } else { // PyArray_NDIM(vertices_arr) == 1 - numpy::array_view<double, 1> vertices(vertices_arr); - Py_DECREF(vertices_arr); - - npy_intp dims[] = { (npy_intp)vertices.size() }; - numpy::array_view<double, 1> result(dims); - CALL_CPP("affine_transform", (affine_transform_1d(vertices, trans, result))); - return result.pyobj(); - } -} - -const char *Py_count_bboxes_overlapping_bbox__doc__ = - "count_bboxes_overlapping_bbox(bbox, bboxes)\n" - "--\n\n"; - -static PyObject *Py_count_bboxes_overlapping_bbox(PyObject *self, PyObject *args) -{ - agg::rect_d bbox; - numpy::array_view<const double, 3> bboxes; - int result; - - if (!PyArg_ParseTuple(args, - "O&O&:count_bboxes_overlapping_bbox", - &convert_rect, - &bbox, - &convert_bboxes, - &bboxes)) { - return NULL; - } - - CALL_CPP("count_bboxes_overlapping_bbox", - (result = count_bboxes_overlapping_bbox(bbox, bboxes))); - - return PyLong_FromLong(result); -} - -const char *Py_path_intersects_path__doc__ = - "path_intersects_path(path1, path2, filled=False)\n" - "--\n\n"; - -static PyObject *Py_path_intersects_path(PyObject *self, PyObject *args, PyObject *kwds) -{ - py::PathIterator p1; - py::PathIterator p2; - agg::trans_affine t1; - agg::trans_affine t2; - int filled = 0; - const char *names[] = { "p1", "p2", "filled", NULL }; - bool result; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "O&O&i:path_intersects_path", - (char **)names, - &convert_path, - &p1, - &convert_path, - &p2, - &filled)) { - return NULL; - } - - CALL_CPP("path_intersects_path", (result = path_intersects_path(p1, p2))); - if (filled) { - if (!result) { - CALL_CPP("path_intersects_path", - (result = path_in_path(p1, t1, p2, t2))); - } - if (!result) { - CALL_CPP("path_intersects_path", - (result = path_in_path(p2, t1, p1, t2))); - } - } - - if (result) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -const char *Py_path_intersects_rectangle__doc__ = - "path_intersects_rectangle(" - "path, rect_x1, rect_y1, rect_x2, rect_y2, filled=False)\n" - "--\n\n"; - -static PyObject *Py_path_intersects_rectangle(PyObject *self, PyObject *args, PyObject *kwds) -{ - py::PathIterator path; - double rect_x1, rect_y1, rect_x2, rect_y2; - bool filled = false; - const char *names[] = { "path", "rect_x1", "rect_y1", "rect_x2", "rect_y2", "filled", NULL }; - bool result; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "O&dddd|O&:path_intersects_rectangle", - (char **)names, - &convert_path, - &path, - &rect_x1, - &rect_y1, - &rect_x2, - &rect_y2, - &convert_bool, - &filled)) { - return NULL; - } - - CALL_CPP("path_intersects_rectangle", (result = path_intersects_rectangle(path, rect_x1, rect_y1, rect_x2, rect_y2, filled))); - - if (result) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - -const char *Py_convert_path_to_polygons__doc__ = - "convert_path_to_polygons(path, trans, width=0, height=0)\n" - "--\n\n"; - -static PyObject *Py_convert_path_to_polygons(PyObject *self, PyObject *args, PyObject *kwds) -{ - py::PathIterator path; - agg::trans_affine trans; - double width = 0.0, height = 0.0; - int closed_only = 1; - std::vector<Polygon> result; - const char *names[] = { "path", "transform", "width", "height", "closed_only", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "O&O&|ddi:convert_path_to_polygons", - (char **)names, - &convert_path, - &path, - &convert_trans_affine, - &trans, - &width, - &height, - &closed_only)) { - return NULL; - } - - CALL_CPP("convert_path_to_polygons", - (convert_path_to_polygons(path, trans, width, height, closed_only, result))); - - return convert_polygon_vector(result); -} - -const char *Py_cleanup_path__doc__ = - "cleanup_path(" - "path, trans, remove_nans, clip_rect, snap_mode, stroke_width, simplify, " - "return_curves, sketch)\n" - "--\n\n"; - -static PyObject *Py_cleanup_path(PyObject *self, PyObject *args) -{ - py::PathIterator path; - agg::trans_affine trans; - bool remove_nans; - agg::rect_d clip_rect; - e_snap_mode snap_mode; - double stroke_width; - PyObject *simplifyobj; - bool simplify = false; - bool return_curves; - SketchParams sketch; - - if (!PyArg_ParseTuple(args, - "O&O&O&O&O&dOO&O&:cleanup_path", - &convert_path, - &path, - &convert_trans_affine, - &trans, - &convert_bool, - &remove_nans, - &convert_rect, - &clip_rect, - &convert_snap, - &snap_mode, - &stroke_width, - &simplifyobj, - &convert_bool, - &return_curves, - &convert_sketch_params, - &sketch)) { - return NULL; - } - - if (simplifyobj == Py_None) { - simplify = path.should_simplify(); - } else { - switch (PyObject_IsTrue(simplifyobj)) { - case 0: simplify = false; break; - case 1: simplify = true; break; - default: return NULL; // errored. - } - } - - bool do_clip = (clip_rect.x1 < clip_rect.x2 && clip_rect.y1 < clip_rect.y2); - - std::vector<double> vertices; - std::vector<npy_uint8> codes; - - CALL_CPP("cleanup_path", - (cleanup_path(path, - trans, - remove_nans, - do_clip, - clip_rect, - snap_mode, - stroke_width, - simplify, - return_curves, - sketch, - vertices, - codes))); - - size_t length = codes.size(); - - npy_intp vertices_dims[] = {(npy_intp)length, 2 }; - numpy::array_view<double, 2> pyvertices(vertices_dims); - - npy_intp codes_dims[] = {(npy_intp)length }; - numpy::array_view<unsigned char, 1> pycodes(codes_dims); - - memcpy(pyvertices.data(), &vertices[0], sizeof(double) * 2 * length); - memcpy(pycodes.data(), &codes[0], sizeof(unsigned char) * length); - - return Py_BuildValue("NN", pyvertices.pyobj(), pycodes.pyobj()); -} - -const char *Py_convert_to_string__doc__ = - "convert_to_string(" - "path, trans, clip_rect, simplify, sketch, precision, codes, postfix)\n" - "--\n\n" - "Convert *path* to a bytestring.\n" - "\n" - "The first five parameters (up to *sketch*) are interpreted as in\n" - "`.cleanup_path`. The following ones are detailed below.\n" - "\n" - "Parameters\n" - "----------\n" - "path : Path\n" - "trans : Transform or None\n" - "clip_rect : sequence of 4 floats, or None\n" - "simplify : bool\n" - "sketch : tuple of 3 floats, or None\n" - "precision : int\n" - " The precision used to \"%.*f\"-format the values. Trailing zeros\n" - " and decimal points are always removed. (precision=-1 is a special\n" - " case used to implement ttconv-back-compatible conversion.)\n" - "codes : sequence of 5 bytestrings\n" - " The bytes representation of each opcode (MOVETO, LINETO, CURVE3,\n" - " CURVE4, CLOSEPOLY), in that order. If the bytes for CURVE3 is\n" - " empty, quad segments are automatically converted to cubic ones\n" - " (this is used by backends such as pdf and ps, which do not support\n" - " quads).\n" - "postfix : bool\n" - " Whether the opcode comes after the values (True) or before (False).\n" - ; - -static PyObject *Py_convert_to_string(PyObject *self, PyObject *args) -{ - py::PathIterator path; - agg::trans_affine trans; - agg::rect_d cliprect; - PyObject *simplifyobj; - bool simplify = false; - SketchParams sketch; - int precision; - char *codes[5]; - bool postfix; - std::string buffer; - bool status; - - if (!PyArg_ParseTuple(args, - "O&O&O&OO&i(yyyyy)O&:convert_to_string", - &convert_path, - &path, - &convert_trans_affine, - &trans, - &convert_rect, - &cliprect, - &simplifyobj, - &convert_sketch_params, - &sketch, - &precision, - &codes[0], - &codes[1], - &codes[2], - &codes[3], - &codes[4], - &convert_bool, - &postfix)) { - return NULL; - } - - if (simplifyobj == Py_None) { - simplify = path.should_simplify(); - } else { - switch (PyObject_IsTrue(simplifyobj)) { - case 0: simplify = false; break; - case 1: simplify = true; break; - default: return NULL; // errored. - } - } - - CALL_CPP("convert_to_string", - (status = convert_to_string( - path, trans, cliprect, simplify, sketch, - precision, codes, postfix, buffer))); - - if (!status) { - PyErr_SetString(PyExc_ValueError, "Malformed path codes"); - return NULL; - } - - return PyBytes_FromStringAndSize(buffer.c_str(), buffer.size()); -} - - -const char *Py_is_sorted_and_has_non_nan__doc__ = - "is_sorted_and_has_non_nan(array, /)\n" - "--\n\n" - "Return whether the 1D *array* is monotonically increasing, ignoring NaNs,\n" - "and has at least one non-nan value."; - -static PyObject *Py_is_sorted_and_has_non_nan(PyObject *self, PyObject *obj) -{ - bool result; - - PyArrayObject *array = (PyArrayObject *)PyArray_FromAny( - obj, NULL, 1, 1, 0, NULL); - - if (array == NULL) { - return NULL; - } - - /* Handle just the most common types here, otherwise coerce to double */ - switch (PyArray_TYPE(array)) { - case NPY_INT: - result = is_sorted_and_has_non_nan<npy_int>(array); - break; - case NPY_LONG: - result = is_sorted_and_has_non_nan<npy_long>(array); - break; - case NPY_LONGLONG: - result = is_sorted_and_has_non_nan<npy_longlong>(array); - break; - case NPY_FLOAT: - result = is_sorted_and_has_non_nan<npy_float>(array); - break; - case NPY_DOUBLE: - result = is_sorted_and_has_non_nan<npy_double>(array); - break; - default: - Py_DECREF(array); - array = (PyArrayObject *)PyArray_FromObject(obj, NPY_DOUBLE, 1, 1); - if (array == NULL) { - return NULL; - } - result = is_sorted_and_has_non_nan<npy_double>(array); - } - - Py_DECREF(array); - - if (result) { - Py_RETURN_TRUE; - } else { - Py_RETURN_FALSE; - } -} - - -static PyMethodDef module_functions[] = { - {"point_in_path", (PyCFunction)Py_point_in_path, METH_VARARGS, Py_point_in_path__doc__}, - {"points_in_path", (PyCFunction)Py_points_in_path, METH_VARARGS, Py_points_in_path__doc__}, - {"update_path_extents", (PyCFunction)Py_update_path_extents, METH_VARARGS, Py_update_path_extents__doc__}, - {"get_path_collection_extents", (PyCFunction)Py_get_path_collection_extents, METH_VARARGS, Py_get_path_collection_extents__doc__}, - {"point_in_path_collection", (PyCFunction)Py_point_in_path_collection, METH_VARARGS, Py_point_in_path_collection__doc__}, - {"path_in_path", (PyCFunction)Py_path_in_path, METH_VARARGS, Py_path_in_path__doc__}, - {"clip_path_to_rect", (PyCFunction)Py_clip_path_to_rect, METH_VARARGS, Py_clip_path_to_rect__doc__}, - {"affine_transform", (PyCFunction)Py_affine_transform, METH_VARARGS, Py_affine_transform__doc__}, - {"count_bboxes_overlapping_bbox", (PyCFunction)Py_count_bboxes_overlapping_bbox, METH_VARARGS, Py_count_bboxes_overlapping_bbox__doc__}, - {"path_intersects_path", (PyCFunction)Py_path_intersects_path, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_path__doc__}, - {"path_intersects_rectangle", (PyCFunction)Py_path_intersects_rectangle, METH_VARARGS|METH_KEYWORDS, Py_path_intersects_rectangle__doc__}, - {"convert_path_to_polygons", (PyCFunction)Py_convert_path_to_polygons, METH_VARARGS|METH_KEYWORDS, Py_convert_path_to_polygons__doc__}, - {"cleanup_path", (PyCFunction)Py_cleanup_path, METH_VARARGS, Py_cleanup_path__doc__}, - {"convert_to_string", (PyCFunction)Py_convert_to_string, METH_VARARGS, Py_convert_to_string__doc__}, - {"is_sorted_and_has_non_nan", (PyCFunction)Py_is_sorted_and_has_non_nan, METH_O, Py_is_sorted_and_has_non_nan__doc__}, - {NULL} -}; - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, "_path", NULL, 0, module_functions -}; - -PyMODINIT_FUNC PyInit__path(void) -{ - import_array(); - return PyModule_Create(&moduledef); -} diff --git a/contrib/python/matplotlib/py3/src/_qhull_wrapper.cpp b/contrib/python/matplotlib/py3/src/_qhull_wrapper.cpp deleted file mode 100644 index 7e4f306305..0000000000 --- a/contrib/python/matplotlib/py3/src/_qhull_wrapper.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Wrapper module for libqhull, providing Delaunay triangulation. - * - * This module's methods should not be accessed directly. To obtain a Delaunay - * triangulation, construct an instance of the matplotlib.tri.Triangulation - * class without specifying a triangles array. - */ -#define PY_SSIZE_T_CLEAN -#include "Python.h" -#include "numpy_cpp.h" -#ifdef _MSC_VER -/* The Qhull header does not declare this as extern "C", but only MSVC seems to - * do name mangling on global variables. We thus need to declare this before - * the header so that it treats it correctly, and doesn't mangle the name. */ -extern "C" { -extern const char qh_version[]; -} -#endif -#include "libqhull_r/qhull_ra.h" -#include <cstdio> -#include <vector> - - -#ifndef MPL_DEVNULL -#error "MPL_DEVNULL must be defined as the OS-equivalent of /dev/null" -#endif - -#define STRINGIFY(x) STR(x) -#define STR(x) #x - - -static const char* qhull_error_msg[6] = { - "", /* 0 = qh_ERRnone */ - "input inconsistency", /* 1 = qh_ERRinput */ - "singular input data", /* 2 = qh_ERRsingular */ - "precision error", /* 3 = qh_ERRprec */ - "insufficient memory", /* 4 = qh_ERRmem */ - "internal error"}; /* 5 = qh_ERRqhull */ - - -/* Return the indices of the 3 vertices that comprise the specified facet (i.e. - * triangle). */ -static void -get_facet_vertices(qhT* qh, const facetT* facet, int indices[3]) -{ - vertexT *vertex, **vertexp; - FOREACHvertex_(facet->vertices) { - *indices++ = qh_pointid(qh, vertex->point); - } -} - -/* Return the indices of the 3 triangles that are neighbors of the specified - * facet (triangle). */ -static void -get_facet_neighbours(const facetT* facet, std::vector<int>& tri_indices, - int indices[3]) -{ - facetT *neighbor, **neighborp; - FOREACHneighbor_(facet) { - *indices++ = (neighbor->upperdelaunay ? -1 : tri_indices[neighbor->id]); - } -} - -/* Return true if the specified points arrays contain at least 3 unique points, - * or false otherwise. */ -static bool -at_least_3_unique_points(npy_intp npoints, const double* x, const double* y) -{ - int i; - const int unique1 = 0; /* First unique point has index 0. */ - int unique2 = 0; /* Second unique point index is 0 until set. */ - - if (npoints < 3) { - return false; - } - - for (i = 1; i < npoints; ++i) { - if (unique2 == 0) { - /* Looking for second unique point. */ - if (x[i] != x[unique1] || y[i] != y[unique1]) { - unique2 = i; - } - } - else { - /* Looking for third unique point. */ - if ( (x[i] != x[unique1] || y[i] != y[unique1]) && - (x[i] != x[unique2] || y[i] != y[unique2]) ) { - /* 3 unique points found, with indices 0, unique2 and i. */ - return true; - } - } - } - - /* Run out of points before 3 unique points found. */ - return false; -} - -/* Holds on to info from Qhull so that it can be destructed automatically. */ -class QhullInfo { -public: - QhullInfo(FILE *error_file, qhT* qh) { - this->error_file = error_file; - this->qh = qh; - } - - ~QhullInfo() { - qh_freeqhull(this->qh, !qh_ALL); - int curlong, totlong; /* Memory remaining. */ - qh_memfreeshort(this->qh, &curlong, &totlong); - if (curlong || totlong) { - PyErr_WarnEx(PyExc_RuntimeWarning, - "Qhull could not free all allocated memory", 1); - } - - if (this->error_file != stderr) { - fclose(error_file); - } - } - -private: - FILE* error_file; - qhT* qh; -}; - -/* Delaunay implementation method. - * If hide_qhull_errors is true then qhull error messages are discarded; - * if it is false then they are written to stderr. */ -static PyObject* -delaunay_impl(npy_intp npoints, const double* x, const double* y, - bool hide_qhull_errors) -{ - qhT qh_qh; /* qh variable type and name must be like */ - qhT* qh = &qh_qh; /* this for Qhull macros to work correctly. */ - facetT* facet; - int i, ntri, max_facet_id; - int exitcode; /* Value returned from qh_new_qhull(). */ - const int ndim = 2; - double x_mean = 0.0; - double y_mean = 0.0; - - QHULL_LIB_CHECK - - /* Allocate points. */ - std::vector<coordT> points(npoints * ndim); - - /* Determine mean x, y coordinates. */ - for (i = 0; i < npoints; ++i) { - x_mean += x[i]; - y_mean += y[i]; - } - x_mean /= npoints; - y_mean /= npoints; - - /* Prepare points array to pass to qhull. */ - for (i = 0; i < npoints; ++i) { - points[2*i ] = x[i] - x_mean; - points[2*i+1] = y[i] - y_mean; - } - - /* qhull expects a FILE* to write errors to. */ - FILE* error_file = NULL; - if (hide_qhull_errors) { - /* qhull errors are ignored by writing to OS-equivalent of /dev/null. - * Rather than have OS-specific code here, instead it is determined by - * setupext.py and passed in via the macro MPL_DEVNULL. */ - error_file = fopen(STRINGIFY(MPL_DEVNULL), "w"); - if (error_file == NULL) { - throw std::runtime_error("Could not open devnull"); - } - } - else { - /* qhull errors written to stderr. */ - error_file = stderr; - } - - /* Perform Delaunay triangulation. */ - QhullInfo info(error_file, qh); - qh_zero(qh, error_file); - exitcode = qh_new_qhull(qh, ndim, (int)npoints, points.data(), False, - (char*)"qhull d Qt Qbb Qc Qz", NULL, error_file); - if (exitcode != qh_ERRnone) { - PyErr_Format(PyExc_RuntimeError, - "Error in qhull Delaunay triangulation calculation: %s (exitcode=%d)%s", - qhull_error_msg[exitcode], exitcode, - hide_qhull_errors ? "; use python verbose option (-v) to see original qhull error." : ""); - return NULL; - } - - /* Split facets so that they only have 3 points each. */ - qh_triangulate(qh); - - /* Determine ntri and max_facet_id. - Note that libqhull uses macros to iterate through collections. */ - ntri = 0; - FORALLfacets { - if (!facet->upperdelaunay) { - ++ntri; - } - } - - max_facet_id = qh->facet_id - 1; - - /* Create array to map facet id to triangle index. */ - std::vector<int> tri_indices(max_facet_id+1); - - /* Allocate Python arrays to return. */ - npy_intp dims[2] = {ntri, 3}; - numpy::array_view<int, ndim> triangles(dims); - int* triangles_ptr = triangles.data(); - - numpy::array_view<int, ndim> neighbors(dims); - int* neighbors_ptr = neighbors.data(); - - /* Determine triangles array and set tri_indices array. */ - i = 0; - FORALLfacets { - if (!facet->upperdelaunay) { - int indices[3]; - tri_indices[facet->id] = i++; - get_facet_vertices(qh, facet, indices); - *triangles_ptr++ = (facet->toporient ? indices[0] : indices[2]); - *triangles_ptr++ = indices[1]; - *triangles_ptr++ = (facet->toporient ? indices[2] : indices[0]); - } - else { - tri_indices[facet->id] = -1; - } - } - - /* Determine neighbors array. */ - FORALLfacets { - if (!facet->upperdelaunay) { - int indices[3]; - get_facet_neighbours(facet, tri_indices, indices); - *neighbors_ptr++ = (facet->toporient ? indices[2] : indices[0]); - *neighbors_ptr++ = (facet->toporient ? indices[0] : indices[2]); - *neighbors_ptr++ = indices[1]; - } - } - - PyObject* tuple = PyTuple_New(2); - if (tuple == 0) { - throw std::runtime_error("Failed to create Python tuple"); - } - - PyTuple_SET_ITEM(tuple, 0, triangles.pyobj()); - PyTuple_SET_ITEM(tuple, 1, neighbors.pyobj()); - return tuple; -} - -/* Process Python arguments and call Delaunay implementation method. */ -static PyObject* -delaunay(PyObject *self, PyObject *args) -{ - numpy::array_view<double, 1> xarray; - numpy::array_view<double, 1> yarray; - PyObject* ret; - npy_intp npoints; - const double* x; - const double* y; - int verbose = 0; - - if (!PyArg_ParseTuple(args, "O&O&i:delaunay", - &xarray.converter_contiguous, &xarray, - &yarray.converter_contiguous, &yarray, - &verbose)) { - return NULL; - } - - npoints = xarray.dim(0); - if (npoints != yarray.dim(0)) { - PyErr_SetString(PyExc_ValueError, - "x and y must be 1D arrays of the same length"); - return NULL; - } - - if (npoints < 3) { - PyErr_SetString(PyExc_ValueError, - "x and y arrays must have a length of at least 3"); - return NULL; - } - - x = xarray.data(); - y = yarray.data(); - - if (!at_least_3_unique_points(npoints, x, y)) { - PyErr_SetString(PyExc_ValueError, - "x and y arrays must consist of at least 3 unique points"); - return NULL; - } - - CALL_CPP("qhull.delaunay", - (ret = delaunay_impl(npoints, x, y, verbose == 0))); - - return ret; -} - -/* Return qhull version string for assistance in debugging. */ -static PyObject* -version(PyObject *self, PyObject *arg) -{ - return PyBytes_FromString(qh_version); -} - -static PyMethodDef qhull_methods[] = { - {"delaunay", delaunay, METH_VARARGS, - "delaunay(x, y, verbose, /)\n" - "--\n\n" - "Compute a Delaunay triangulation.\n" - "\n" - "Parameters\n" - "----------\n" - "x, y : 1d arrays\n" - " The coordinates of the point set, which must consist of at least\n" - " three unique points.\n" - "verbose : int\n" - " Python's verbosity level.\n" - "\n" - "Returns\n" - "-------\n" - "triangles, neighbors : int arrays, shape (ntri, 3)\n" - " Indices of triangle vertices and indices of triangle neighbors.\n" - }, - {"version", version, METH_NOARGS, - "version()\n--\n\n" - "Return the qhull version string."}, - {NULL, NULL, 0, NULL} -}; - -static struct PyModuleDef qhull_module = { - PyModuleDef_HEAD_INIT, - "qhull", "Computing Delaunay triangulations.\n", -1, qhull_methods -}; - -PyMODINIT_FUNC -PyInit__qhull(void) -{ - import_array(); - return PyModule_Create(&qhull_module); -} diff --git a/contrib/python/matplotlib/py3/src/_tkagg.cpp b/contrib/python/matplotlib/py3/src/_tkagg.cpp deleted file mode 100644 index 5c36b3f07f..0000000000 --- a/contrib/python/matplotlib/py3/src/_tkagg.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -// Where is PIL? -// -// Many years ago, Matplotlib used to include code from PIL (the Python Imaging -// Library). Since then, the code has changed a lot - the organizing principle -// and methods of operation are now quite different. Because our review of -// the codebase showed that all the code that came from PIL was removed or -// rewritten, we have removed the PIL licensing information. If you want PIL, -// you can get it at https://python-pillow.org/ - -#define PY_SSIZE_T_CLEAN -#include <Python.h> - -#ifdef _WIN32 -#define WIN32_DLL -#endif -#ifdef __CYGWIN__ -/* - * Unfortunately cygwin's libdl inherits restrictions from the underlying - * Windows OS, at least currently. Therefore, a symbol may be loaded from a - * module by dlsym() only if it is really located in the given module, - * dependencies are not included. So we have to use native WinAPI on Cygwin - * also. - */ -#define WIN32_DLL -static inline PyObject *PyErr_SetFromWindowsErr(int ierr) { - PyErr_SetString(PyExc_OSError, "Call to EnumProcessModules failed"); - return NULL; -} -#endif - -#ifdef WIN32_DLL -#include <string> -#include <windows.h> -#include <commctrl.h> -#define PSAPI_VERSION 1 -#include <psapi.h> // Must be linked with 'psapi' library -#define dlsym GetProcAddress -#else -#include <dlfcn.h> -#endif - -// Include our own excerpts from the Tcl / Tk headers -#include "_tkmini.h" - -static int convert_voidptr(PyObject *obj, void *p) -{ - void **val = (void **)p; - *val = PyLong_AsVoidPtr(obj); - return *val != NULL ? 1 : !PyErr_Occurred(); -} - -// Global vars for Tk functions. We load these symbols from the tkinter -// extension module or loaded Tk libraries at run-time. -static Tk_FindPhoto_t TK_FIND_PHOTO; -static Tk_PhotoPutBlock_t TK_PHOTO_PUT_BLOCK; -// Global vars for Tcl functions. We load these symbols from the tkinter -// extension module or loaded Tcl libraries at run-time. -static Tcl_SetVar_t TCL_SETVAR; - -static PyObject *mpl_tk_blit(PyObject *self, PyObject *args) -{ - Tcl_Interp *interp; - char const *photo_name; - int height, width; - unsigned char *data_ptr; - int comp_rule; - int put_retval; - int o0, o1, o2, o3; - int x1, x2, y1, y2; - Tk_PhotoHandle photo; - Tk_PhotoImageBlock block; - if (!PyArg_ParseTuple(args, "O&s(iiO&)i(iiii)(iiii):blit", - convert_voidptr, &interp, &photo_name, - &height, &width, convert_voidptr, &data_ptr, - &comp_rule, - &o0, &o1, &o2, &o3, - &x1, &x2, &y1, &y2)) { - goto exit; - } - if (!(photo = TK_FIND_PHOTO(interp, photo_name))) { - PyErr_SetString(PyExc_ValueError, "Failed to extract Tk_PhotoHandle"); - goto exit; - } - if (0 > y1 || y1 > y2 || y2 > height || 0 > x1 || x1 > x2 || x2 > width) { - PyErr_SetString(PyExc_ValueError, "Attempting to draw out of bounds"); - goto exit; - } - if (comp_rule != TK_PHOTO_COMPOSITE_OVERLAY && comp_rule != TK_PHOTO_COMPOSITE_SET) { - PyErr_SetString(PyExc_ValueError, "Invalid comp_rule argument"); - goto exit; - } - - Py_BEGIN_ALLOW_THREADS - block.pixelPtr = data_ptr + 4 * ((height - y2) * width + x1); - block.width = x2 - x1; - block.height = y2 - y1; - block.pitch = 4 * width; - block.pixelSize = 4; - block.offset[0] = o0; - block.offset[1] = o1; - block.offset[2] = o2; - block.offset[3] = o3; - put_retval = TK_PHOTO_PUT_BLOCK( - interp, photo, &block, x1, height - y2, x2 - x1, y2 - y1, comp_rule); - Py_END_ALLOW_THREADS - if (put_retval == TCL_ERROR) { - return PyErr_NoMemory(); - } - -exit: - if (PyErr_Occurred()) { - return NULL; - } else { - Py_RETURN_NONE; - } -} - -#ifdef WIN32_DLL -LRESULT CALLBACK -DpiSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, - UINT_PTR uIdSubclass, DWORD_PTR dwRefData) -{ - switch (uMsg) { - case WM_DPICHANGED: - // This function is a subclassed window procedure, and so is run during - // the Tcl/Tk event loop. Unfortunately, Tkinter has a *second* lock on - // Tcl threading that is not exposed publicly, but is currently taken - // while we're in the window procedure. So while we can take the GIL to - // call Python code, we must not also call *any* Tk code from Python. - // So stay with Tcl calls in C only. - { - // This variable naming must match the name used in - // lib/matplotlib/backends/_backend_tk.py:FigureManagerTk. - std::string var_name("window_dpi"); - var_name += std::to_string((unsigned long long)hwnd); - - // X is high word, Y is low word, but they are always equal. - std::string dpi = std::to_string(LOWORD(wParam)); - - Tcl_Interp* interp = (Tcl_Interp*)dwRefData; - TCL_SETVAR(interp, var_name.c_str(), dpi.c_str(), 0); - } - return 0; - case WM_NCDESTROY: - RemoveWindowSubclass(hwnd, DpiSubclassProc, uIdSubclass); - break; - } - - return DefSubclassProc(hwnd, uMsg, wParam, lParam); -} -#endif - -static PyObject* -mpl_tk_enable_dpi_awareness(PyObject* self, PyObject*const* args, - Py_ssize_t nargs) -{ - if (nargs != 2) { - return PyErr_Format(PyExc_TypeError, - "enable_dpi_awareness() takes 2 positional " - "arguments but %zd were given", - nargs); - } - -#ifdef WIN32_DLL - HWND frame_handle = NULL; - Tcl_Interp *interp = NULL; - - if (!convert_voidptr(args[0], &frame_handle)) { - return NULL; - } - if (!convert_voidptr(args[1], &interp)) { - return NULL; - } - -#ifdef _DPI_AWARENESS_CONTEXTS_ - HMODULE user32 = LoadLibrary("user32.dll"); - - typedef DPI_AWARENESS_CONTEXT (WINAPI *GetWindowDpiAwarenessContext_t)(HWND); - GetWindowDpiAwarenessContext_t GetWindowDpiAwarenessContextPtr = - (GetWindowDpiAwarenessContext_t)GetProcAddress( - user32, "GetWindowDpiAwarenessContext"); - if (GetWindowDpiAwarenessContextPtr == NULL) { - FreeLibrary(user32); - Py_RETURN_FALSE; - } - - typedef BOOL (WINAPI *AreDpiAwarenessContextsEqual_t)(DPI_AWARENESS_CONTEXT, - DPI_AWARENESS_CONTEXT); - AreDpiAwarenessContextsEqual_t AreDpiAwarenessContextsEqualPtr = - (AreDpiAwarenessContextsEqual_t)GetProcAddress( - user32, "AreDpiAwarenessContextsEqual"); - if (AreDpiAwarenessContextsEqualPtr == NULL) { - FreeLibrary(user32); - Py_RETURN_FALSE; - } - - DPI_AWARENESS_CONTEXT ctx = GetWindowDpiAwarenessContextPtr(frame_handle); - bool per_monitor = ( - AreDpiAwarenessContextsEqualPtr( - ctx, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) || - AreDpiAwarenessContextsEqualPtr( - ctx, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)); - - if (per_monitor) { - // Per monitor aware means we need to handle WM_DPICHANGED by wrapping - // the Window Procedure, and the Python side needs to trace the Tk - // window_dpi variable stored on interp. - SetWindowSubclass(frame_handle, DpiSubclassProc, 0, (DWORD_PTR)interp); - } - FreeLibrary(user32); - return PyBool_FromLong(per_monitor); -#endif -#endif - - Py_RETURN_NONE; -} - -static PyMethodDef functions[] = { - { "blit", (PyCFunction)mpl_tk_blit, METH_VARARGS }, - { "enable_dpi_awareness", (PyCFunction)mpl_tk_enable_dpi_awareness, - METH_FASTCALL }, - { NULL, NULL } /* sentinel */ -}; - -// Functions to fill global Tcl/Tk function pointers by dynamic loading. - -template <class T> -bool load_tcl_tk(T lib) -{ - // Try to fill Tcl/Tk global vars with function pointers. Return whether - // all of them have been filled. - if (auto ptr = dlsym(lib, "Tcl_SetVar")) { - TCL_SETVAR = (Tcl_SetVar_t)ptr; - } - if (auto ptr = dlsym(lib, "Tk_FindPhoto")) { - TK_FIND_PHOTO = (Tk_FindPhoto_t)ptr; - } - if (auto ptr = dlsym(lib, "Tk_PhotoPutBlock")) { - TK_PHOTO_PUT_BLOCK = (Tk_PhotoPutBlock_t)ptr; - } - return TCL_SETVAR && TK_FIND_PHOTO && TK_PHOTO_PUT_BLOCK; -} - -#ifdef WIN32_DLL - -/* On Windows, we can't load the tkinter module to get the Tcl/Tk symbols, - * because Windows does not load symbols into the library name-space of - * importing modules. So, knowing that tkinter has already been imported by - * Python, we scan all modules in the running process for the Tcl/Tk function - * names. - */ - -void load_tkinter_funcs(void) -{ - HANDLE process = GetCurrentProcess(); // Pseudo-handle, doesn't need closing. - HMODULE* modules = NULL; - DWORD size; - if (!EnumProcessModules(process, NULL, 0, &size)) { - PyErr_SetFromWindowsErr(0); - goto exit; - } - if (!(modules = static_cast<HMODULE*>(malloc(size)))) { - PyErr_NoMemory(); - goto exit; - } - if (!EnumProcessModules(process, modules, size, &size)) { - PyErr_SetFromWindowsErr(0); - goto exit; - } - for (unsigned i = 0; i < size / sizeof(HMODULE); ++i) { - if (load_tcl_tk(modules[i])) { - return; - } - } -exit: - free(modules); -} - -#else // not Windows - -/* - * On Unix, we can get the Tk symbols from the tkinter module, because tkinter - * uses these symbols, and the symbols are therefore visible in the tkinter - * dynamic library (module). - */ - -void load_tkinter_funcs(void) -{ - // Load tkinter global funcs from tkinter compiled module. - void *main_program = NULL, *tkinter_lib = NULL; - PyObject *module = NULL, *py_path = NULL, *py_path_b = NULL; - char *path; - - // Try loading from the main program namespace first. - main_program = dlopen(NULL, RTLD_LAZY); - if (load_tcl_tk(main_program)) { - goto exit; - } - // Clear exception triggered when we didn't find symbols above. - PyErr_Clear(); - - // Handle PyPy first, as that import will correctly fail on CPython. - module = PyImport_ImportModule("_tkinter.tklib_cffi"); // PyPy - if (!module) { - PyErr_Clear(); - module = PyImport_ImportModule("_tkinter"); // CPython - } - if (!(module && - (py_path = PyObject_GetAttrString(module, "__file__")) && - (py_path_b = PyUnicode_EncodeFSDefault(py_path)) && - (path = PyBytes_AsString(py_path_b)))) { - goto exit; - } - tkinter_lib = dlopen(path, RTLD_LAZY); - if (!tkinter_lib) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - goto exit; - } - if (load_tcl_tk(tkinter_lib)) { - goto exit; - } - -exit: - // We don't need to keep a reference open as the main program & tkinter - // have been imported. Try to close each library separately (otherwise the - // second dlclose could clear a dlerror from the first dlclose). - bool raised_dlerror = false; - if (main_program && dlclose(main_program) && !raised_dlerror) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - raised_dlerror = true; - } - if (tkinter_lib && dlclose(tkinter_lib) && !raised_dlerror) { - PyErr_SetString(PyExc_RuntimeError, dlerror()); - raised_dlerror = true; - } - Py_XDECREF(module); - Py_XDECREF(py_path); - Py_XDECREF(py_path_b); -} -#endif // end not Windows - -static PyModuleDef _tkagg_module = { - PyModuleDef_HEAD_INIT, "_tkagg", NULL, -1, functions -}; - -PyMODINIT_FUNC PyInit__tkagg(void) -{ - load_tkinter_funcs(); - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - // Always raise ImportError (normalizing a previously set exception if - // needed) to interact properly with backend auto-fallback. - if (value) { - PyErr_NormalizeException(&type, &value, &traceback); - PyErr_SetObject(PyExc_ImportError, value); - return NULL; - } else if (!TCL_SETVAR) { - PyErr_SetString(PyExc_ImportError, "Failed to load Tcl_SetVar"); - return NULL; - } else if (!TK_FIND_PHOTO) { - PyErr_SetString(PyExc_ImportError, "Failed to load Tk_FindPhoto"); - return NULL; - } else if (!TK_PHOTO_PUT_BLOCK) { - PyErr_SetString(PyExc_ImportError, "Failed to load Tk_PhotoPutBlock"); - return NULL; - } - return PyModule_Create(&_tkagg_module); -} diff --git a/contrib/python/matplotlib/py3/src/_tkmini.h b/contrib/python/matplotlib/py3/src/_tkmini.h deleted file mode 100644 index 85f245815e..0000000000 --- a/contrib/python/matplotlib/py3/src/_tkmini.h +++ /dev/null @@ -1,110 +0,0 @@ -/* Small excerpts from the Tcl / Tk 8.6 headers - * - * License terms copied from: - * http://www.tcl.tk/software/tcltk/license.html - * as of 20 May 2016. - * - * Copyright (c) 1987-1994 The Regents of the University of California. - * Copyright (c) 1993-1996 Lucent Technologies. - * Copyright (c) 1994-1998 Sun Microsystems, Inc. - * Copyright (c) 1998-2000 by Scriptics Corporation. - * Copyright (c) 2002 by Kevin B. Kenny. All rights reserved. - * - * This software is copyrighted by the Regents of the University - * of California, Sun Microsystems, Inc., Scriptics Corporation, - * and other parties. The following terms apply to all files - * associated with the software unless explicitly disclaimed in - * individual files. - * - * The authors hereby grant permission to use, copy, modify, - * distribute, and license this software and its documentation - * for any purpose, provided that existing copyright notices are - * retained in all copies and that this notice is included - * verbatim in any distributions. No written agreement, license, - * or royalty fee is required for any of the authorized uses. - * Modifications to this software may be copyrighted by their - * authors and need not follow the licensing terms described - * here, provided that the new terms are clearly indicated on - * the first page of each file where they apply. - * - * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO - * ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR - * CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS - * SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN - * IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * - * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON - * AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO - * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, - * ENHANCEMENTS, OR MODIFICATIONS. - * - * GOVERNMENT USE: If you are acquiring this software on behalf - * of the U.S. government, the Government shall have only - * "Restricted Rights" in the software and related documentation - * as defined in the Federal Acquisition Regulations (FARs) in - * Clause 52.227.19 (c) (2). If you are acquiring the software - * on behalf of the Department of Defense, the software shall be - * classified as "Commercial Computer Software" and the - * Government shall have only "Restricted Rights" as defined in - * Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the - * foregoing, the authors grant the U.S. Government and others - * acting in its behalf permission to use and distribute the - * software in accordance with the terms specified in this - * license - */ - -/* - * Unless otherwise noted, these definitions are stable from Tcl / Tk 8.5 - * through Tck / Tk master as of 21 May 2016 - */ - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * Users of versions of Tcl >= 8.6 encouraged to treat Tcl_Interp as an opaque - * pointer. The following definition results when TCL_NO_DEPRECATED defined. - */ -typedef struct Tcl_Interp Tcl_Interp; - -/* Tk header excerpts */ - -typedef void *Tk_PhotoHandle; - -typedef struct Tk_PhotoImageBlock -{ - unsigned char *pixelPtr; - int width; - int height; - int pitch; - int pixelSize; - int offset[4]; -} Tk_PhotoImageBlock; - -#define TK_PHOTO_COMPOSITE_OVERLAY 0 // apply transparency rules pixel-wise -#define TK_PHOTO_COMPOSITE_SET 1 // set image buffer directly -#define TCL_OK 0 -#define TCL_ERROR 1 - -/* Typedefs derived from function signatures in Tk header */ -/* Tk_FindPhoto typedef */ -typedef Tk_PhotoHandle (*Tk_FindPhoto_t) (Tcl_Interp *interp, const char - *imageName); -/* Tk_PhotoPutBLock typedef */ -typedef int (*Tk_PhotoPutBlock_t) (Tcl_Interp *interp, Tk_PhotoHandle handle, - Tk_PhotoImageBlock *blockPtr, int x, int y, - int width, int height, int compRule); - -/* Typedefs derived from function signatures in Tcl header */ -/* Tcl_SetVar typedef */ -typedef const char *(*Tcl_SetVar_t)(Tcl_Interp *interp, const char *varName, - const char *newValue, int flags); - -#ifdef __cplusplus -} -#endif diff --git a/contrib/python/matplotlib/py3/src/_ttconv.cpp b/contrib/python/matplotlib/py3/src/_ttconv.cpp deleted file mode 100644 index 72fdfba696..0000000000 --- a/contrib/python/matplotlib/py3/src/_ttconv.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* - _ttconv.c - - Python wrapper for TrueType conversion library in ../ttconv. - */ -#include "mplutils.h" - -#include <pybind11/pybind11.h> -#include "ttconv/pprdrv.h" -#include <vector> - -namespace py = pybind11; - -/** - * An implementation of TTStreamWriter that writes to a Python - * file-like object. - */ -class PythonFileWriter : public TTStreamWriter -{ - py::function _write_method; - - public: - PythonFileWriter(py::object& file_object) - : _write_method(file_object.attr("write")) {} - - virtual void write(const char *a) - { - PyObject* decoded = PyUnicode_DecodeLatin1(a, strlen(a), ""); - if (decoded == NULL) { - throw py::error_already_set(); - } - _write_method(py::handle(decoded)); - Py_DECREF(decoded); - } -}; - -static void convert_ttf_to_ps( - const char *filename, - py::object &output, - int fonttype, - py::iterable* glyph_ids) -{ - PythonFileWriter output_(output); - - std::vector<int> glyph_ids_; - if (glyph_ids) { - for (py::handle glyph_id: *glyph_ids) { - glyph_ids_.push_back(glyph_id.cast<int>()); - } - } - - if (fonttype != 3 && fonttype != 42) { - throw py::value_error( - "fonttype must be either 3 (raw Postscript) or 42 (embedded Truetype)"); - } - - try - { - insert_ttfont(filename, output_, static_cast<font_type_enum>(fonttype), glyph_ids_); - } - catch (TTException &e) - { - throw std::runtime_error(e.getMessage()); - } - catch (...) - { - throw std::runtime_error("Unknown C++ exception"); - } -} - -PYBIND11_MODULE(_ttconv, m) { - m.doc() = "Module to handle converting and subsetting TrueType " - "fonts to Postscript Type 3, Postscript Type 42 and " - "Pdf Type 3 fonts."; - m.def("convert_ttf_to_ps", &convert_ttf_to_ps, - py::arg("filename"), - py::arg("output"), - py::arg("fonttype"), - py::arg("glyph_ids") = py::none(), - "Converts the Truetype font into a Type 3 or Type 42 Postscript font, " - "optionally subsetting the font to only the desired set of characters.\n" - "\n" - "filename is the path to a TTF font file.\n" - "output is a Python file-like object with a write method that the Postscript " - "font data will be written to.\n" - "fonttype may be either 3 or 42. Type 3 is a \"raw Postscript\" font. " - "Type 42 is an embedded Truetype font. Glyph subsetting is not supported " - "for Type 42 fonts within this module (needs to be done externally).\n" - "glyph_ids (optional) is a list of glyph ids (integers) to keep when " - "subsetting to a Type 3 font. If glyph_ids is not provided or is None, " - "then all glyphs will be included. If any of the glyphs specified are " - "composite glyphs, then the component glyphs will also be included." - ); -} diff --git a/contrib/python/matplotlib/py3/src/agg_workaround.h b/contrib/python/matplotlib/py3/src/agg_workaround.h deleted file mode 100644 index 4762195192..0000000000 --- a/contrib/python/matplotlib/py3/src/agg_workaround.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef MPL_AGG_WORKAROUND_H -#define MPL_AGG_WORKAROUND_H - -#include "agg_pixfmt_rgba.h" - -/********************************************************************** - WORKAROUND: This class is to workaround a bug in Agg SVN where the - blending of RGBA32 pixels does not preserve enough precision -*/ - -template<class ColorT, class Order> -struct fixed_blender_rgba_pre : agg::conv_rgba_pre<ColorT, Order> -{ - typedef ColorT color_type; - typedef Order order_type; - typedef typename color_type::value_type value_type; - typedef typename color_type::calc_type calc_type; - typedef typename color_type::long_type long_type; - enum base_scale_e - { - base_shift = color_type::base_shift, - base_mask = color_type::base_mask - }; - - //-------------------------------------------------------------------- - static AGG_INLINE void blend_pix(value_type* p, - value_type cr, value_type cg, value_type cb, - value_type alpha, agg::cover_type cover) - { - blend_pix(p, - color_type::mult_cover(cr, cover), - color_type::mult_cover(cg, cover), - color_type::mult_cover(cb, cover), - color_type::mult_cover(alpha, cover)); - } - - //-------------------------------------------------------------------- - static AGG_INLINE void blend_pix(value_type* p, - value_type cr, value_type cg, value_type cb, - value_type alpha) - { - alpha = base_mask - alpha; - p[Order::R] = (value_type)(((p[Order::R] * alpha) >> base_shift) + cr); - p[Order::G] = (value_type)(((p[Order::G] * alpha) >> base_shift) + cg); - p[Order::B] = (value_type)(((p[Order::B] * alpha) >> base_shift) + cb); - p[Order::A] = (value_type)(base_mask - ((alpha * (base_mask - p[Order::A])) >> base_shift)); - } -}; - - -template<class ColorT, class Order> -struct fixed_blender_rgba_plain : agg::conv_rgba_plain<ColorT, Order> -{ - typedef ColorT color_type; - typedef Order order_type; - typedef typename color_type::value_type value_type; - typedef typename color_type::calc_type calc_type; - typedef typename color_type::long_type long_type; - enum base_scale_e { base_shift = color_type::base_shift }; - - //-------------------------------------------------------------------- - static AGG_INLINE void blend_pix(value_type* p, - value_type cr, value_type cg, value_type cb, value_type alpha, agg::cover_type cover) - { - blend_pix(p, cr, cg, cb, color_type::mult_cover(alpha, cover)); - } - - //-------------------------------------------------------------------- - static AGG_INLINE void blend_pix(value_type* p, - value_type cr, value_type cg, value_type cb, value_type alpha) - { - if(alpha == 0) return; - calc_type a = p[Order::A]; - calc_type r = p[Order::R] * a; - calc_type g = p[Order::G] * a; - calc_type b = p[Order::B] * a; - a = ((alpha + a) << base_shift) - alpha * a; - p[Order::A] = (value_type)(a >> base_shift); - p[Order::R] = (value_type)((((cr << base_shift) - r) * alpha + (r << base_shift)) / a); - p[Order::G] = (value_type)((((cg << base_shift) - g) * alpha + (g << base_shift)) / a); - p[Order::B] = (value_type)((((cb << base_shift) - b) * alpha + (b << base_shift)) / a); - } -}; - -#endif diff --git a/contrib/python/matplotlib/py3/src/array.h b/contrib/python/matplotlib/py3/src/array.h deleted file mode 100644 index 47d8299554..0000000000 --- a/contrib/python/matplotlib/py3/src/array.h +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* Utilities to create scalars and empty arrays that behave like the - Numpy array wrappers in numpy_cpp.h */ - -#ifndef MPL_SCALAR_H -#define MPL_SCALAR_H - -namespace array -{ - -template <typename T, int ND> -class scalar -{ - public: - T m_value; - - scalar(const T value) : m_value(value) - { - } - - T &operator()(int i, int j = 0, int k = 0) - { - return m_value; - } - - const T &operator()(int i, int j = 0, int k = 0) const - { - return m_value; - } - - int dim(size_t i) - { - return 1; - } - - size_t size() - { - return 1; - } -}; - -template <typename T> -class empty -{ - public: - typedef empty<T> sub_t; - - empty() - { - } - - T &operator()(int i, int j = 0, int k = 0) - { - throw std::runtime_error("Accessed empty array"); - } - - const T &operator()(int i, int j = 0, int k = 0) const - { - throw std::runtime_error("Accessed empty array"); - } - - sub_t operator[](int i) const - { - return empty<T>(); - } - - int dim(size_t i) const - { - return 0; - } - - size_t size() const - { - return 0; - } -}; -} - -#endif diff --git a/contrib/python/matplotlib/py3/src/checkdep_freetype2.c b/contrib/python/matplotlib/py3/src/checkdep_freetype2.c deleted file mode 100644 index 8d9d8ca24a..0000000000 --- a/contrib/python/matplotlib/py3/src/checkdep_freetype2.c +++ /dev/null @@ -1,19 +0,0 @@ -#ifdef __has_include - #if !__has_include(<ft2build.h>) - #error "FreeType version 2.3 or higher is required. \ -You may unset the system_freetype entry in mplsetup.cfg to let Matplotlib download it." - #endif -#endif - -#include <ft2build.h> -#include FT_FREETYPE_H - -#define XSTR(x) STR(x) -#define STR(x) #x - -#pragma message("Compiling with FreeType version " \ - XSTR(FREETYPE_MAJOR) "." XSTR(FREETYPE_MINOR) "." XSTR(FREETYPE_PATCH) ".") -#if FREETYPE_MAJOR << 16 + FREETYPE_MINOR << 8 + FREETYPE_PATCH < 0x020300 - #error "FreeType version 2.3 or higher is required. \ -You may unset the system_freetype entry in mplsetup.cfg to let Matplotlib download it." -#endif diff --git a/contrib/python/matplotlib/py3/src/ft2font.cpp b/contrib/python/matplotlib/py3/src/ft2font.cpp deleted file mode 100644 index 9750413741..0000000000 --- a/contrib/python/matplotlib/py3/src/ft2font.cpp +++ /dev/null @@ -1,840 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#define NO_IMPORT_ARRAY - -#include <algorithm> -#include <iterator> -#include <sstream> -#include <stdexcept> -#include <string> - -#include "ft2font.h" -#include "mplutils.h" -#include "numpy_cpp.h" -#include "py_exceptions.h" - -#ifndef M_PI -#define M_PI 3.14159265358979323846264338328 -#endif - -/** - To improve the hinting of the fonts, this code uses a hack - presented here: - - http://agg.sourceforge.net/antigrain.com/research/font_rasterization/index.html - - The idea is to limit the effect of hinting in the x-direction, while - preserving hinting in the y-direction. Since freetype does not - support this directly, the dpi in the x-direction is set higher than - in the y-direction, which affects the hinting grid. Then, a global - transform is placed on the font to shrink it back to the desired - size. While it is a bit surprising that the dpi setting affects - hinting, whereas the global transform does not, this is documented - behavior of FreeType, and therefore hopefully unlikely to change. - The FreeType 2 tutorial says: - - NOTE: The transformation is applied to every glyph that is - loaded through FT_Load_Glyph and is completely independent of - any hinting process. This means that you won't get the same - results if you load a glyph at the size of 24 pixels, or a glyph - at the size at 12 pixels scaled by 2 through a transform, - because the hints will have been computed differently (except - you have disabled hints). - */ - -FT_Library _ft2Library; - -// FreeType error codes; loaded as per fterror.h. -static char const* ft_error_string(FT_Error error) { -#undef __FTERRORS_H__ -#define FT_ERROR_START_LIST switch (error) { -#define FT_ERRORDEF( e, v, s ) case v: return s; -#define FT_ERROR_END_LIST default: return NULL; } -#include FT_ERRORS_H -} - -void throw_ft_error(std::string message, FT_Error error) { - char const* s = ft_error_string(error); - std::ostringstream os(""); - if (s) { - os << message << " (" << s << "; error code 0x" << std::hex << error << ")"; - } else { // Should not occur, but don't add another error from failed lookup. - os << message << " (error code 0x" << std::hex << error << ")"; - } - throw std::runtime_error(os.str()); -} - -FT2Image::FT2Image() : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0) -{ -} - -FT2Image::FT2Image(unsigned long width, unsigned long height) - : m_dirty(true), m_buffer(NULL), m_width(0), m_height(0) -{ - resize(width, height); -} - -FT2Image::~FT2Image() -{ - delete[] m_buffer; -} - -void FT2Image::resize(long width, long height) -{ - if (width <= 0) { - width = 1; - } - if (height <= 0) { - height = 1; - } - size_t numBytes = width * height; - - if ((unsigned long)width != m_width || (unsigned long)height != m_height) { - if (numBytes > m_width * m_height) { - delete[] m_buffer; - m_buffer = NULL; - m_buffer = new unsigned char[numBytes]; - } - - m_width = (unsigned long)width; - m_height = (unsigned long)height; - } - - if (numBytes && m_buffer) { - memset(m_buffer, 0, numBytes); - } - - m_dirty = true; -} - -void FT2Image::draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) -{ - FT_Int image_width = (FT_Int)m_width; - FT_Int image_height = (FT_Int)m_height; - FT_Int char_width = bitmap->width; - FT_Int char_height = bitmap->rows; - - FT_Int x1 = std::min(std::max(x, 0), image_width); - FT_Int y1 = std::min(std::max(y, 0), image_height); - FT_Int x2 = std::min(std::max(x + char_width, 0), image_width); - FT_Int y2 = std::min(std::max(y + char_height, 0), image_height); - - FT_Int x_start = std::max(0, -x); - FT_Int y_offset = y1 - std::max(0, -y); - - if (bitmap->pixel_mode == FT_PIXEL_MODE_GRAY) { - for (FT_Int i = y1; i < y2; ++i) { - unsigned char *dst = m_buffer + (i * image_width + x1); - unsigned char *src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start); - for (FT_Int j = x1; j < x2; ++j, ++dst, ++src) - *dst |= *src; - } - } else if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { - for (FT_Int i = y1; i < y2; ++i) { - unsigned char *dst = m_buffer + (i * image_width + x1); - unsigned char *src = bitmap->buffer + ((i - y_offset) * bitmap->pitch); - for (FT_Int j = x1; j < x2; ++j, ++dst) { - int x = (j - x1 + x_start); - int val = *(src + (x >> 3)) & (1 << (7 - (x & 0x7))); - *dst = val ? 255 : *dst; - } - } - } else { - throw std::runtime_error("Unknown pixel mode"); - } - - m_dirty = true; -} - -void FT2Image::draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1) -{ - if (x0 > m_width || x1 > m_width || y0 > m_height || y1 > m_height) { - throw std::runtime_error("Rect coords outside image bounds"); - } - - size_t top = y0 * m_width; - size_t bottom = y1 * m_width; - for (size_t i = x0; i < x1 + 1; ++i) { - m_buffer[i + top] = 255; - m_buffer[i + bottom] = 255; - } - - for (size_t j = y0 + 1; j < y1; ++j) { - m_buffer[x0 + j * m_width] = 255; - m_buffer[x1 + j * m_width] = 255; - } - - m_dirty = true; -} - -void -FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1) -{ - x0 = std::min(x0, m_width); - y0 = std::min(y0, m_height); - x1 = std::min(x1 + 1, m_width); - y1 = std::min(y1 + 1, m_height); - - for (size_t j = y0; j < y1; j++) { - for (size_t i = x0; i < x1; i++) { - m_buffer[i + j * m_width] = 255; - } - } - - m_dirty = true; -} - -static void ft_glyph_warn(FT_ULong charcode) -{ - PyObject *text_helpers = NULL, *tmp = NULL; - if (!(text_helpers = PyImport_ImportModule("matplotlib._text_helpers")) || - !(tmp = PyObject_CallMethod(text_helpers, "warn_on_missing_glyph", "k", charcode))) { - goto exit; - } -exit: - Py_XDECREF(text_helpers); - Py_XDECREF(tmp); - if (PyErr_Occurred()) { - throw py::exception(); - } -} - -static FT_UInt -ft_get_char_index_or_warn(FT_Face face, FT_ULong charcode, bool warn = true) -{ - FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - if (glyph_index) { - return glyph_index; - } - if (warn) { - ft_glyph_warn(charcode); - } - return 0; -} - -// ft_outline_decomposer should be passed to FT_Outline_Decompose. On the -// first pass, vertices and codes are set to NULL, and index is simply -// incremented for each vertex that should be inserted, so that it is set, at -// the end, to the total number of vertices. On a second pass, vertices and -// codes should point to correctly sized arrays, and index set again to zero, -// to get fill vertices and codes with the outline decomposition. -struct ft_outline_decomposer -{ - int index; - double* vertices; - unsigned char* codes; -}; - -static int -ft_outline_move_to(FT_Vector const* to, void* user) -{ - ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user); - if (d->codes) { - if (d->index) { - // Appending CLOSEPOLY is important to make patheffects work. - *(d->vertices++) = 0; - *(d->vertices++) = 0; - *(d->codes++) = CLOSEPOLY; - } - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = MOVETO; - } - d->index += d->index ? 2 : 1; - return 0; -} - -static int -ft_outline_line_to(FT_Vector const* to, void* user) -{ - ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user); - if (d->codes) { - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = LINETO; - } - d->index++; - return 0; -} - -static int -ft_outline_conic_to(FT_Vector const* control, FT_Vector const* to, void* user) -{ - ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user); - if (d->codes) { - *(d->vertices++) = control->x * (1. / 64.); - *(d->vertices++) = control->y * (1. / 64.); - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = CURVE3; - *(d->codes++) = CURVE3; - } - d->index += 2; - return 0; -} - -static int -ft_outline_cubic_to( - FT_Vector const* c1, FT_Vector const* c2, FT_Vector const* to, void* user) -{ - ft_outline_decomposer* d = reinterpret_cast<ft_outline_decomposer*>(user); - if (d->codes) { - *(d->vertices++) = c1->x * (1. / 64.); - *(d->vertices++) = c1->y * (1. / 64.); - *(d->vertices++) = c2->x * (1. / 64.); - *(d->vertices++) = c2->y * (1. / 64.); - *(d->vertices++) = to->x * (1. / 64.); - *(d->vertices++) = to->y * (1. / 64.); - *(d->codes++) = CURVE4; - *(d->codes++) = CURVE4; - *(d->codes++) = CURVE4; - } - d->index += 3; - return 0; -} - -static FT_Outline_Funcs ft_outline_funcs = { - ft_outline_move_to, - ft_outline_line_to, - ft_outline_conic_to, - ft_outline_cubic_to}; - -PyObject* -FT2Font::get_path() -{ - if (!face->glyph) { - PyErr_SetString(PyExc_RuntimeError, "No glyph loaded"); - return NULL; - } - ft_outline_decomposer decomposer = {}; - if (FT_Error error = - FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - PyErr_Format(PyExc_RuntimeError, - "FT_Outline_Decompose failed with error 0x%x", error); - return NULL; - } - if (!decomposer.index) { // Don't append CLOSEPOLY to null glyphs. - npy_intp vertices_dims[2] = { 0, 2 }; - numpy::array_view<double, 2> vertices(vertices_dims); - npy_intp codes_dims[1] = { 0 }; - numpy::array_view<unsigned char, 1> codes(codes_dims); - return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); - } - npy_intp vertices_dims[2] = { decomposer.index + 1, 2 }; - numpy::array_view<double, 2> vertices(vertices_dims); - npy_intp codes_dims[1] = { decomposer.index + 1 }; - numpy::array_view<unsigned char, 1> codes(codes_dims); - decomposer.index = 0; - decomposer.vertices = vertices.data(); - decomposer.codes = codes.data(); - if (FT_Error error = - FT_Outline_Decompose( - &face->glyph->outline, &ft_outline_funcs, &decomposer)) { - PyErr_Format(PyExc_RuntimeError, - "FT_Outline_Decompose failed with error 0x%x", error); - return NULL; - } - *(decomposer.vertices++) = 0; - *(decomposer.vertices++) = 0; - *(decomposer.codes++) = CLOSEPOLY; - return Py_BuildValue("NN", vertices.pyobj(), codes.pyobj()); -} - -FT2Font::FT2Font(FT_Open_Args &open_args, - long hinting_factor_, - std::vector<FT2Font *> &fallback_list) - : image(), face(NULL) -{ - clear(); - - FT_Error error = FT_Open_Face(_ft2Library, &open_args, 0, &face); - if (error) { - throw_ft_error("Can not load face", error); - } - - // set default kerning factor to 0, i.e., no kerning manipulation - kerning_factor = 0; - - // set a default fontsize 12 pt at 72dpi - hinting_factor = hinting_factor_; - - error = FT_Set_Char_Size(face, 12 * 64, 0, 72 * (unsigned int)hinting_factor, 72); - if (error) { - FT_Done_Face(face); - throw_ft_error("Could not set the fontsize", error); - } - - if (open_args.stream != NULL) { - face->face_flags |= FT_FACE_FLAG_EXTERNAL_STREAM; - } - - FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; - FT_Set_Transform(face, &transform, 0); - - // Set fallbacks - std::copy(fallback_list.begin(), fallback_list.end(), std::back_inserter(fallbacks)); -} - -FT2Font::~FT2Font() -{ - for (size_t i = 0; i < glyphs.size(); i++) { - FT_Done_Glyph(glyphs[i]); - } - - if (face) { - FT_Done_Face(face); - } -} - -void FT2Font::clear() -{ - pen.x = 0; - pen.y = 0; - - for (size_t i = 0; i < glyphs.size(); i++) { - FT_Done_Glyph(glyphs[i]); - } - - glyphs.clear(); - glyph_to_font.clear(); - char_to_font.clear(); - - for (size_t i = 0; i < fallbacks.size(); i++) { - fallbacks[i]->clear(); - } -} - -void FT2Font::set_size(double ptsize, double dpi) -{ - FT_Error error = FT_Set_Char_Size( - face, (FT_F26Dot6)(ptsize * 64), 0, (FT_UInt)(dpi * hinting_factor), (FT_UInt)dpi); - if (error) { - throw_ft_error("Could not set the fontsize", error); - } - FT_Matrix transform = { 65536 / hinting_factor, 0, 0, 65536 }; - FT_Set_Transform(face, &transform, 0); - - for (size_t i = 0; i < fallbacks.size(); i++) { - fallbacks[i]->set_size(ptsize, dpi); - } -} - -void FT2Font::set_charmap(int i) -{ - if (i >= face->num_charmaps) { - throw std::runtime_error("i exceeds the available number of char maps"); - } - FT_CharMap charmap = face->charmaps[i]; - if (FT_Error error = FT_Set_Charmap(face, charmap)) { - throw_ft_error("Could not set the charmap", error); - } -} - -void FT2Font::select_charmap(unsigned long i) -{ - if (FT_Error error = FT_Select_Charmap(face, (FT_Encoding)i)) { - throw_ft_error("Could not set the charmap", error); - } -} - -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback = false) -{ - if (fallback && glyph_to_font.find(left) != glyph_to_font.end() && - glyph_to_font.find(right) != glyph_to_font.end()) { - FT2Font *left_ft_object = glyph_to_font[left]; - FT2Font *right_ft_object = glyph_to_font[right]; - if (left_ft_object != right_ft_object) { - // we do not know how to do kerning between different fonts - return 0; - } - // if left_ft_object is the same as right_ft_object, - // do the exact same thing which set_text does. - return right_ft_object->get_kerning(left, right, mode, false); - } - else - { - FT_Vector delta; - return get_kerning(left, right, mode, delta); - } -} - -int FT2Font::get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta) -{ - if (!FT_HAS_KERNING(face)) { - return 0; - } - - if (!FT_Get_Kerning(face, left, right, mode, &delta)) { - return (int)(delta.x) / (hinting_factor << kerning_factor); - } else { - return 0; - } -} - -void FT2Font::set_kerning_factor(int factor) -{ - kerning_factor = factor; - for (size_t i = 0; i < fallbacks.size(); i++) { - fallbacks[i]->set_kerning_factor(factor); - } -} - -void FT2Font::set_text( - size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys) -{ - FT_Matrix matrix; /* transformation matrix */ - - angle = angle * (2 * M_PI / 360.0); - - // this computes width and height in subpixels so we have to multiply by 64 - double cosangle = cos(angle) * 0x10000L; - double sinangle = sin(angle) * 0x10000L; - - matrix.xx = (FT_Fixed)cosangle; - matrix.xy = (FT_Fixed)-sinangle; - matrix.yx = (FT_Fixed)sinangle; - matrix.yy = (FT_Fixed)cosangle; - - clear(); - - bbox.xMin = bbox.yMin = 32000; - bbox.xMax = bbox.yMax = -32000; - - FT_UInt previous = 0; - FT2Font *previous_ft_object = NULL; - - for (size_t n = 0; n < N; n++) { - FT_UInt glyph_index = 0; - FT_BBox glyph_bbox; - FT_Pos last_advance; - - FT_Error charcode_error, glyph_error; - FT2Font *ft_object_with_glyph = this; - bool was_found = load_char_with_fallback(ft_object_with_glyph, glyph_index, glyphs, - char_to_font, glyph_to_font, codepoints[n], flags, - charcode_error, glyph_error, false); - if (!was_found) { - ft_glyph_warn((FT_ULong)codepoints[n]); - - // render missing glyph tofu - // come back to top-most font - ft_object_with_glyph = this; - char_to_font[codepoints[n]] = ft_object_with_glyph; - glyph_to_font[glyph_index] = ft_object_with_glyph; - ft_object_with_glyph->load_glyph(glyph_index, flags, ft_object_with_glyph, false); - } - - // retrieve kerning distance and move pen position - if ((ft_object_with_glyph == previous_ft_object) && // if both fonts are the same - ft_object_with_glyph->has_kerning() && // if the font knows how to kern - previous && glyph_index // and we really have 2 glyphs - ) { - FT_Vector delta; - pen.x += ft_object_with_glyph->get_kerning(previous, glyph_index, FT_KERNING_DEFAULT, delta); - } - - // extract glyph image and store it in our table - FT_Glyph &thisGlyph = glyphs[glyphs.size() - 1]; - - last_advance = ft_object_with_glyph->get_face()->glyph->advance.x; - FT_Glyph_Transform(thisGlyph, 0, &pen); - FT_Glyph_Transform(thisGlyph, &matrix, 0); - xys.push_back(pen.x); - xys.push_back(pen.y); - - FT_Glyph_Get_CBox(thisGlyph, FT_GLYPH_BBOX_SUBPIXELS, &glyph_bbox); - - bbox.xMin = std::min(bbox.xMin, glyph_bbox.xMin); - bbox.xMax = std::max(bbox.xMax, glyph_bbox.xMax); - bbox.yMin = std::min(bbox.yMin, glyph_bbox.yMin); - bbox.yMax = std::max(bbox.yMax, glyph_bbox.yMax); - - pen.x += last_advance; - - previous = glyph_index; - previous_ft_object = ft_object_with_glyph; - - } - - FT_Vector_Transform(&pen, &matrix); - advance = pen.x; - - if (bbox.xMin > bbox.xMax) { - bbox.xMin = bbox.yMin = bbox.xMax = bbox.yMax = 0; - } -} - -void FT2Font::load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback = false) -{ - // if this is parent FT2Font, cache will be filled in 2 ways: - // 1. set_text was previously called - // 2. set_text was not called and fallback was enabled - if (fallback && char_to_font.find(charcode) != char_to_font.end()) { - ft_object = char_to_font[charcode]; - // since it will be assigned to ft_object anyway - FT2Font *throwaway = NULL; - ft_object->load_char(charcode, flags, throwaway, false); - } else if (fallback) { - FT_UInt final_glyph_index; - FT_Error charcode_error, glyph_error; - FT2Font *ft_object_with_glyph = this; - bool was_found = load_char_with_fallback(ft_object_with_glyph, final_glyph_index, glyphs, char_to_font, - glyph_to_font, charcode, flags, charcode_error, glyph_error, true); - if (!was_found) { - ft_glyph_warn(charcode); - if (charcode_error) { - throw_ft_error("Could not load charcode", charcode_error); - } - else if (glyph_error) { - throw_ft_error("Could not load charcode", glyph_error); - } - } - ft_object = ft_object_with_glyph; - } else { - ft_object = this; - FT_UInt glyph_index = ft_get_char_index_or_warn(face, (FT_ULong)charcode); - - if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - throw_ft_error("Could not load charcode", error); - } - FT_Glyph thisGlyph; - if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - throw_ft_error("Could not get glyph", error); - } - glyphs.push_back(thisGlyph); - } -} - - -bool FT2Font::get_char_fallback_index(FT_ULong charcode, int& index) const -{ - FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - if (glyph_index) { - // -1 means the host has the char and we do not need to fallback - index = -1; - return true; - } else { - int inner_index = 0; - bool was_found; - - for (size_t i = 0; i < fallbacks.size(); ++i) { - // TODO handle recursion somehow! - was_found = fallbacks[i]->get_char_fallback_index(charcode, inner_index); - if (was_found) { - index = i; - return true; - } - } - } - return false; -} - - -bool FT2Font::load_char_with_fallback(FT2Font *&ft_object_with_glyph, - FT_UInt &final_glyph_index, - std::vector<FT_Glyph> &parent_glyphs, - std::unordered_map<long, FT2Font *> &parent_char_to_font, - std::unordered_map<FT_UInt, FT2Font *> &parent_glyph_to_font, - long charcode, - FT_Int32 flags, - FT_Error &charcode_error, - FT_Error &glyph_error, - bool override = false) -{ - FT_UInt glyph_index = FT_Get_Char_Index(face, charcode); - - if (glyph_index || override) { - charcode_error = FT_Load_Glyph(face, glyph_index, flags); - if (charcode_error) { - return false; - } - - FT_Glyph thisGlyph; - glyph_error = FT_Get_Glyph(face->glyph, &thisGlyph); - if (glyph_error) { - return false; - } - - final_glyph_index = glyph_index; - - // cache the result for future - // need to store this for anytime a character is loaded from a parent - // FT2Font object or to generate a mapping of individual characters to fonts - ft_object_with_glyph = this; - parent_glyph_to_font[final_glyph_index] = this; - parent_char_to_font[charcode] = this; - parent_glyphs.push_back(thisGlyph); - return true; - } - - else { - for (size_t i = 0; i < fallbacks.size(); ++i) { - bool was_found = fallbacks[i]->load_char_with_fallback( - ft_object_with_glyph, final_glyph_index, parent_glyphs, parent_char_to_font, - parent_glyph_to_font, charcode, flags, charcode_error, glyph_error, override); - if (was_found) { - return true; - } - } - return false; - } -} - -void FT2Font::load_glyph(FT_UInt glyph_index, - FT_Int32 flags, - FT2Font *&ft_object, - bool fallback = false) -{ - // cache is only for parent FT2Font - if (fallback && glyph_to_font.find(glyph_index) != glyph_to_font.end()) { - ft_object = glyph_to_font[glyph_index]; - } else { - ft_object = this; - } - - ft_object->load_glyph(glyph_index, flags); -} - -void FT2Font::load_glyph(FT_UInt glyph_index, FT_Int32 flags) -{ - if (FT_Error error = FT_Load_Glyph(face, glyph_index, flags)) { - throw_ft_error("Could not load glyph", error); - } - FT_Glyph thisGlyph; - if (FT_Error error = FT_Get_Glyph(face->glyph, &thisGlyph)) { - throw_ft_error("Could not get glyph", error); - } - glyphs.push_back(thisGlyph); -} - -FT_UInt FT2Font::get_char_index(FT_ULong charcode, bool fallback = false) -{ - FT2Font *ft_object = NULL; - if (fallback && char_to_font.find(charcode) != char_to_font.end()) { - // fallback denotes whether we want to search fallback list. - // should call set_text/load_char_with_fallback to parent FT2Font before - // wanting to use fallback list here. (since that populates the cache) - ft_object = char_to_font[charcode]; - } else { - // set as self - ft_object = this; - } - - // historically, get_char_index never raises a warning - return ft_get_char_index_or_warn(ft_object->get_face(), charcode, false); -} - -void FT2Font::get_width_height(long *width, long *height) -{ - *width = advance; - *height = bbox.yMax - bbox.yMin; -} - -long FT2Font::get_descent() -{ - return -bbox.yMin; -} - -void FT2Font::get_bitmap_offset(long *x, long *y) -{ - *x = bbox.xMin; - *y = 0; -} - -void FT2Font::draw_glyphs_to_bitmap(bool antialiased) -{ - long width = (bbox.xMax - bbox.xMin) / 64 + 2; - long height = (bbox.yMax - bbox.yMin) / 64 + 2; - - image.resize(width, height); - - for (size_t n = 0; n < glyphs.size(); n++) { - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } - - FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n]; - // now, draw to our target surface (convert position) - - // bitmap left and top in pixel, string bbox in subpixel - FT_Int x = (FT_Int)(bitmap->left - (bbox.xMin * (1. / 64.))); - FT_Int y = (FT_Int)((bbox.yMax * (1. / 64.)) - bitmap->top + 1); - - image.draw_bitmap(&bitmap->bitmap, x, y); - } -} - -void FT2Font::get_xys(bool antialiased, std::vector<double> &xys) -{ - for (size_t n = 0; n < glyphs.size(); n++) { - - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[n], antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, 0, 1); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } - - FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n]; - - // bitmap left and top in pixel, string bbox in subpixel - FT_Int x = (FT_Int)(bitmap->left - bbox.xMin * (1. / 64.)); - FT_Int y = (FT_Int)(bbox.yMax * (1. / 64.) - bitmap->top + 1); - // make sure the index is non-neg - x = x < 0 ? 0 : x; - y = y < 0 ? 0 : y; - xys.push_back(x); - xys.push_back(y); - } -} - -void FT2Font::draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased) -{ - FT_Vector sub_offset; - sub_offset.x = 0; // int((xd - (double)x) * 64.0); - sub_offset.y = 0; // int((yd - (double)y) * 64.0); - - if (glyphInd >= glyphs.size()) { - throw std::runtime_error("glyph num is out of range"); - } - - FT_Error error = FT_Glyph_To_Bitmap( - &glyphs[glyphInd], - antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, - &sub_offset, // additional translation - 1 // destroy image - ); - if (error) { - throw_ft_error("Could not convert glyph to bitmap", error); - } - - FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyphInd]; - - im.draw_bitmap(&bitmap->bitmap, x + bitmap->left, y); -} - -void FT2Font::get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback = false) -{ - if (fallback && glyph_to_font.find(glyph_number) != glyph_to_font.end()) { - // cache is only for parent FT2Font - FT2Font *ft_object = glyph_to_font[glyph_number]; - ft_object->get_glyph_name(glyph_number, buffer, false); - return; - } - if (!FT_HAS_GLYPH_NAMES(face)) { - /* Note that this generated name must match the name that - is generated by ttconv in ttfont_CharStrings_getname. */ - PyOS_snprintf(buffer, 128, "uni%08x", glyph_number); - } else { - if (FT_Error error = FT_Get_Glyph_Name(face, glyph_number, buffer, 128)) { - throw_ft_error("Could not get glyph names", error); - } - } -} - -long FT2Font::get_name_index(char *name) -{ - return FT_Get_Name_Index(face, (FT_String *)name); -} diff --git a/contrib/python/matplotlib/py3/src/ft2font.h b/contrib/python/matplotlib/py3/src/ft2font.h deleted file mode 100644 index d566c3f9bd..0000000000 --- a/contrib/python/matplotlib/py3/src/ft2font.h +++ /dev/null @@ -1,159 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* A python interface to FreeType */ -#pragma once -#ifndef MPL_FT2FONT_H -#define MPL_FT2FONT_H -#include <vector> -#include <stdint.h> -#include <unordered_map> - -extern "C" { -#include <ft2build.h> -#include FT_FREETYPE_H -#include FT_GLYPH_H -#include FT_OUTLINE_H -#include FT_SFNT_NAMES_H -#include FT_TYPE1_TABLES_H -#include FT_TRUETYPE_TABLES_H -} - -#define PY_SSIZE_T_CLEAN -#include <Python.h> - -/* - By definition, FT_FIXED as 2 16bit values stored in a single long. - */ -#define FIXED_MAJOR(val) (signed short)((val & 0xffff0000) >> 16) -#define FIXED_MINOR(val) (unsigned short)(val & 0xffff) - -// the FreeType string rendered into a width, height buffer -class FT2Image -{ - public: - FT2Image(); - FT2Image(unsigned long width, unsigned long height); - virtual ~FT2Image(); - - void resize(long width, long height); - void draw_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y); - void draw_rect(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1); - void draw_rect_filled(unsigned long x0, unsigned long y0, unsigned long x1, unsigned long y1); - - unsigned char *get_buffer() - { - return m_buffer; - } - unsigned long get_width() - { - return m_width; - } - unsigned long get_height() - { - return m_height; - } - - private: - bool m_dirty; - unsigned char *m_buffer; - unsigned long m_width; - unsigned long m_height; - - // prevent copying - FT2Image(const FT2Image &); - FT2Image &operator=(const FT2Image &); -}; - -extern FT_Library _ft2Library; - -class FT2Font -{ - - public: - FT2Font(FT_Open_Args &open_args, long hinting_factor, std::vector<FT2Font *> &fallback_list); - virtual ~FT2Font(); - void clear(); - void set_size(double ptsize, double dpi); - void set_charmap(int i); - void select_charmap(unsigned long i); - void set_text( - size_t N, uint32_t *codepoints, double angle, FT_Int32 flags, std::vector<double> &xys); - int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, bool fallback); - int get_kerning(FT_UInt left, FT_UInt right, FT_UInt mode, FT_Vector &delta); - void set_kerning_factor(int factor); - void load_char(long charcode, FT_Int32 flags, FT2Font *&ft_object, bool fallback); - bool load_char_with_fallback(FT2Font *&ft_object_with_glyph, - FT_UInt &final_glyph_index, - std::vector<FT_Glyph> &parent_glyphs, - std::unordered_map<long, FT2Font *> &parent_char_to_font, - std::unordered_map<FT_UInt, FT2Font *> &parent_glyph_to_font, - long charcode, - FT_Int32 flags, - FT_Error &charcode_error, - FT_Error &glyph_error, - bool override); - void load_glyph(FT_UInt glyph_index, FT_Int32 flags, FT2Font *&ft_object, bool fallback); - void load_glyph(FT_UInt glyph_index, FT_Int32 flags); - void get_width_height(long *width, long *height); - void get_bitmap_offset(long *x, long *y); - long get_descent(); - // TODO: Since we know the size of the array upfront, we probably don't - // need to dynamically allocate like this - void get_xys(bool antialiased, std::vector<double> &xys); - void draw_glyphs_to_bitmap(bool antialiased); - void draw_glyph_to_bitmap(FT2Image &im, int x, int y, size_t glyphInd, bool antialiased); - void get_glyph_name(unsigned int glyph_number, char *buffer, bool fallback); - long get_name_index(char *name); - FT_UInt get_char_index(FT_ULong charcode, bool fallback); - PyObject* get_path(); - bool get_char_fallback_index(FT_ULong charcode, int& index) const; - - FT_Face const &get_face() const - { - return face; - } - - FT2Image &get_image() - { - return image; - } - FT_Glyph const &get_last_glyph() const - { - return glyphs.back(); - } - size_t get_last_glyph_index() const - { - return glyphs.size() - 1; - } - size_t get_num_glyphs() const - { - return glyphs.size(); - } - long get_hinting_factor() const - { - return hinting_factor; - } - FT_Bool has_kerning() const - { - return FT_HAS_KERNING(face); - } - - private: - FT2Image image; - FT_Face face; - FT_Vector pen; /* untransformed origin */ - std::vector<FT_Glyph> glyphs; - std::vector<FT2Font *> fallbacks; - std::unordered_map<FT_UInt, FT2Font *> glyph_to_font; - std::unordered_map<long, FT2Font *> char_to_font; - FT_BBox bbox; - FT_Pos advance; - long hinting_factor; - int kerning_factor; - - // prevent copying - FT2Font(const FT2Font &); - FT2Font &operator=(const FT2Font &); -}; - -#endif diff --git a/contrib/python/matplotlib/py3/src/ft2font_wrapper.cpp b/contrib/python/matplotlib/py3/src/ft2font_wrapper.cpp deleted file mode 100644 index 7888a9c212..0000000000 --- a/contrib/python/matplotlib/py3/src/ft2font_wrapper.cpp +++ /dev/null @@ -1,1578 +0,0 @@ -#include "mplutils.h" -#include "ft2font.h" -#include "py_converters.h" -#include "py_exceptions.h" -#include "numpy_cpp.h" - -// From Python -#include <structmember.h> - -#include <set> -#include <algorithm> - -#define STRINGIFY(s) XSTRINGIFY(s) -#define XSTRINGIFY(s) #s - -static PyObject *convert_xys_to_array(std::vector<double> &xys) -{ - npy_intp dims[] = {(npy_intp)xys.size() / 2, 2 }; - if (dims[0] > 0) { - return PyArray_SimpleNewFromData(2, dims, NPY_DOUBLE, &xys[0]); - } else { - return PyArray_SimpleNew(2, dims, NPY_DOUBLE); - } -} - -/********************************************************************** - * FT2Image - * */ - -typedef struct -{ - PyObject_HEAD - FT2Image *x; - Py_ssize_t shape[2]; - Py_ssize_t strides[2]; - Py_ssize_t suboffsets[2]; -} PyFT2Image; - -static PyTypeObject PyFT2ImageType; - -static PyObject *PyFT2Image_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyFT2Image *self; - self = (PyFT2Image *)type->tp_alloc(type, 0); - self->x = NULL; - return (PyObject *)self; -} - -static int PyFT2Image_init(PyFT2Image *self, PyObject *args, PyObject *kwds) -{ - double width; - double height; - - if (!PyArg_ParseTuple(args, "dd:FT2Image", &width, &height)) { - return -1; - } - - CALL_CPP_INIT("FT2Image", (self->x = new FT2Image(width, height))); - - return 0; -} - -static void PyFT2Image_dealloc(PyFT2Image *self) -{ - delete self->x; - Py_TYPE(self)->tp_free((PyObject *)self); -} - -const char *PyFT2Image_draw_rect__doc__ = - "draw_rect(self, x0, y0, x1, y1)\n" - "--\n\n" - "Draw an empty rectangle to the image.\n" - "\n" - ".. deprecated:: 3.8\n"; -; - -static PyObject *PyFT2Image_draw_rect(PyFT2Image *self, PyObject *args) -{ - char const* msg = - "FT2Image.draw_rect is deprecated since Matplotlib 3.8 and will be removed " - "two minor releases later as it is not used in the library. If you rely on " - "it, please let us know."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - - double x0, y0, x1, y1; - - if (!PyArg_ParseTuple(args, "dddd:draw_rect", &x0, &y0, &x1, &y1)) { - return NULL; - } - - CALL_CPP("draw_rect", (self->x->draw_rect(x0, y0, x1, y1))); - - Py_RETURN_NONE; -} - -const char *PyFT2Image_draw_rect_filled__doc__ = - "draw_rect_filled(self, x0, y0, x1, y1)\n" - "--\n\n" - "Draw a filled rectangle to the image.\n"; - -static PyObject *PyFT2Image_draw_rect_filled(PyFT2Image *self, PyObject *args) -{ - double x0, y0, x1, y1; - - if (!PyArg_ParseTuple(args, "dddd:draw_rect_filled", &x0, &y0, &x1, &y1)) { - return NULL; - } - - CALL_CPP("draw_rect_filled", (self->x->draw_rect_filled(x0, y0, x1, y1))); - - Py_RETURN_NONE; -} - -static int PyFT2Image_get_buffer(PyFT2Image *self, Py_buffer *buf, int flags) -{ - FT2Image *im = self->x; - - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = im->get_buffer(); - buf->len = im->get_width() * im->get_height(); - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 2; - self->shape[0] = im->get_height(); - self->shape[1] = im->get_width(); - buf->shape = self->shape; - self->strides[0] = im->get_width(); - self->strides[1] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; -} - -static PyTypeObject* PyFT2Image_init_type() -{ - static PyMethodDef methods[] = { - {"draw_rect", (PyCFunction)PyFT2Image_draw_rect, METH_VARARGS, PyFT2Image_draw_rect__doc__}, - {"draw_rect_filled", (PyCFunction)PyFT2Image_draw_rect_filled, METH_VARARGS, PyFT2Image_draw_rect_filled__doc__}, - {NULL} - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyFT2Image_get_buffer; - - PyFT2ImageType.tp_name = "matplotlib.ft2font.FT2Image"; - PyFT2ImageType.tp_basicsize = sizeof(PyFT2Image); - PyFT2ImageType.tp_dealloc = (destructor)PyFT2Image_dealloc; - PyFT2ImageType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyFT2ImageType.tp_methods = methods; - PyFT2ImageType.tp_new = PyFT2Image_new; - PyFT2ImageType.tp_init = (initproc)PyFT2Image_init; - PyFT2ImageType.tp_as_buffer = &buffer_procs; - - return &PyFT2ImageType; -} - -/********************************************************************** - * Glyph - * */ - -typedef struct -{ - PyObject_HEAD - size_t glyphInd; - long width; - long height; - long horiBearingX; - long horiBearingY; - long horiAdvance; - long linearHoriAdvance; - long vertBearingX; - long vertBearingY; - long vertAdvance; - FT_BBox bbox; -} PyGlyph; - -static PyTypeObject PyGlyphType; - -static PyObject *PyGlyph_from_FT2Font(const FT2Font *font) -{ - const FT_Face &face = font->get_face(); - const long hinting_factor = font->get_hinting_factor(); - const FT_Glyph &glyph = font->get_last_glyph(); - - PyGlyph *self; - self = (PyGlyph *)PyGlyphType.tp_alloc(&PyGlyphType, 0); - - self->glyphInd = font->get_last_glyph_index(); - FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_subpixels, &self->bbox); - - self->width = face->glyph->metrics.width / hinting_factor; - self->height = face->glyph->metrics.height; - self->horiBearingX = face->glyph->metrics.horiBearingX / hinting_factor; - self->horiBearingY = face->glyph->metrics.horiBearingY; - self->horiAdvance = face->glyph->metrics.horiAdvance; - self->linearHoriAdvance = face->glyph->linearHoriAdvance / hinting_factor; - self->vertBearingX = face->glyph->metrics.vertBearingX; - self->vertBearingY = face->glyph->metrics.vertBearingY; - self->vertAdvance = face->glyph->metrics.vertAdvance; - - return (PyObject *)self; -} - -static void PyGlyph_dealloc(PyGlyph *self) -{ - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static PyObject *PyGlyph_get_bbox(PyGlyph *self, void *closure) -{ - return Py_BuildValue( - "llll", self->bbox.xMin, self->bbox.yMin, self->bbox.xMax, self->bbox.yMax); -} - -static PyTypeObject *PyGlyph_init_type() -{ - static PyMemberDef members[] = { - {(char *)"width", T_LONG, offsetof(PyGlyph, width), READONLY, (char *)""}, - {(char *)"height", T_LONG, offsetof(PyGlyph, height), READONLY, (char *)""}, - {(char *)"horiBearingX", T_LONG, offsetof(PyGlyph, horiBearingX), READONLY, (char *)""}, - {(char *)"horiBearingY", T_LONG, offsetof(PyGlyph, horiBearingY), READONLY, (char *)""}, - {(char *)"horiAdvance", T_LONG, offsetof(PyGlyph, horiAdvance), READONLY, (char *)""}, - {(char *)"linearHoriAdvance", T_LONG, offsetof(PyGlyph, linearHoriAdvance), READONLY, (char *)""}, - {(char *)"vertBearingX", T_LONG, offsetof(PyGlyph, vertBearingX), READONLY, (char *)""}, - {(char *)"vertBearingY", T_LONG, offsetof(PyGlyph, vertBearingY), READONLY, (char *)""}, - {(char *)"vertAdvance", T_LONG, offsetof(PyGlyph, vertAdvance), READONLY, (char *)""}, - {NULL} - }; - - static PyGetSetDef getset[] = { - {(char *)"bbox", (getter)PyGlyph_get_bbox, NULL, NULL, NULL}, - {NULL} - }; - - PyGlyphType.tp_name = "matplotlib.ft2font.Glyph"; - PyGlyphType.tp_basicsize = sizeof(PyGlyph); - PyGlyphType.tp_dealloc = (destructor)PyGlyph_dealloc; - PyGlyphType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyGlyphType.tp_members = members; - PyGlyphType.tp_getset = getset; - - return &PyGlyphType; -} - -/********************************************************************** - * FT2Font - * */ - -struct PyFT2Font -{ - PyObject_HEAD - FT2Font *x; - PyObject *py_file; - FT_StreamRec stream; - Py_ssize_t shape[2]; - Py_ssize_t strides[2]; - Py_ssize_t suboffsets[2]; - std::vector<PyObject *> fallbacks; -}; - -static PyTypeObject PyFT2FontType; - -static unsigned long read_from_file_callback(FT_Stream stream, - unsigned long offset, - unsigned char *buffer, - unsigned long count) -{ - PyObject *py_file = ((PyFT2Font *)stream->descriptor.pointer)->py_file; - PyObject *seek_result = NULL, *read_result = NULL; - Py_ssize_t n_read = 0; - if (!(seek_result = PyObject_CallMethod(py_file, "seek", "k", offset)) - || !(read_result = PyObject_CallMethod(py_file, "read", "k", count))) { - goto exit; - } - char *tmpbuf; - if (PyBytes_AsStringAndSize(read_result, &tmpbuf, &n_read) == -1) { - goto exit; - } - memcpy(buffer, tmpbuf, n_read); -exit: - Py_XDECREF(seek_result); - Py_XDECREF(read_result); - if (PyErr_Occurred()) { - PyErr_WriteUnraisable(py_file); - if (!count) { - return 1; // Non-zero signals error, when count == 0. - } - } - return (unsigned long)n_read; -} - -static void close_file_callback(FT_Stream stream) -{ - PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); - PyFT2Font *self = (PyFT2Font *)stream->descriptor.pointer; - PyObject *close_result = NULL; - if (!(close_result = PyObject_CallMethod(self->py_file, "close", ""))) { - goto exit; - } -exit: - Py_XDECREF(close_result); - Py_CLEAR(self->py_file); - if (PyErr_Occurred()) { - PyErr_WriteUnraisable((PyObject*)self); - } - PyErr_Restore(type, value, traceback); -} - -static PyObject *PyFT2Font_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyFT2Font *self; - self = (PyFT2Font *)type->tp_alloc(type, 0); - self->x = NULL; - self->py_file = NULL; - memset(&self->stream, 0, sizeof(FT_StreamRec)); - return (PyObject *)self; -} - -const char *PyFT2Font_init__doc__ = - "FT2Font(filename, hinting_factor=8, *, _fallback_list=None, _kerning_factor=0)\n" - "--\n\n" - "Create a new FT2Font object.\n" - "\n" - "Parameters\n" - "----------\n" - "filename : str or file-like\n" - " The source of the font data in a format (ttf or ttc) that FreeType can read\n" - "\n" - "hinting_factor : int, optional\n" - " Must be positive. Used to scale the hinting in the x-direction\n" - "_fallback_list : list of FT2Font, optional\n" - " A list of FT2Font objects used to find missing glyphs.\n" - "\n" - " .. warning::\n" - " This API is both private and provisional: do not use it directly\n" - "\n" - "_kerning_factor : int, optional\n" - " Used to adjust the degree of kerning.\n" - "\n" - " .. warning::\n" - " This API is private: do not use it directly\n" - "\n" - "Attributes\n" - "----------\n" - "num_faces : int\n" - " Number of faces in file.\n" - "face_flags, style_flags : int\n" - " Face and style flags; see the ft2font constants.\n" - "num_glyphs : int\n" - " Number of glyphs in the face.\n" - "family_name, style_name : str\n" - " Face family and style name.\n" - "num_fixed_sizes : int\n" - " Number of bitmap in the face.\n" - "scalable : bool\n" - " Whether face is scalable; attributes after this one are only\n" - " defined for scalable faces.\n" - "bbox : tuple[int, int, int, int]\n" - " Face global bounding box (xmin, ymin, xmax, ymax).\n" - "units_per_EM : int\n" - " Number of font units covered by the EM.\n" - "ascender, descender : int\n" - " Ascender and descender in 26.6 units.\n" - "height : int\n" - " Height in 26.6 units; used to compute a default line spacing\n" - " (baseline-to-baseline distance).\n" - "max_advance_width, max_advance_height : int\n" - " Maximum horizontal and vertical cursor advance for all glyphs.\n" - "underline_position, underline_thickness : int\n" - " Vertical position and thickness of the underline bar.\n" - "postscript_name : str\n" - " PostScript name of the font.\n"; - -static int PyFT2Font_init(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyObject *filename = NULL, *open = NULL, *data = NULL, *fallback_list = NULL; - FT_Open_Args open_args; - long hinting_factor = 8; - int kerning_factor = 0; - const char *names[] = { - "filename", "hinting_factor", "_fallback_list", "_kerning_factor", NULL - }; - std::vector<FT2Font *> fallback_fonts; - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O|l$Oi:FT2Font", (char **)names, &filename, - &hinting_factor, &fallback_list, &kerning_factor)) { - return -1; - } - if (hinting_factor <= 0) { - PyErr_SetString(PyExc_ValueError, - "hinting_factor must be greater than 0"); - goto exit; - } - - self->stream.base = NULL; - self->stream.size = 0x7fffffff; // Unknown size. - self->stream.pos = 0; - self->stream.descriptor.pointer = self; - self->stream.read = &read_from_file_callback; - memset((void *)&open_args, 0, sizeof(FT_Open_Args)); - open_args.flags = FT_OPEN_STREAM; - open_args.stream = &self->stream; - - if (fallback_list) { - if (!PyList_Check(fallback_list)) { - PyErr_SetString(PyExc_TypeError, "Fallback list must be a list"); - goto exit; - } - Py_ssize_t size = PyList_Size(fallback_list); - - // go through fallbacks once to make sure the types are right - for (Py_ssize_t i = 0; i < size; ++i) { - // this returns a borrowed reference - PyObject* item = PyList_GetItem(fallback_list, i); - if (!PyObject_IsInstance(item, PyObject_Type(reinterpret_cast<PyObject *>(self)))) { - PyErr_SetString(PyExc_TypeError, "Fallback fonts must be FT2Font objects."); - goto exit; - } - } - // go through a second time to add them to our lists - for (Py_ssize_t i = 0; i < size; ++i) { - // this returns a borrowed reference - PyObject* item = PyList_GetItem(fallback_list, i); - // Increase the ref count, we will undo this in dealloc this makes - // sure things do not get gc'd under us! - Py_INCREF(item); - self->fallbacks.push_back(item); - // Also (locally) cache the underlying FT2Font objects. As long as - // the Python objects are kept alive, these pointer are good. - FT2Font *fback = reinterpret_cast<PyFT2Font *>(item)->x; - fallback_fonts.push_back(fback); - } - } - - if (PyBytes_Check(filename) || PyUnicode_Check(filename)) { - if (!(open = PyDict_GetItemString(PyEval_GetBuiltins(), "open")) // Borrowed reference. - || !(self->py_file = PyObject_CallFunction(open, "Os", filename, "rb"))) { - goto exit; - } - self->stream.close = &close_file_callback; - } else if (!PyObject_HasAttrString(filename, "read") - || !(data = PyObject_CallMethod(filename, "read", "i", 0)) - || !PyBytes_Check(data)) { - PyErr_SetString(PyExc_TypeError, - "First argument must be a path to a font file or a binary-mode file object"); - Py_CLEAR(data); - goto exit; - } else { - self->py_file = filename; - self->stream.close = NULL; - Py_INCREF(filename); - } - Py_CLEAR(data); - - CALL_CPP_FULL( - "FT2Font", (self->x = new FT2Font(open_args, hinting_factor, fallback_fonts)), - Py_CLEAR(self->py_file), -1); - - CALL_CPP_INIT("FT2Font->set_kerning_factor", (self->x->set_kerning_factor(kerning_factor))); - -exit: - return PyErr_Occurred() ? -1 : 0; -} - -static void PyFT2Font_dealloc(PyFT2Font *self) -{ - delete self->x; - for (size_t i = 0; i < self->fallbacks.size(); i++) { - Py_DECREF(self->fallbacks[i]); - } - - Py_XDECREF(self->py_file); - Py_TYPE(self)->tp_free((PyObject *)self); -} - -const char *PyFT2Font_clear__doc__ = - "clear(self)\n" - "--\n\n" - "Clear all the glyphs, reset for a new call to `.set_text`.\n"; - -static PyObject *PyFT2Font_clear(PyFT2Font *self, PyObject *args) -{ - CALL_CPP("clear", (self->x->clear())); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_set_size__doc__ = - "set_size(self, ptsize, dpi)\n" - "--\n\n" - "Set the point size and dpi of the text.\n"; - -static PyObject *PyFT2Font_set_size(PyFT2Font *self, PyObject *args) -{ - double ptsize; - double dpi; - - if (!PyArg_ParseTuple(args, "dd:set_size", &ptsize, &dpi)) { - return NULL; - } - - CALL_CPP("set_size", (self->x->set_size(ptsize, dpi))); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_set_charmap__doc__ = - "set_charmap(self, i)\n" - "--\n\n" - "Make the i-th charmap current.\n"; - -static PyObject *PyFT2Font_set_charmap(PyFT2Font *self, PyObject *args) -{ - int i; - - if (!PyArg_ParseTuple(args, "i:set_charmap", &i)) { - return NULL; - } - - CALL_CPP("set_charmap", (self->x->set_charmap(i))); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_select_charmap__doc__ = - "select_charmap(self, i)\n" - "--\n\n" - "Select a charmap by its FT_Encoding number.\n"; - -static PyObject *PyFT2Font_select_charmap(PyFT2Font *self, PyObject *args) -{ - unsigned long i; - - if (!PyArg_ParseTuple(args, "k:select_charmap", &i)) { - return NULL; - } - - CALL_CPP("select_charmap", self->x->select_charmap(i)); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_get_kerning__doc__ = - "get_kerning(self, left, right, mode)\n" - "--\n\n" - "Get the kerning between *left* and *right* glyph indices.\n" - "*mode* is a kerning mode constant:\n\n" - "- KERNING_DEFAULT - Return scaled and grid-fitted kerning distances\n" - "- KERNING_UNFITTED - Return scaled but un-grid-fitted kerning distances\n" - "- KERNING_UNSCALED - Return the kerning vector in original font units\n"; - -static PyObject *PyFT2Font_get_kerning(PyFT2Font *self, PyObject *args) -{ - FT_UInt left, right, mode; - int result; - int fallback = 1; - - if (!PyArg_ParseTuple(args, "III:get_kerning", &left, &right, &mode)) { - return NULL; - } - - CALL_CPP("get_kerning", (result = self->x->get_kerning(left, right, mode, (bool)fallback))); - - return PyLong_FromLong(result); -} - -const char *PyFT2Font_get_fontmap__doc__ = - "_get_fontmap(self, string)\n" - "--\n\n" - "Get a mapping between characters and the font that includes them.\n" - "A dictionary mapping unicode characters to PyFT2Font objects."; -static PyObject *PyFT2Font_get_fontmap(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyObject *textobj; - const char *names[] = { "string", NULL }; - - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O:_get_fontmap", (char **)names, &textobj)) { - return NULL; - } - - std::set<FT_ULong> codepoints; - size_t size; - - if (PyUnicode_Check(textobj)) { - size = PyUnicode_GET_LENGTH(textobj); - for (size_t i = 0; i < size; ++i) { - codepoints.insert(PyUnicode_ReadChar(textobj, i)); - } - } else { - PyErr_SetString(PyExc_TypeError, "string must be str"); - return NULL; - } - PyObject *char_to_font; - if (!(char_to_font = PyDict_New())) { - return NULL; - } - for (auto it = codepoints.begin(); it != codepoints.end(); ++it) { - auto x = *it; - PyObject* target_font; - int index; - if (self->x->get_char_fallback_index(x, index)) { - if (index >= 0) { - target_font = self->fallbacks[index]; - } else { - target_font = (PyObject *)self; - } - } else { - // TODO Handle recursion! - target_font = (PyObject *)self; - } - - PyObject *key = NULL; - bool error = (!(key = PyUnicode_FromFormat("%c", x)) - || (PyDict_SetItem(char_to_font, key, target_font) == -1)); - Py_XDECREF(key); - if (error) { - Py_DECREF(char_to_font); - PyErr_SetString(PyExc_ValueError, "Something went very wrong"); - return NULL; - } - } - return char_to_font; -} - - -const char *PyFT2Font_set_text__doc__ = - "set_text(self, string, angle=0.0, flags=32)\n" - "--\n\n" - "Set the text *string* and *angle*.\n" - "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" - "the default value is LOAD_FORCE_AUTOHINT.\n" - "You must call this before `.draw_glyphs_to_bitmap`.\n" - "A sequence of x,y positions is returned.\n"; - -static PyObject *PyFT2Font_set_text(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyObject *textobj; - double angle = 0.0; - FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - std::vector<double> xys; - const char *names[] = { "string", "angle", "flags", NULL }; - - /* This makes a technically incorrect assumption that FT_Int32 is - int. In theory it can also be long, if the size of int is less - than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords( - args, kwds, "O|di:set_text", (char **)names, &textobj, &angle, &flags)) { - return NULL; - } - - std::vector<uint32_t> codepoints; - size_t size; - - if (PyUnicode_Check(textobj)) { - size = PyUnicode_GET_LENGTH(textobj); - codepoints.resize(size); - for (size_t i = 0; i < size; ++i) { - codepoints[i] = PyUnicode_ReadChar(textobj, i); - } - } else { - PyErr_SetString(PyExc_TypeError, "set_text requires str-input."); - return NULL; - } - - uint32_t* codepoints_array = NULL; - if (size > 0) { - codepoints_array = &codepoints[0]; - } - CALL_CPP("set_text", self->x->set_text(size, codepoints_array, angle, flags, xys)); - - return convert_xys_to_array(xys); -} - -const char *PyFT2Font_get_num_glyphs__doc__ = - "get_num_glyphs(self)\n" - "--\n\n" - "Return the number of loaded glyphs.\n"; - -static PyObject *PyFT2Font_get_num_glyphs(PyFT2Font *self, PyObject *args) -{ - return PyLong_FromSize_t(self->x->get_num_glyphs()); -} - -const char *PyFT2Font_load_char__doc__ = - "load_char(self, charcode, flags=32)\n" - "--\n\n" - "Load character with *charcode* in current fontfile and set glyph.\n" - "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" - "the default value is LOAD_FORCE_AUTOHINT.\n" - "Return value is a Glyph object, with attributes\n\n" - "- width: glyph width\n" - "- height: glyph height\n" - "- bbox: the glyph bbox (xmin, ymin, xmax, ymax)\n" - "- horiBearingX: left side bearing in horizontal layouts\n" - "- horiBearingY: top side bearing in horizontal layouts\n" - "- horiAdvance: advance width for horizontal layout\n" - "- vertBearingX: left side bearing in vertical layouts\n" - "- vertBearingY: top side bearing in vertical layouts\n" - "- vertAdvance: advance height for vertical layout\n"; - -static PyObject *PyFT2Font_load_char(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - long charcode; - int fallback = 1; - FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - const char *names[] = { "charcode", "flags", NULL }; - - /* This makes a technically incorrect assumption that FT_Int32 is - int. In theory it can also be long, if the size of int is less - than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "l|i:load_char", (char **)names, &charcode, - &flags)) { - return NULL; - } - - FT2Font *ft_object = NULL; - CALL_CPP("load_char", (self->x->load_char(charcode, flags, ft_object, (bool)fallback))); - - return PyGlyph_from_FT2Font(ft_object); -} - -const char *PyFT2Font_load_glyph__doc__ = - "load_glyph(self, glyphindex, flags=32)\n" - "--\n\n" - "Load character with *glyphindex* in current fontfile and set glyph.\n" - "*flags* can be a bitwise-or of the LOAD_XXX constants;\n" - "the default value is LOAD_FORCE_AUTOHINT.\n" - "Return value is a Glyph object, with attributes\n\n" - "- width: glyph width\n" - "- height: glyph height\n" - "- bbox: the glyph bbox (xmin, ymin, xmax, ymax)\n" - "- horiBearingX: left side bearing in horizontal layouts\n" - "- horiBearingY: top side bearing in horizontal layouts\n" - "- horiAdvance: advance width for horizontal layout\n" - "- vertBearingX: left side bearing in vertical layouts\n" - "- vertBearingY: top side bearing in vertical layouts\n" - "- vertAdvance: advance height for vertical layout\n"; - -static PyObject *PyFT2Font_load_glyph(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - FT_UInt glyph_index; - FT_Int32 flags = FT_LOAD_FORCE_AUTOHINT; - int fallback = 1; - const char *names[] = { "glyph_index", "flags", NULL }; - - /* This makes a technically incorrect assumption that FT_Int32 is - int. In theory it can also be long, if the size of int is less - than 32 bits. This is very unlikely on modern platforms. */ - if (!PyArg_ParseTupleAndKeywords(args, kwds, "I|i:load_glyph", (char **)names, &glyph_index, - &flags)) { - return NULL; - } - - FT2Font *ft_object = NULL; - CALL_CPP("load_glyph", (self->x->load_glyph(glyph_index, flags, ft_object, (bool)fallback))); - - return PyGlyph_from_FT2Font(ft_object); -} - -const char *PyFT2Font_get_width_height__doc__ = - "get_width_height(self)\n" - "--\n\n" - "Get the width and height in 26.6 subpixels of the current string set by `.set_text`.\n" - "The rotation of the string is accounted for. To get width and height\n" - "in pixels, divide these values by 64.\n"; - -static PyObject *PyFT2Font_get_width_height(PyFT2Font *self, PyObject *args) -{ - long width, height; - - CALL_CPP("get_width_height", (self->x->get_width_height(&width, &height))); - - return Py_BuildValue("ll", width, height); -} - -const char *PyFT2Font_get_bitmap_offset__doc__ = - "get_bitmap_offset(self)\n" - "--\n\n" - "Get the (x, y) offset in 26.6 subpixels for the bitmap if ink hangs left or below (0, 0).\n" - "Since Matplotlib only supports left-to-right text, y is always 0.\n"; - -static PyObject *PyFT2Font_get_bitmap_offset(PyFT2Font *self, PyObject *args) -{ - long x, y; - - CALL_CPP("get_bitmap_offset", (self->x->get_bitmap_offset(&x, &y))); - - return Py_BuildValue("ll", x, y); -} - -const char *PyFT2Font_get_descent__doc__ = - "get_descent(self)\n" - "--\n\n" - "Get the descent in 26.6 subpixels of the current string set by `.set_text`.\n" - "The rotation of the string is accounted for. To get the descent\n" - "in pixels, divide this value by 64.\n"; - -static PyObject *PyFT2Font_get_descent(PyFT2Font *self, PyObject *args) -{ - long descent; - - CALL_CPP("get_descent", (descent = self->x->get_descent())); - - return PyLong_FromLong(descent); -} - -const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = - "draw_glyphs_to_bitmap(self, antialiased=True)\n" - "--\n\n" - "Draw the glyphs that were loaded by `.set_text` to the bitmap.\n" - "The bitmap size will be automatically set to include the glyphs.\n"; - -static PyObject *PyFT2Font_draw_glyphs_to_bitmap(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - bool antialiased = true; - const char *names[] = { "antialiased", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:draw_glyphs_to_bitmap", - (char **)names, &convert_bool, &antialiased)) { - return NULL; - } - - CALL_CPP("draw_glyphs_to_bitmap", (self->x->draw_glyphs_to_bitmap(antialiased))); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_get_xys__doc__ = - "get_xys(self, antialiased=True)\n" - "--\n\n" - "Get the xy locations of the current glyphs.\n" - "\n" - ".. deprecated:: 3.8\n"; - -static PyObject *PyFT2Font_get_xys(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - char const* msg = - "FT2Font.get_xys is deprecated since Matplotlib 3.8 and will be removed two " - "minor releases later as it is not used in the library. If you rely on it, " - "please let us know."; - if (PyErr_WarnEx(PyExc_DeprecationWarning, msg, 1)) { - return NULL; - } - - bool antialiased = true; - std::vector<double> xys; - const char *names[] = { "antialiased", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O&:get_xys", - (char **)names, &convert_bool, &antialiased)) { - return NULL; - } - - CALL_CPP("get_xys", (self->x->get_xys(antialiased, xys))); - - return convert_xys_to_array(xys); -} - -const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = - "draw_glyph_to_bitmap(self, image, x, y, glyph, antialiased=True)\n" - "--\n\n" - "Draw a single glyph to the bitmap at pixel locations x, y\n" - "Note it is your responsibility to set up the bitmap manually\n" - "with ``set_bitmap_size(w, h)`` before this call is made.\n" - "\n" - "If you want automatic layout, use `.set_text` in combinations with\n" - "`.draw_glyphs_to_bitmap`. This function is instead intended for people\n" - "who want to render individual glyphs (e.g., returned by `.load_char`)\n" - "at precise locations.\n"; - -static PyObject *PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, PyObject *args, PyObject *kwds) -{ - PyFT2Image *image; - double xd, yd; - PyGlyph *glyph; - bool antialiased = true; - const char *names[] = { "image", "x", "y", "glyph", "antialiased", NULL }; - - if (!PyArg_ParseTupleAndKeywords(args, - kwds, - "O!ddO!|O&:draw_glyph_to_bitmap", - (char **)names, - &PyFT2ImageType, - &image, - &xd, - &yd, - &PyGlyphType, - &glyph, - &convert_bool, - &antialiased)) { - return NULL; - } - - CALL_CPP("draw_glyph_to_bitmap", - self->x->draw_glyph_to_bitmap(*(image->x), xd, yd, glyph->glyphInd, antialiased)); - - Py_RETURN_NONE; -} - -const char *PyFT2Font_get_glyph_name__doc__ = - "get_glyph_name(self, index)\n" - "--\n\n" - "Retrieve the ASCII name of a given glyph *index* in a face.\n" - "\n" - "Due to Matplotlib's internal design, for fonts that do not contain glyph\n" - "names (per FT_FACE_FLAG_GLYPH_NAMES), this returns a made-up name which\n" - "does *not* roundtrip through `.get_name_index`.\n"; - -static PyObject *PyFT2Font_get_glyph_name(PyFT2Font *self, PyObject *args) -{ - unsigned int glyph_number; - char buffer[128]; - int fallback = 1; - - if (!PyArg_ParseTuple(args, "I:get_glyph_name", &glyph_number)) { - return NULL; - } - CALL_CPP("get_glyph_name", (self->x->get_glyph_name(glyph_number, buffer, (bool)fallback))); - return PyUnicode_FromString(buffer); -} - -const char *PyFT2Font_get_charmap__doc__ = - "get_charmap(self)\n" - "--\n\n" - "Return a dict that maps the character codes of the selected charmap\n" - "(Unicode by default) to their corresponding glyph indices.\n"; - -static PyObject *PyFT2Font_get_charmap(PyFT2Font *self, PyObject *args) -{ - PyObject *charmap; - if (!(charmap = PyDict_New())) { - return NULL; - } - FT_UInt index; - FT_ULong code = FT_Get_First_Char(self->x->get_face(), &index); - while (index != 0) { - PyObject *key = NULL, *val = NULL; - bool error = (!(key = PyLong_FromLong(code)) - || !(val = PyLong_FromLong(index)) - || (PyDict_SetItem(charmap, key, val) == -1)); - Py_XDECREF(key); - Py_XDECREF(val); - if (error) { - Py_DECREF(charmap); - return NULL; - } - code = FT_Get_Next_Char(self->x->get_face(), code, &index); - } - return charmap; -} - - -const char *PyFT2Font_get_char_index__doc__ = - "get_char_index(self, codepoint)\n" - "--\n\n" - "Return the glyph index corresponding to a character *codepoint*.\n"; - -static PyObject *PyFT2Font_get_char_index(PyFT2Font *self, PyObject *args) -{ - FT_UInt index; - FT_ULong ccode; - int fallback = 1; - - if (!PyArg_ParseTuple(args, "k:get_char_index", &ccode)) { - return NULL; - } - - CALL_CPP("get_char_index", index = self->x->get_char_index(ccode, (bool)fallback)); - - return PyLong_FromLong(index); -} - - -const char *PyFT2Font_get_sfnt__doc__ = - "get_sfnt(self)\n" - "--\n\n" - "Load the entire SFNT names table, as a dict whose keys are\n" - "(platform-ID, ISO-encoding-scheme, language-code, and description)\n" - "tuples.\n"; - -static PyObject *PyFT2Font_get_sfnt(PyFT2Font *self, PyObject *args) -{ - PyObject *names; - - if (!(self->x->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { - PyErr_SetString(PyExc_ValueError, "No SFNT name table"); - return NULL; - } - - size_t count = FT_Get_Sfnt_Name_Count(self->x->get_face()); - - names = PyDict_New(); - if (names == NULL) { - return NULL; - } - - for (FT_UInt j = 0; j < count; ++j) { - FT_SfntName sfnt; - FT_Error error = FT_Get_Sfnt_Name(self->x->get_face(), j, &sfnt); - - if (error) { - Py_DECREF(names); - PyErr_SetString(PyExc_ValueError, "Could not get SFNT name"); - return NULL; - } - - PyObject *key = Py_BuildValue( - "HHHH", sfnt.platform_id, sfnt.encoding_id, sfnt.language_id, sfnt.name_id); - if (key == NULL) { - Py_DECREF(names); - return NULL; - } - - PyObject *val = PyBytes_FromStringAndSize((const char *)sfnt.string, sfnt.string_len); - if (val == NULL) { - Py_DECREF(key); - Py_DECREF(names); - return NULL; - } - - if (PyDict_SetItem(names, key, val)) { - Py_DECREF(key); - Py_DECREF(val); - Py_DECREF(names); - return NULL; - } - - Py_DECREF(key); - Py_DECREF(val); - } - - return names; -} - -const char *PyFT2Font_get_name_index__doc__ = - "get_name_index(self, name)\n" - "--\n\n" - "Return the glyph index of a given glyph *name*.\n" - "The glyph index 0 means 'undefined character code'.\n"; - -static PyObject *PyFT2Font_get_name_index(PyFT2Font *self, PyObject *args) -{ - char *glyphname; - long name_index; - if (!PyArg_ParseTuple(args, "s:get_name_index", &glyphname)) { - return NULL; - } - CALL_CPP("get_name_index", name_index = self->x->get_name_index(glyphname)); - return PyLong_FromLong(name_index); -} - -const char *PyFT2Font_get_ps_font_info__doc__ = - "get_ps_font_info(self)\n" - "--\n\n" - "Return the information in the PS Font Info structure.\n"; - -static PyObject *PyFT2Font_get_ps_font_info(PyFT2Font *self, PyObject *args) -{ - PS_FontInfoRec fontinfo; - - FT_Error error = FT_Get_PS_Font_Info(self->x->get_face(), &fontinfo); - if (error) { - PyErr_SetString(PyExc_ValueError, "Could not get PS font info"); - return NULL; - } - - return Py_BuildValue("ssssslbhH", - fontinfo.version ? fontinfo.version : "", - fontinfo.notice ? fontinfo.notice : "", - fontinfo.full_name ? fontinfo.full_name : "", - fontinfo.family_name ? fontinfo.family_name : "", - fontinfo.weight ? fontinfo.weight : "", - fontinfo.italic_angle, - fontinfo.is_fixed_pitch, - fontinfo.underline_position, - fontinfo.underline_thickness); -} - -const char *PyFT2Font_get_sfnt_table__doc__ = - "get_sfnt_table(self, name)\n" - "--\n\n" - "Return one of the following SFNT tables: head, maxp, OS/2, hhea, " - "vhea, post, or pclt.\n"; - -static PyObject *PyFT2Font_get_sfnt_table(PyFT2Font *self, PyObject *args) -{ - char *tagname; - if (!PyArg_ParseTuple(args, "s:get_sfnt_table", &tagname)) { - return NULL; - } - - int tag; - const char *tags[] = { "head", "maxp", "OS/2", "hhea", "vhea", "post", "pclt", NULL }; - - for (tag = 0; tags[tag] != NULL; tag++) { - if (strncmp(tagname, tags[tag], 5) == 0) { - break; - } - } - - void *table = FT_Get_Sfnt_Table(self->x->get_face(), (FT_Sfnt_Tag)tag); - if (!table) { - Py_RETURN_NONE; - } - - switch (tag) { - case 0: { - char head_dict[] = - "{s:(h,H), s:(h,H), s:l, s:l, s:H, s:H," - "s:(l,l), s:(l,l), s:h, s:h, s:h, s:h, s:H, s:H, s:h, s:h, s:h}"; - TT_Header *t = (TT_Header *)table; - return Py_BuildValue(head_dict, - "version", FIXED_MAJOR(t->Table_Version), FIXED_MINOR(t->Table_Version), - "fontRevision", FIXED_MAJOR(t->Font_Revision), FIXED_MINOR(t->Font_Revision), - "checkSumAdjustment", t->CheckSum_Adjust, - "magicNumber", t->Magic_Number, - "flags", t->Flags, - "unitsPerEm", t->Units_Per_EM, - "created", t->Created[0], t->Created[1], - "modified", t->Modified[0], t->Modified[1], - "xMin", t->xMin, - "yMin", t->yMin, - "xMax", t->xMax, - "yMax", t->yMax, - "macStyle", t->Mac_Style, - "lowestRecPPEM", t->Lowest_Rec_PPEM, - "fontDirectionHint", t->Font_Direction, - "indexToLocFormat", t->Index_To_Loc_Format, - "glyphDataFormat", t->Glyph_Data_Format); - } - case 1: { - char maxp_dict[] = - "{s:(h,H), s:H, s:H, s:H, s:H, s:H, s:H," - "s:H, s:H, s:H, s:H, s:H, s:H, s:H, s:H}"; - TT_MaxProfile *t = (TT_MaxProfile *)table; - return Py_BuildValue(maxp_dict, - "version", FIXED_MAJOR(t->version), FIXED_MINOR(t->version), - "numGlyphs", t->numGlyphs, - "maxPoints", t->maxPoints, - "maxContours", t->maxContours, - "maxComponentPoints", t->maxCompositePoints, - "maxComponentContours", t->maxCompositeContours, - "maxZones", t->maxZones, - "maxTwilightPoints", t->maxTwilightPoints, - "maxStorage", t->maxStorage, - "maxFunctionDefs", t->maxFunctionDefs, - "maxInstructionDefs", t->maxInstructionDefs, - "maxStackElements", t->maxStackElements, - "maxSizeOfInstructions", t->maxSizeOfInstructions, - "maxComponentElements", t->maxComponentElements, - "maxComponentDepth", t->maxComponentDepth); - } - case 2: { - char os_2_dict[] = - "{s:H, s:h, s:H, s:H, s:H, s:h, s:h, s:h," - "s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:h, s:y#, s:(kkkk)," - "s:y#, s:H, s:H, s:H}"; - TT_OS2 *t = (TT_OS2 *)table; - return Py_BuildValue(os_2_dict, - "version", t->version, - "xAvgCharWidth", t->xAvgCharWidth, - "usWeightClass", t->usWeightClass, - "usWidthClass", t->usWidthClass, - "fsType", t->fsType, - "ySubscriptXSize", t->ySubscriptXSize, - "ySubscriptYSize", t->ySubscriptYSize, - "ySubscriptXOffset", t->ySubscriptXOffset, - "ySubscriptYOffset", t->ySubscriptYOffset, - "ySuperscriptXSize", t->ySuperscriptXSize, - "ySuperscriptYSize", t->ySuperscriptYSize, - "ySuperscriptXOffset", t->ySuperscriptXOffset, - "ySuperscriptYOffset", t->ySuperscriptYOffset, - "yStrikeoutSize", t->yStrikeoutSize, - "yStrikeoutPosition", t->yStrikeoutPosition, - "sFamilyClass", t->sFamilyClass, - "panose", t->panose, Py_ssize_t(10), - "ulCharRange", t->ulUnicodeRange1, t->ulUnicodeRange2, t->ulUnicodeRange3, t->ulUnicodeRange4, - "achVendID", t->achVendID, Py_ssize_t(4), - "fsSelection", t->fsSelection, - "fsFirstCharIndex", t->usFirstCharIndex, - "fsLastCharIndex", t->usLastCharIndex); - } - case 3: { - char hhea_dict[] = - "{s:(h,H), s:h, s:h, s:h, s:H, s:h, s:h, s:h," - "s:h, s:h, s:h, s:h, s:H}"; - TT_HoriHeader *t = (TT_HoriHeader *)table; - return Py_BuildValue(hhea_dict, - "version", FIXED_MAJOR(t->Version), FIXED_MINOR(t->Version), - "ascent", t->Ascender, - "descent", t->Descender, - "lineGap", t->Line_Gap, - "advanceWidthMax", t->advance_Width_Max, - "minLeftBearing", t->min_Left_Side_Bearing, - "minRightBearing", t->min_Right_Side_Bearing, - "xMaxExtent", t->xMax_Extent, - "caretSlopeRise", t->caret_Slope_Rise, - "caretSlopeRun", t->caret_Slope_Run, - "caretOffset", t->caret_Offset, - "metricDataFormat", t->metric_Data_Format, - "numOfLongHorMetrics", t->number_Of_HMetrics); - } - case 4: { - char vhea_dict[] = - "{s:(h,H), s:h, s:h, s:h, s:H, s:h, s:h, s:h," - "s:h, s:h, s:h, s:h, s:H}"; - TT_VertHeader *t = (TT_VertHeader *)table; - return Py_BuildValue(vhea_dict, - "version", FIXED_MAJOR(t->Version), FIXED_MINOR(t->Version), - "vertTypoAscender", t->Ascender, - "vertTypoDescender", t->Descender, - "vertTypoLineGap", t->Line_Gap, - "advanceHeightMax", t->advance_Height_Max, - "minTopSideBearing", t->min_Top_Side_Bearing, - "minBottomSizeBearing", t->min_Bottom_Side_Bearing, - "yMaxExtent", t->yMax_Extent, - "caretSlopeRise", t->caret_Slope_Rise, - "caretSlopeRun", t->caret_Slope_Run, - "caretOffset", t->caret_Offset, - "metricDataFormat", t->metric_Data_Format, - "numOfLongVerMetrics", t->number_Of_VMetrics); - } - case 5: { - char post_dict[] = "{s:(h,H), s:(h,H), s:h, s:h, s:k, s:k, s:k, s:k, s:k}"; - TT_Postscript *t = (TT_Postscript *)table; - return Py_BuildValue(post_dict, - "format", FIXED_MAJOR(t->FormatType), FIXED_MINOR(t->FormatType), - "italicAngle", FIXED_MAJOR(t->italicAngle), FIXED_MINOR(t->italicAngle), - "underlinePosition", t->underlinePosition, - "underlineThickness", t->underlineThickness, - "isFixedPitch", t->isFixedPitch, - "minMemType42", t->minMemType42, - "maxMemType42", t->maxMemType42, - "minMemType1", t->minMemType1, - "maxMemType1", t->maxMemType1); - } - case 6: { - char pclt_dict[] = - "{s:(h,H), s:k, s:H, s:H, s:H, s:H, s:H, s:H, s:y#, s:y#, s:b, " - "s:b, s:b}"; - TT_PCLT *t = (TT_PCLT *)table; - return Py_BuildValue(pclt_dict, - "version", FIXED_MAJOR(t->Version), FIXED_MINOR(t->Version), - "fontNumber", t->FontNumber, - "pitch", t->Pitch, - "xHeight", t->xHeight, - "style", t->Style, - "typeFamily", t->TypeFamily, - "capHeight", t->CapHeight, - "symbolSet", t->SymbolSet, - "typeFace", t->TypeFace, Py_ssize_t(16), - "characterComplement", t->CharacterComplement, Py_ssize_t(8), - "strokeWeight", t->StrokeWeight, - "widthType", t->WidthType, - "serifStyle", t->SerifStyle); - } - default: - Py_RETURN_NONE; - } -} - -const char *PyFT2Font_get_path__doc__ = - "get_path(self)\n" - "--\n\n" - "Get the path data from the currently loaded glyph as a tuple of vertices, " - "codes.\n"; - -static PyObject *PyFT2Font_get_path(PyFT2Font *self, PyObject *args) -{ - CALL_CPP("get_path", return self->x->get_path()); -} - -const char *PyFT2Font_get_image__doc__ = - "get_image(self)\n" - "--\n\n" - "Return the underlying image buffer for this font object.\n"; - -static PyObject *PyFT2Font_get_image(PyFT2Font *self, PyObject *args) -{ - FT2Image &im = self->x->get_image(); - npy_intp dims[] = {(npy_intp)im.get_height(), (npy_intp)im.get_width() }; - return PyArray_SimpleNewFromData(2, dims, NPY_UBYTE, im.get_buffer()); -} - -static PyObject *PyFT2Font_postscript_name(PyFT2Font *self, void *closure) -{ - const char *ps_name = FT_Get_Postscript_Name(self->x->get_face()); - if (ps_name == NULL) { - ps_name = "UNAVAILABLE"; - } - - return PyUnicode_FromString(ps_name); -} - -static PyObject *PyFT2Font_num_faces(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_faces); -} - -static PyObject *PyFT2Font_family_name(PyFT2Font *self, void *closure) -{ - const char *name = self->x->get_face()->family_name; - if (name == NULL) { - name = "UNAVAILABLE"; - } - return PyUnicode_FromString(name); -} - -static PyObject *PyFT2Font_style_name(PyFT2Font *self, void *closure) -{ - const char *name = self->x->get_face()->style_name; - if (name == NULL) { - name = "UNAVAILABLE"; - } - return PyUnicode_FromString(name); -} - -static PyObject *PyFT2Font_face_flags(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->face_flags); -} - -static PyObject *PyFT2Font_style_flags(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->style_flags); -} - -static PyObject *PyFT2Font_num_glyphs(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_glyphs); -} - -static PyObject *PyFT2Font_num_fixed_sizes(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_fixed_sizes); -} - -static PyObject *PyFT2Font_num_charmaps(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->num_charmaps); -} - -static PyObject *PyFT2Font_scalable(PyFT2Font *self, void *closure) -{ - if (FT_IS_SCALABLE(self->x->get_face())) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; -} - -static PyObject *PyFT2Font_units_per_EM(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->units_per_EM); -} - -static PyObject *PyFT2Font_get_bbox(PyFT2Font *self, void *closure) -{ - FT_BBox *bbox = &(self->x->get_face()->bbox); - - return Py_BuildValue("llll", - bbox->xMin, bbox->yMin, bbox->xMax, bbox->yMax); -} - -static PyObject *PyFT2Font_ascender(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->ascender); -} - -static PyObject *PyFT2Font_descender(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->descender); -} - -static PyObject *PyFT2Font_height(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->height); -} - -static PyObject *PyFT2Font_max_advance_width(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->max_advance_width); -} - -static PyObject *PyFT2Font_max_advance_height(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->max_advance_height); -} - -static PyObject *PyFT2Font_underline_position(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->underline_position); -} - -static PyObject *PyFT2Font_underline_thickness(PyFT2Font *self, void *closure) -{ - return PyLong_FromLong(self->x->get_face()->underline_thickness); -} - -static PyObject *PyFT2Font_fname(PyFT2Font *self, void *closure) -{ - if (self->stream.close) { // Called passed a filename to the constructor. - return PyObject_GetAttrString(self->py_file, "name"); - } else { - Py_INCREF(self->py_file); - return self->py_file; - } -} - -static int PyFT2Font_get_buffer(PyFT2Font *self, Py_buffer *buf, int flags) -{ - FT2Image &im = self->x->get_image(); - - Py_INCREF(self); - buf->obj = (PyObject *)self; - buf->buf = im.get_buffer(); - buf->len = im.get_width() * im.get_height(); - buf->readonly = 0; - buf->format = (char *)"B"; - buf->ndim = 2; - self->shape[0] = im.get_height(); - self->shape[1] = im.get_width(); - buf->shape = self->shape; - self->strides[0] = im.get_width(); - self->strides[1] = 1; - buf->strides = self->strides; - buf->suboffsets = NULL; - buf->itemsize = 1; - buf->internal = NULL; - - return 1; -} - -static PyTypeObject *PyFT2Font_init_type() -{ - static PyGetSetDef getset[] = { - {(char *)"postscript_name", (getter)PyFT2Font_postscript_name, NULL, NULL, NULL}, - {(char *)"num_faces", (getter)PyFT2Font_num_faces, NULL, NULL, NULL}, - {(char *)"family_name", (getter)PyFT2Font_family_name, NULL, NULL, NULL}, - {(char *)"style_name", (getter)PyFT2Font_style_name, NULL, NULL, NULL}, - {(char *)"face_flags", (getter)PyFT2Font_face_flags, NULL, NULL, NULL}, - {(char *)"style_flags", (getter)PyFT2Font_style_flags, NULL, NULL, NULL}, - {(char *)"num_glyphs", (getter)PyFT2Font_num_glyphs, NULL, NULL, NULL}, - {(char *)"num_fixed_sizes", (getter)PyFT2Font_num_fixed_sizes, NULL, NULL, NULL}, - {(char *)"num_charmaps", (getter)PyFT2Font_num_charmaps, NULL, NULL, NULL}, - {(char *)"scalable", (getter)PyFT2Font_scalable, NULL, NULL, NULL}, - {(char *)"units_per_EM", (getter)PyFT2Font_units_per_EM, NULL, NULL, NULL}, - {(char *)"bbox", (getter)PyFT2Font_get_bbox, NULL, NULL, NULL}, - {(char *)"ascender", (getter)PyFT2Font_ascender, NULL, NULL, NULL}, - {(char *)"descender", (getter)PyFT2Font_descender, NULL, NULL, NULL}, - {(char *)"height", (getter)PyFT2Font_height, NULL, NULL, NULL}, - {(char *)"max_advance_width", (getter)PyFT2Font_max_advance_width, NULL, NULL, NULL}, - {(char *)"max_advance_height", (getter)PyFT2Font_max_advance_height, NULL, NULL, NULL}, - {(char *)"underline_position", (getter)PyFT2Font_underline_position, NULL, NULL, NULL}, - {(char *)"underline_thickness", (getter)PyFT2Font_underline_thickness, NULL, NULL, NULL}, - {(char *)"fname", (getter)PyFT2Font_fname, NULL, NULL, NULL}, - {NULL} - }; - - static PyMethodDef methods[] = { - {"clear", (PyCFunction)PyFT2Font_clear, METH_NOARGS, PyFT2Font_clear__doc__}, - {"set_size", (PyCFunction)PyFT2Font_set_size, METH_VARARGS, PyFT2Font_set_size__doc__}, - {"set_charmap", (PyCFunction)PyFT2Font_set_charmap, METH_VARARGS, PyFT2Font_set_charmap__doc__}, - {"select_charmap", (PyCFunction)PyFT2Font_select_charmap, METH_VARARGS, PyFT2Font_select_charmap__doc__}, - {"get_kerning", (PyCFunction)PyFT2Font_get_kerning, METH_VARARGS, PyFT2Font_get_kerning__doc__}, - {"set_text", (PyCFunction)PyFT2Font_set_text, METH_VARARGS|METH_KEYWORDS, PyFT2Font_set_text__doc__}, - {"_get_fontmap", (PyCFunction)PyFT2Font_get_fontmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_fontmap__doc__}, - {"get_num_glyphs", (PyCFunction)PyFT2Font_get_num_glyphs, METH_NOARGS, PyFT2Font_get_num_glyphs__doc__}, - {"load_char", (PyCFunction)PyFT2Font_load_char, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_char__doc__}, - {"load_glyph", (PyCFunction)PyFT2Font_load_glyph, METH_VARARGS|METH_KEYWORDS, PyFT2Font_load_glyph__doc__}, - {"get_width_height", (PyCFunction)PyFT2Font_get_width_height, METH_NOARGS, PyFT2Font_get_width_height__doc__}, - {"get_bitmap_offset", (PyCFunction)PyFT2Font_get_bitmap_offset, METH_NOARGS, PyFT2Font_get_bitmap_offset__doc__}, - {"get_descent", (PyCFunction)PyFT2Font_get_descent, METH_NOARGS, PyFT2Font_get_descent__doc__}, - {"draw_glyphs_to_bitmap", (PyCFunction)PyFT2Font_draw_glyphs_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyphs_to_bitmap__doc__}, - {"get_xys", (PyCFunction)PyFT2Font_get_xys, METH_VARARGS|METH_KEYWORDS, PyFT2Font_get_xys__doc__}, - {"draw_glyph_to_bitmap", (PyCFunction)PyFT2Font_draw_glyph_to_bitmap, METH_VARARGS|METH_KEYWORDS, PyFT2Font_draw_glyph_to_bitmap__doc__}, - {"get_glyph_name", (PyCFunction)PyFT2Font_get_glyph_name, METH_VARARGS, PyFT2Font_get_glyph_name__doc__}, - {"get_charmap", (PyCFunction)PyFT2Font_get_charmap, METH_NOARGS, PyFT2Font_get_charmap__doc__}, - {"get_char_index", (PyCFunction)PyFT2Font_get_char_index, METH_VARARGS, PyFT2Font_get_char_index__doc__}, - {"get_sfnt", (PyCFunction)PyFT2Font_get_sfnt, METH_NOARGS, PyFT2Font_get_sfnt__doc__}, - {"get_name_index", (PyCFunction)PyFT2Font_get_name_index, METH_VARARGS, PyFT2Font_get_name_index__doc__}, - {"get_ps_font_info", (PyCFunction)PyFT2Font_get_ps_font_info, METH_NOARGS, PyFT2Font_get_ps_font_info__doc__}, - {"get_sfnt_table", (PyCFunction)PyFT2Font_get_sfnt_table, METH_VARARGS, PyFT2Font_get_sfnt_table__doc__}, - {"get_path", (PyCFunction)PyFT2Font_get_path, METH_NOARGS, PyFT2Font_get_path__doc__}, - {"get_image", (PyCFunction)PyFT2Font_get_image, METH_NOARGS, PyFT2Font_get_image__doc__}, - {NULL} - }; - - static PyBufferProcs buffer_procs; - buffer_procs.bf_getbuffer = (getbufferproc)PyFT2Font_get_buffer; - - PyFT2FontType.tp_name = "matplotlib.ft2font.FT2Font"; - PyFT2FontType.tp_doc = PyFT2Font_init__doc__; - PyFT2FontType.tp_basicsize = sizeof(PyFT2Font); - PyFT2FontType.tp_dealloc = (destructor)PyFT2Font_dealloc; - PyFT2FontType.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE; - PyFT2FontType.tp_methods = methods; - PyFT2FontType.tp_getset = getset; - PyFT2FontType.tp_new = PyFT2Font_new; - PyFT2FontType.tp_init = (initproc)PyFT2Font_init; - PyFT2FontType.tp_as_buffer = &buffer_procs; - - return &PyFT2FontType; -} - -static struct PyModuleDef moduledef = { PyModuleDef_HEAD_INIT, "ft2font" }; - -PyMODINIT_FUNC PyInit_ft2font(void) -{ - import_array(); - - if (FT_Init_FreeType(&_ft2Library)) { // initialize library - return PyErr_Format( - PyExc_RuntimeError, "Could not initialize the freetype2 library"); - } - FT_Int major, minor, patch; - char version_string[64]; - FT_Library_Version(_ft2Library, &major, &minor, &patch); - snprintf(version_string, sizeof(version_string), "%d.%d.%d", major, minor, patch); - - PyObject *m; - if (!(m = PyModule_Create(&moduledef)) || - prepare_and_add_type(PyFT2Image_init_type(), m) || - prepare_and_add_type(PyFT2Font_init_type(), m) || - // Glyph is not constructible from Python, thus not added to the module. - PyType_Ready(PyGlyph_init_type()) || - PyModule_AddStringConstant(m, "__freetype_version__", version_string) || - PyModule_AddStringConstant(m, "__freetype_build_type__", STRINGIFY(FREETYPE_BUILD_TYPE)) || - PyModule_AddIntConstant(m, "SCALABLE", FT_FACE_FLAG_SCALABLE) || - PyModule_AddIntConstant(m, "FIXED_SIZES", FT_FACE_FLAG_FIXED_SIZES) || - PyModule_AddIntConstant(m, "FIXED_WIDTH", FT_FACE_FLAG_FIXED_WIDTH) || - PyModule_AddIntConstant(m, "SFNT", FT_FACE_FLAG_SFNT) || - PyModule_AddIntConstant(m, "HORIZONTAL", FT_FACE_FLAG_HORIZONTAL) || - PyModule_AddIntConstant(m, "VERTICAL", FT_FACE_FLAG_VERTICAL) || - PyModule_AddIntConstant(m, "KERNING", FT_FACE_FLAG_KERNING) || - PyModule_AddIntConstant(m, "FAST_GLYPHS", FT_FACE_FLAG_FAST_GLYPHS) || - PyModule_AddIntConstant(m, "MULTIPLE_MASTERS", FT_FACE_FLAG_MULTIPLE_MASTERS) || - PyModule_AddIntConstant(m, "GLYPH_NAMES", FT_FACE_FLAG_GLYPH_NAMES) || - PyModule_AddIntConstant(m, "EXTERNAL_STREAM", FT_FACE_FLAG_EXTERNAL_STREAM) || - PyModule_AddIntConstant(m, "ITALIC", FT_STYLE_FLAG_ITALIC) || - PyModule_AddIntConstant(m, "BOLD", FT_STYLE_FLAG_BOLD) || - PyModule_AddIntConstant(m, "KERNING_DEFAULT", FT_KERNING_DEFAULT) || - PyModule_AddIntConstant(m, "KERNING_UNFITTED", FT_KERNING_UNFITTED) || - PyModule_AddIntConstant(m, "KERNING_UNSCALED", FT_KERNING_UNSCALED) || - PyModule_AddIntConstant(m, "LOAD_DEFAULT", FT_LOAD_DEFAULT) || - PyModule_AddIntConstant(m, "LOAD_NO_SCALE", FT_LOAD_NO_SCALE) || - PyModule_AddIntConstant(m, "LOAD_NO_HINTING", FT_LOAD_NO_HINTING) || - PyModule_AddIntConstant(m, "LOAD_RENDER", FT_LOAD_RENDER) || - PyModule_AddIntConstant(m, "LOAD_NO_BITMAP", FT_LOAD_NO_BITMAP) || - PyModule_AddIntConstant(m, "LOAD_VERTICAL_LAYOUT", FT_LOAD_VERTICAL_LAYOUT) || - PyModule_AddIntConstant(m, "LOAD_FORCE_AUTOHINT", FT_LOAD_FORCE_AUTOHINT) || - PyModule_AddIntConstant(m, "LOAD_CROP_BITMAP", FT_LOAD_CROP_BITMAP) || - PyModule_AddIntConstant(m, "LOAD_PEDANTIC", FT_LOAD_PEDANTIC) || - PyModule_AddIntConstant(m, "LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH", FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH) || - PyModule_AddIntConstant(m, "LOAD_NO_RECURSE", FT_LOAD_NO_RECURSE) || - PyModule_AddIntConstant(m, "LOAD_IGNORE_TRANSFORM", FT_LOAD_IGNORE_TRANSFORM) || - PyModule_AddIntConstant(m, "LOAD_MONOCHROME", FT_LOAD_MONOCHROME) || - PyModule_AddIntConstant(m, "LOAD_LINEAR_DESIGN", FT_LOAD_LINEAR_DESIGN) || - PyModule_AddIntConstant(m, "LOAD_NO_AUTOHINT", (unsigned long)FT_LOAD_NO_AUTOHINT) || - PyModule_AddIntConstant(m, "LOAD_TARGET_NORMAL", (unsigned long)FT_LOAD_TARGET_NORMAL) || - PyModule_AddIntConstant(m, "LOAD_TARGET_LIGHT", (unsigned long)FT_LOAD_TARGET_LIGHT) || - PyModule_AddIntConstant(m, "LOAD_TARGET_MONO", (unsigned long)FT_LOAD_TARGET_MONO) || - PyModule_AddIntConstant(m, "LOAD_TARGET_LCD", (unsigned long)FT_LOAD_TARGET_LCD) || - PyModule_AddIntConstant(m, "LOAD_TARGET_LCD_V", (unsigned long)FT_LOAD_TARGET_LCD_V)) { - FT_Done_FreeType(_ft2Library); - Py_XDECREF(m); - return NULL; - } - - return m; -} diff --git a/contrib/python/matplotlib/py3/src/mplutils.h b/contrib/python/matplotlib/py3/src/mplutils.h deleted file mode 100644 index 6eb89899ca..0000000000 --- a/contrib/python/matplotlib/py3/src/mplutils.h +++ /dev/null @@ -1,99 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -/* Small utilities that are shared by most extension modules. */ - -#ifndef MPLUTILS_H -#define MPLUTILS_H -#define PY_SSIZE_T_CLEAN - -#include <Python.h> -#include <stdint.h> - -#ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -#endif -#ifndef _AIX -#ifdef _XOPEN_SOURCE -# undef _XOPEN_SOURCE -#endif -#endif - -// Prevent multiple conflicting definitions of swab from stdlib.h and unistd.h -#if defined(__sun) || defined(sun) -#if defined(_XPG4) -#undef _XPG4 -#endif -#if defined(_XPG3) -#undef _XPG3 -#endif -#endif - - -inline int mpl_round_to_int(double v) -{ - return (int)(v + ((v >= 0.0) ? 0.5 : -0.5)); -} - -inline double mpl_round(double v) -{ - return (double)mpl_round_to_int(v); -} - -// 'kind' codes for paths. -enum { - STOP = 0, - MOVETO = 1, - LINETO = 2, - CURVE3 = 3, - CURVE4 = 4, - CLOSEPOLY = 0x4f -}; - -const size_t NUM_VERTICES[] = { 1, 1, 1, 2, 3, 1 }; - -inline int prepare_and_add_type(PyTypeObject *type, PyObject *module) -{ - if (PyType_Ready(type)) { - return -1; - } - char const* ptr = strrchr(type->tp_name, '.'); - if (!ptr) { - PyErr_SetString(PyExc_ValueError, "tp_name should be a qualified name"); - return -1; - } - if (PyModule_AddObject(module, ptr + 1, (PyObject *)type)) { - return -1; - } - return 0; -} - -#ifdef __cplusplus // not for macosx.m -// Check that array has shape (N, d1) or (N, d1, d2). We cast d1, d2 to longs -// so that we don't need to access the NPY_INTP_FMT macro here. - -template<typename T> -inline bool check_trailing_shape(T array, char const* name, long d1) -{ - if (array.dim(1) != d1) { - PyErr_Format(PyExc_ValueError, - "%s must have shape (N, %ld), got (%ld, %ld)", - name, d1, array.dim(0), array.dim(1)); - return false; - } - return true; -} - -template<typename T> -inline bool check_trailing_shape(T array, char const* name, long d1, long d2) -{ - if (array.dim(1) != d1 || array.dim(2) != d2) { - PyErr_Format(PyExc_ValueError, - "%s must have shape (N, %ld, %ld), got (%ld, %ld, %ld)", - name, d1, d2, array.dim(0), array.dim(1), array.dim(2)); - return false; - } - return true; -} -#endif - -#endif diff --git a/contrib/python/matplotlib/py3/src/numpy_cpp.h b/contrib/python/matplotlib/py3/src/numpy_cpp.h deleted file mode 100644 index 36c763d158..0000000000 --- a/contrib/python/matplotlib/py3/src/numpy_cpp.h +++ /dev/null @@ -1,578 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_NUMPY_CPP_H -#define MPL_NUMPY_CPP_H -#define PY_SSIZE_T_CLEAN -/*************************************************************************** - * This file is based on original work by Mark Wiebe, available at: - * - * http://github.com/mwiebe/numpy-cpp - * - * However, the needs of matplotlib wrappers, such as treating an - * empty array as having the correct dimensions, have made this rather - * matplotlib-specific, so it's no longer compatible with the - * original. - */ - -#include "py_exceptions.h" - -#include <complex> - -#ifdef _POSIX_C_SOURCE -# undef _POSIX_C_SOURCE -#endif -#ifndef _AIX -#ifdef _XOPEN_SOURCE -# undef _XOPEN_SOURCE -#endif -#endif - -// Prevent multiple conflicting definitions of swab from stdlib.h and unistd.h -#if defined(__sun) || defined(sun) -#if defined(_XPG4) -#undef _XPG4 -#endif -#if defined(_XPG3) -#undef _XPG3 -#endif -#endif - -#include <Python.h> -#include <numpy/ndarrayobject.h> - -namespace numpy -{ - -// Type traits for the NumPy types -template <typename T> -struct type_num_of; - -/* Be careful with bool arrays as python has sizeof(npy_bool) == 1, but it is - * not always the case that sizeof(bool) == 1. Using the array_view_accessors - * is always fine regardless of sizeof(bool), so do this rather than using - * array.data() and pointer arithmetic which will not work correctly if - * sizeof(bool) != 1. */ -template <> struct type_num_of<bool> -{ - enum { - value = NPY_BOOL - }; -}; -template <> -struct type_num_of<npy_byte> -{ - enum { - value = NPY_BYTE - }; -}; -template <> -struct type_num_of<npy_ubyte> -{ - enum { - value = NPY_UBYTE - }; -}; -template <> -struct type_num_of<npy_short> -{ - enum { - value = NPY_SHORT - }; -}; -template <> -struct type_num_of<npy_ushort> -{ - enum { - value = NPY_USHORT - }; -}; -template <> -struct type_num_of<npy_int> -{ - enum { - value = NPY_INT - }; -}; -template <> -struct type_num_of<npy_uint> -{ - enum { - value = NPY_UINT - }; -}; -template <> -struct type_num_of<npy_long> -{ - enum { - value = NPY_LONG - }; -}; -template <> -struct type_num_of<npy_ulong> -{ - enum { - value = NPY_ULONG - }; -}; -template <> -struct type_num_of<npy_longlong> -{ - enum { - value = NPY_LONGLONG - }; -}; -template <> -struct type_num_of<npy_ulonglong> -{ - enum { - value = NPY_ULONGLONG - }; -}; -template <> -struct type_num_of<npy_float> -{ - enum { - value = NPY_FLOAT - }; -}; -template <> -struct type_num_of<npy_double> -{ - enum { - value = NPY_DOUBLE - }; -}; -#if NPY_LONGDOUBLE != NPY_DOUBLE -template <> -struct type_num_of<npy_longdouble> -{ - enum { - value = NPY_LONGDOUBLE - }; -}; -#endif -template <> -struct type_num_of<npy_cfloat> -{ - enum { - value = NPY_CFLOAT - }; -}; -template <> -struct type_num_of<std::complex<npy_float> > -{ - enum { - value = NPY_CFLOAT - }; -}; -template <> -struct type_num_of<npy_cdouble> -{ - enum { - value = NPY_CDOUBLE - }; -}; -template <> -struct type_num_of<std::complex<npy_double> > -{ - enum { - value = NPY_CDOUBLE - }; -}; -#if NPY_CLONGDOUBLE != NPY_CDOUBLE -template <> -struct type_num_of<npy_clongdouble> -{ - enum { - value = NPY_CLONGDOUBLE - }; -}; -template <> -struct type_num_of<std::complex<npy_longdouble> > -{ - enum { - value = NPY_CLONGDOUBLE - }; -}; -#endif -template <> -struct type_num_of<PyObject *> -{ - enum { - value = NPY_OBJECT - }; -}; -template <typename T> -struct type_num_of<T &> -{ - enum { - value = type_num_of<T>::value - }; -}; -template <typename T> -struct type_num_of<const T> -{ - enum { - value = type_num_of<T>::value - }; -}; - -template <typename T> -struct is_const -{ - enum { - value = false - }; -}; -template <typename T> -struct is_const<const T> -{ - enum { - value = true - }; -}; - -namespace detail -{ -template <template <typename, int> class AV, typename T, int ND> -class array_view_accessors; - -template <template <typename, int> class AV, typename T> -class array_view_accessors<AV, T, 1> -{ - public: - typedef AV<T, 1> AVC; - typedef T sub_t; - - T &operator()(npy_intp i) - { - AVC *self = static_cast<AVC *>(this); - - return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i); - } - - const T &operator()(npy_intp i) const - { - const AVC *self = static_cast<const AVC *>(this); - - return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i); - } - - T &operator[](npy_intp i) - { - AVC *self = static_cast<AVC *>(this); - - return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i); - } - - const T &operator[](npy_intp i) const - { - const AVC *self = static_cast<const AVC *>(this); - - return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i); - } -}; - -template <template <typename, int> class AV, typename T> -class array_view_accessors<AV, T, 2> -{ - public: - typedef AV<T, 2> AVC; - typedef AV<T, 1> sub_t; - - T &operator()(npy_intp i, npy_intp j) - { - AVC *self = static_cast<AVC *>(this); - - return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i + - self->m_strides[1] * j); - } - - const T &operator()(npy_intp i, npy_intp j) const - { - const AVC *self = static_cast<const AVC *>(this); - - return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i + - self->m_strides[1] * j); - } - - sub_t subarray(npy_intp i) const - { - const AVC *self = static_cast<const AVC *>(this); - - return sub_t(self->m_arr, - self->m_data + self->m_strides[0] * i, - self->m_shape + 1, - self->m_strides + 1); - } -}; - -template <template <typename, int> class AV, typename T> -class array_view_accessors<AV, T, 3> -{ - public: - typedef AV<T, 3> AVC; - typedef AV<T, 2> sub_t; - - T &operator()(npy_intp i, npy_intp j, npy_intp k) - { - AVC *self = static_cast<AVC *>(this); - - return *reinterpret_cast<T *>(self->m_data + self->m_strides[0] * i + - self->m_strides[1] * j + self->m_strides[2] * k); - } - - const T &operator()(npy_intp i, npy_intp j, npy_intp k) const - { - const AVC *self = static_cast<const AVC *>(this); - - return *reinterpret_cast<const T *>(self->m_data + self->m_strides[0] * i + - self->m_strides[1] * j + self->m_strides[2] * k); - } - - sub_t subarray(npy_intp i) const - { - const AVC *self = static_cast<const AVC *>(this); - - return sub_t(self->m_arr, - self->m_data + self->m_strides[0] * i, - self->m_shape + 1, - self->m_strides + 1); - } - - -}; - -// When adding instantiations of array_view_accessors, remember to add entries -// to zeros[] below. - -} - -static npy_intp zeros[] = { 0, 0, 0 }; - -template <typename T, int ND> -class array_view : public detail::array_view_accessors<array_view, T, ND> -{ - friend class detail::array_view_accessors<numpy::array_view, T, ND>; - - private: - // Copies of the array data - PyArrayObject *m_arr; - npy_intp *m_shape; - npy_intp *m_strides; - char *m_data; - - public: - typedef T value_type; - - enum { - ndim = ND - }; - - array_view() : m_arr(NULL), m_data(NULL) - { - m_shape = zeros; - m_strides = zeros; - } - - array_view(PyObject *arr, bool contiguous = false) : m_arr(NULL), m_data(NULL) - { - if (!set(arr, contiguous)) { - throw py::exception(); - } - } - - array_view(const array_view &other) : m_arr(NULL), m_data(NULL) - { - m_arr = other.m_arr; - Py_XINCREF(m_arr); - m_data = other.m_data; - m_shape = other.m_shape; - m_strides = other.m_strides; - } - - array_view(PyArrayObject *arr, char *data, npy_intp *shape, npy_intp *strides) - { - m_arr = arr; - Py_XINCREF(arr); - m_data = data; - m_shape = shape; - m_strides = strides; - } - - array_view(PyArrayObject *arr) - { - m_arr = arr; - Py_XINCREF(arr); - m_shape = PyArray_DIMS(m_arr); - m_strides = PyArray_STRIDES(m_arr); - m_data = PyArray_BYTES(m_arr); - } - - array_view(npy_intp shape[ND]) : m_arr(NULL), m_shape(NULL), m_strides(NULL), m_data(NULL) - { - PyObject *arr = PyArray_SimpleNew(ND, shape, type_num_of<T>::value); - if (arr == NULL) { - throw py::exception(); - } - if (!set(arr, true)) { - Py_DECREF(arr); - throw py::exception(); - } - Py_DECREF(arr); - } - - ~array_view() - { - Py_XDECREF(m_arr); - } - - array_view& operator=(const array_view &other) - { - if (this != &other) - { - Py_XDECREF(m_arr); - m_arr = other.m_arr; - Py_XINCREF(m_arr); - m_data = other.m_data; - m_shape = other.m_shape; - m_strides = other.m_strides; - } - return *this; - } - - bool set(PyObject *arr, bool contiguous = false) - { - PyArrayObject *tmp; - - if (arr == NULL || arr == Py_None) { - Py_XDECREF(m_arr); - m_arr = NULL; - m_data = NULL; - m_shape = zeros; - m_strides = zeros; - } else { - if (contiguous) { - tmp = (PyArrayObject *)PyArray_ContiguousFromAny(arr, type_num_of<T>::value, 0, ND); - } else { - tmp = (PyArrayObject *)PyArray_FromObject(arr, type_num_of<T>::value, 0, ND); - } - if (tmp == NULL) { - return false; - } - - if (PyArray_NDIM(tmp) == 0 || PyArray_DIM(tmp, 0) == 0) { - Py_XDECREF(m_arr); - m_arr = NULL; - m_data = NULL; - m_shape = zeros; - m_strides = zeros; - if (PyArray_NDIM(tmp) == 0 && ND == 0) { - m_arr = tmp; - return true; - } - } - if (PyArray_NDIM(tmp) != ND) { - PyErr_Format(PyExc_ValueError, - "Expected %d-dimensional array, got %d", - ND, - PyArray_NDIM(tmp)); - Py_DECREF(tmp); - return false; - } - - /* Copy some of the data to the view object for faster access */ - Py_XDECREF(m_arr); - m_arr = tmp; - m_shape = PyArray_DIMS(m_arr); - m_strides = PyArray_STRIDES(m_arr); - m_data = PyArray_BYTES(tmp); - } - - return true; - } - - npy_intp dim(size_t i) const - { - if (i >= ND) { - return 0; - } - return m_shape[i]; - } - - /* - In most cases, code should use size() instead of dim(0), since - size() == 0 when any dimension is 0. - */ - size_t size() const - { - bool empty = (ND == 0); - for (size_t i = 0; i < ND; i++) { - if (m_shape[i] == 0) { - empty = true; - } - } - if (empty) { - return 0; - } else { - return (size_t)dim(0); - } - } - - bool empty() const - { - return size() == 0; - } - - // Do not use this for array_view<bool, ND>. See comment near top of file. - const T *data() const - { - return (const T *)m_data; - } - - // Do not use this for array_view<bool, ND>. See comment near top of file. - T *data() - { - return (T *)m_data; - } - - // Return a new reference. - PyObject *pyobj() - { - Py_XINCREF(m_arr); - return (PyObject *)m_arr; - } - - // Steal a reference. - PyObject *pyobj_steal() - { - return (PyObject *)m_arr; - } - - static int converter(PyObject *obj, void *arrp) - { - array_view<T, ND> *arr = (array_view<T, ND> *)arrp; - - if (!arr->set(obj)) { - return 0; - } - - return 1; - } - - static int converter_contiguous(PyObject *obj, void *arrp) - { - array_view<T, ND> *arr = (array_view<T, ND> *)arrp; - - if (!arr->set(obj, true)) { - return 0; - } - - return 1; - } -}; - -} // namespace numpy - - -#endif diff --git a/contrib/python/matplotlib/py3/src/path_converters.h b/contrib/python/matplotlib/py3/src/path_converters.h deleted file mode 100644 index 8583d84855..0000000000 --- a/contrib/python/matplotlib/py3/src/path_converters.h +++ /dev/null @@ -1,1106 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_PATH_CONVERTERS_H -#define MPL_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 cannot 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_codes; - bool valid_segment_exists; - bool m_last_segment_valid; - bool m_was_broken; - double m_initX; - double m_initY; - - public: - /* has_codes should be true if the path contains bezier curve segments, or - * closed loops, as this requires a slower algorithm to remove the NaNs. - * When in doubt, set to true. - */ - PathNanRemover(VertexSource &source, bool remove_nans, bool has_codes) - : m_source(&source), m_remove_nans(remove_nans), m_has_codes(has_codes), - m_last_segment_valid(false), m_was_broken(false), - m_initX(nan("")), m_initY(nan("")) - { - // ignore all close/end_poly commands until after the first valid - // (nan-free) command is encountered - valid_segment_exists = false; - } - - 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_codes) { - /* This is the slow method for when there might be curves or closed - * loops. */ - 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); - /* The vertices attached to STOP and CLOSEPOLY are never used, - * so we leave them as-is even if NaN. */ - if (code == agg::path_cmd_stop) { - return code; - } else if (code == (agg::path_cmd_end_poly | - agg::path_flags_close) && - valid_segment_exists) { - /* However, CLOSEPOLY only makes sense if a valid MOVETO - * command has already been emitted. But if a NaN was - * removed in the path, then we cannot close it as it is no - * longer a loop. We must emulate that by inserting a - * LINETO instead. */ - if (m_was_broken) { - if (m_last_segment_valid && ( - std::isfinite(m_initX) && - std::isfinite(m_initY))) { - /* Join to start if both ends are valid. */ - queue_push(agg::path_cmd_line_to, m_initX, m_initY); - break; - } else { - /* Skip the close, in case there are additional - * subpaths. */ - continue; - } - m_was_broken = false; - break; - } else { - return code; - } - } else if (code == agg::path_cmd_move_to) { - /* Save the initial point in order to produce the last - * segment closing a loop, *if* we broke the loop. */ - m_initX = *x; - m_initY = *y; - m_was_broken = false; - } - - if (needs_move_to) { - queue_push(agg::path_cmd_move_to, *x, *y); - } - - size_t num_extra_points = num_extra_points_map[code & 0xF]; - m_last_segment_valid = (std::isfinite(*x) && std::isfinite(*y)); - queue_push(code, *x, *y); - - /* Note: this test cannot 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); - m_last_segment_valid = m_last_segment_valid && - (std::isfinite(*x) && std::isfinite(*y)); - queue_push(code, *x, *y); - } - - if (m_last_segment_valid) { - valid_segment_exists = true; - break; - } - - m_was_broken = true; - 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_codes - { - /* This is the fast path for when we know we have no codes. */ - code = m_source->vertex(x, y); - - if (code == agg::path_cmd_stop || - (code == (agg::path_cmd_end_poly | agg::path_flags_close) && - valid_segment_exists)) { - 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) && - valid_segment_exists)) { - return code; - } - } while (!(std::isfinite(*x) && std::isfinite(*y))); - return agg::path_cmd_move_to; - } - valid_segment_exists = true; - 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; - bool m_was_clipped; - - 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_lastX(nan("")), - m_lastY(nan("")), - m_moveto(true), - m_initX(nan("")), - m_initY(nan("")), - m_has_init(false), - m_was_clipped(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_lastX(nan("")), - m_lastY(nan("")), - m_moveto(true), - m_initX(nan("")), - m_initY(nan("")), - m_has_init(false), - m_was_clipped(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_was_clipped = false; - m_moveto = true; - m_source->rewind(path_id); - } - - int draw_clipped_line(double x0, double y0, double x1, double y1, - bool closed=false) - { - 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 - m_was_clipped = m_was_clipped || (moved != 0); - 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); - if (closed && !m_was_clipped) { - // Close the path only if the end point hasn't moved. - queue_push(agg::path_cmd_end_poly | agg::path_flags_close, - 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) { - // If not doing any clipping, just pass along the vertices verbatim - return m_source->vertex(x, y); - } - - /* 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) { - // Queue the line from last point to the initial point, and - // if never clipped, add a close code. - draw_clipped_line(m_lastX, m_lastY, m_initX, m_initY, - true); - } else { - // An empty path that is immediately closed. - queue_push( - agg::path_cmd_end_poly | agg::path_flags_close, - m_lastX, m_lastY); - } - // If paths were not clipped, then the above code queued - // something, and we should exit the loop. Otherwise, continue - // to the next point, as there may be a new subpath. - if (queue_nonempty()) { - goto exit_loop; - } - break; - - 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; - m_was_clipped = false; - // 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_has_init && - 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; - } -}; - -/************************************************************ - 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 = mpl_round_to_int(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); - const double d_M_PI = 3.14159265358979323846; - m_p_scale = (2.0 * d_M_PI) / (m_length * m_randomness); - m_log_randomness = 2.0 * log(m_randomness); - } - - 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(); - // Original computation - // p += pow(k, 2*rand - 1) - // r = sin(p * c) - // x86 computes pow(a, b) as exp(b*log(a)) - // First, move -1 out, so - // p' += pow(k, 2*rand) - // r = sin(p * c') where c' = c / k - // Next, use x86 logic (will not be worse on other platforms as - // the log is only computed once and pow and exp are, at worst, - // the same) - // So p+= exp(2*rand*log(k)) - // lk = 2*log(k) - // p += exp(rand*lk) - m_p += exp(d_rand * m_log_randomness); - 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); - double r = sin(m_p * m_p_scale) * m_scale; - double roverlen = r / len; - *x += roverlen * num; - *y -= roverlen * den; - } - } 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; - double m_p_scale; - double m_log_randomness; -}; - -#endif // MPL_PATH_CONVERTERS_H diff --git a/contrib/python/matplotlib/py3/src/py_adaptors.h b/contrib/python/matplotlib/py3/src/py_adaptors.h deleted file mode 100644 index 7722137dc6..0000000000 --- a/contrib/python/matplotlib/py3/src/py_adaptors.h +++ /dev/null @@ -1,248 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_PY_ADAPTORS_H -#define MPL_PY_ADAPTORS_H -#define PY_SSIZE_T_CLEAN -/*************************************************************************** - * This module contains a number of C++ classes that adapt Python data - * structures to C++ and Agg-friendly interfaces. - */ - -#include <Python.h> - -#include "numpy/arrayobject.h" - -#include "py_exceptions.h" - -extern "C" { -int convert_path(PyObject *obj, void *pathp); -} - -namespace py -{ - -/************************************************************ - * py::PathIterator acts as a bridge between Numpy and Agg. Given a - * pair of Numpy arrays, vertices and codes, it iterates over - * those vertices and codes, using the standard Agg vertex source - * interface: - * - * unsigned vertex(double* x, double* y) - */ -class PathIterator -{ - /* We hold references to the Python objects, not just the - underlying data arrays, so that Python reference counting - can work. - */ - PyArrayObject *m_vertices; - PyArrayObject *m_codes; - - unsigned m_iterator; - unsigned m_total_vertices; - - /* This class doesn't actually do any simplification, but we - store the value here, since it is obtained from the Python - object. - */ - bool m_should_simplify; - double m_simplify_threshold; - - public: - inline PathIterator() - : m_vertices(NULL), - m_codes(NULL), - m_iterator(0), - m_total_vertices(0), - m_should_simplify(false), - m_simplify_threshold(1.0 / 9.0) - { - } - - inline PathIterator(PyObject *vertices, - PyObject *codes, - bool should_simplify, - double simplify_threshold) - : m_vertices(NULL), m_codes(NULL), m_iterator(0) - { - if (!set(vertices, codes, should_simplify, simplify_threshold)) - throw py::exception(); - } - - inline PathIterator(PyObject *vertices, PyObject *codes) - : m_vertices(NULL), m_codes(NULL), m_iterator(0) - { - if (!set(vertices, codes)) - throw py::exception(); - } - - inline PathIterator(const PathIterator &other) - { - Py_XINCREF(other.m_vertices); - m_vertices = other.m_vertices; - - Py_XINCREF(other.m_codes); - m_codes = other.m_codes; - - m_iterator = 0; - m_total_vertices = other.m_total_vertices; - - m_should_simplify = other.m_should_simplify; - m_simplify_threshold = other.m_simplify_threshold; - } - - ~PathIterator() - { - Py_XDECREF(m_vertices); - Py_XDECREF(m_codes); - } - - inline int - set(PyObject *vertices, PyObject *codes, bool should_simplify, double simplify_threshold) - { - m_should_simplify = should_simplify; - m_simplify_threshold = simplify_threshold; - - Py_XDECREF(m_vertices); - m_vertices = (PyArrayObject *)PyArray_FromObject(vertices, NPY_DOUBLE, 2, 2); - - if (!m_vertices || PyArray_DIM(m_vertices, 1) != 2) { - PyErr_SetString(PyExc_ValueError, "Invalid vertices array"); - return 0; - } - - Py_XDECREF(m_codes); - m_codes = NULL; - - if (codes != NULL && codes != Py_None) { - m_codes = (PyArrayObject *)PyArray_FromObject(codes, NPY_UINT8, 1, 1); - - if (!m_codes || PyArray_DIM(m_codes, 0) != PyArray_DIM(m_vertices, 0)) { - PyErr_SetString(PyExc_ValueError, "Invalid codes array"); - return 0; - } - } - - m_total_vertices = (unsigned)PyArray_DIM(m_vertices, 0); - m_iterator = 0; - - return 1; - } - - inline int set(PyObject *vertices, PyObject *codes) - { - return set(vertices, codes, false, 0.0); - } - - inline unsigned vertex(double *x, double *y) - { - if (m_iterator >= m_total_vertices) { - *x = 0.0; - *y = 0.0; - return agg::path_cmd_stop; - } - - const size_t idx = m_iterator++; - - char *pair = (char *)PyArray_GETPTR2(m_vertices, idx, 0); - *x = *(double *)pair; - *y = *(double *)(pair + PyArray_STRIDE(m_vertices, 1)); - - if (m_codes != NULL) { - return (unsigned)(*(char *)PyArray_GETPTR1(m_codes, idx)); - } else { - return idx == 0 ? agg::path_cmd_move_to : agg::path_cmd_line_to; - } - } - - inline void rewind(unsigned path_id) - { - m_iterator = path_id; - } - - inline unsigned total_vertices() const - { - return m_total_vertices; - } - - inline bool should_simplify() const - { - return m_should_simplify; - } - - inline double simplify_threshold() const - { - return m_simplify_threshold; - } - - inline bool has_codes() const - { - return m_codes != NULL; - } - - inline void *get_id() - { - return (void *)m_vertices; - } -}; - -class PathGenerator -{ - PyObject *m_paths; - Py_ssize_t m_npaths; - - public: - typedef PathIterator path_iterator; - - PathGenerator() : m_paths(NULL), m_npaths(0) {} - - ~PathGenerator() - { - Py_XDECREF(m_paths); - } - - int set(PyObject *obj) - { - if (!PySequence_Check(obj)) { - return 0; - } - - Py_XDECREF(m_paths); - m_paths = obj; - Py_INCREF(m_paths); - - m_npaths = PySequence_Size(m_paths); - - return 1; - } - - Py_ssize_t num_paths() const - { - return m_npaths; - } - - Py_ssize_t size() const - { - return m_npaths; - } - - path_iterator operator()(size_t i) - { - path_iterator path; - PyObject *item; - - item = PySequence_GetItem(m_paths, i % m_npaths); - if (item == NULL) { - throw py::exception(); - } - if (!convert_path(item, &path)) { - Py_DECREF(item); - throw py::exception(); - } - Py_DECREF(item); - return path; - } -}; -} - -#endif diff --git a/contrib/python/matplotlib/py3/src/py_converters.cpp b/contrib/python/matplotlib/py3/src/py_converters.cpp deleted file mode 100644 index 04382c5f94..0000000000 --- a/contrib/python/matplotlib/py3/src/py_converters.cpp +++ /dev/null @@ -1,558 +0,0 @@ -#define NO_IMPORT_ARRAY -#define PY_SSIZE_T_CLEAN -#include "py_converters.h" -#include "numpy_cpp.h" - -#include "agg_basics.h" -#include "agg_color_rgba.h" -#include "agg_math_stroke.h" - -extern "C" { - -static int convert_string_enum(PyObject *obj, const char *name, const char **names, int *values, int *result) -{ - PyObject *bytesobj; - char *str; - - if (obj == NULL || obj == Py_None) { - return 1; - } - - if (PyUnicode_Check(obj)) { - bytesobj = PyUnicode_AsASCIIString(obj); - if (bytesobj == NULL) { - return 0; - } - } else if (PyBytes_Check(obj)) { - Py_INCREF(obj); - bytesobj = obj; - } else { - PyErr_Format(PyExc_TypeError, "%s must be str or bytes", name); - return 0; - } - - str = PyBytes_AsString(bytesobj); - if (str == NULL) { - Py_DECREF(bytesobj); - return 0; - } - - for ( ; *names != NULL; names++, values++) { - if (strncmp(str, *names, 64) == 0) { - *result = *values; - Py_DECREF(bytesobj); - return 1; - } - } - - PyErr_Format(PyExc_ValueError, "invalid %s value", name); - Py_DECREF(bytesobj); - return 0; -} - -int convert_from_method(PyObject *obj, const char *name, converter func, void *p) -{ - PyObject *value; - - value = PyObject_CallMethod(obj, name, NULL); - if (value == NULL) { - if (!PyObject_HasAttrString(obj, name)) { - PyErr_Clear(); - return 1; - } - return 0; - } - - if (!func(value, p)) { - Py_DECREF(value); - return 0; - } - - Py_DECREF(value); - return 1; -} - -int convert_from_attr(PyObject *obj, const char *name, converter func, void *p) -{ - PyObject *value; - - value = PyObject_GetAttrString(obj, name); - if (value == NULL) { - if (!PyObject_HasAttrString(obj, name)) { - PyErr_Clear(); - return 1; - } - return 0; - } - - if (!func(value, p)) { - Py_DECREF(value); - return 0; - } - - Py_DECREF(value); - return 1; -} - -int convert_double(PyObject *obj, void *p) -{ - double *val = (double *)p; - - *val = PyFloat_AsDouble(obj); - if (PyErr_Occurred()) { - return 0; - } - - return 1; -} - -int convert_bool(PyObject *obj, void *p) -{ - bool *val = (bool *)p; - switch (PyObject_IsTrue(obj)) { - case 0: *val = false; break; - case 1: *val = true; break; - default: return 0; // errored. - } - return 1; -} - -int convert_cap(PyObject *capobj, void *capp) -{ - const char *names[] = {"butt", "round", "projecting", NULL}; - int values[] = {agg::butt_cap, agg::round_cap, agg::square_cap}; - int result = agg::butt_cap; - - if (!convert_string_enum(capobj, "capstyle", names, values, &result)) { - return 0; - } - - *(agg::line_cap_e *)capp = (agg::line_cap_e)result; - return 1; -} - -int convert_join(PyObject *joinobj, void *joinp) -{ - const char *names[] = {"miter", "round", "bevel", NULL}; - int values[] = {agg::miter_join_revert, agg::round_join, agg::bevel_join}; - int result = agg::miter_join_revert; - - if (!convert_string_enum(joinobj, "joinstyle", names, values, &result)) { - return 0; - } - - *(agg::line_join_e *)joinp = (agg::line_join_e)result; - return 1; -} - -int convert_rect(PyObject *rectobj, void *rectp) -{ - agg::rect_d *rect = (agg::rect_d *)rectp; - - if (rectobj == NULL || rectobj == Py_None) { - rect->x1 = 0.0; - rect->y1 = 0.0; - rect->x2 = 0.0; - rect->y2 = 0.0; - } else { - PyArrayObject *rect_arr = (PyArrayObject *)PyArray_ContiguousFromAny( - rectobj, NPY_DOUBLE, 1, 2); - if (rect_arr == NULL) { - return 0; - } - - if (PyArray_NDIM(rect_arr) == 2) { - if (PyArray_DIM(rect_arr, 0) != 2 || - PyArray_DIM(rect_arr, 1) != 2) { - PyErr_SetString(PyExc_ValueError, "Invalid bounding box"); - Py_DECREF(rect_arr); - return 0; - } - - } else { // PyArray_NDIM(rect_arr) == 1 - if (PyArray_DIM(rect_arr, 0) != 4) { - PyErr_SetString(PyExc_ValueError, "Invalid bounding box"); - Py_DECREF(rect_arr); - return 0; - } - } - - double *buff = (double *)PyArray_DATA(rect_arr); - rect->x1 = buff[0]; - rect->y1 = buff[1]; - rect->x2 = buff[2]; - rect->y2 = buff[3]; - - Py_DECREF(rect_arr); - } - return 1; -} - -int convert_rgba(PyObject *rgbaobj, void *rgbap) -{ - agg::rgba *rgba = (agg::rgba *)rgbap; - PyObject *rgbatuple = NULL; - int success = 1; - if (rgbaobj == NULL || rgbaobj == Py_None) { - rgba->r = 0.0; - rgba->g = 0.0; - rgba->b = 0.0; - rgba->a = 0.0; - } else { - if (!(rgbatuple = PySequence_Tuple(rgbaobj))) { - success = 0; - goto exit; - } - rgba->a = 1.0; - if (!PyArg_ParseTuple( - rgbatuple, "ddd|d:rgba", &(rgba->r), &(rgba->g), &(rgba->b), &(rgba->a))) { - success = 0; - goto exit; - } - } -exit: - Py_XDECREF(rgbatuple); - return success; -} - -int convert_dashes(PyObject *dashobj, void *dashesp) -{ - Dashes *dashes = (Dashes *)dashesp; - - double dash_offset = 0.0; - PyObject *dashes_seq = NULL; - - if (!PyArg_ParseTuple(dashobj, "dO:dashes", &dash_offset, &dashes_seq)) { - return 0; - } - - if (dashes_seq == Py_None) { - return 1; - } - - if (!PySequence_Check(dashes_seq)) { - PyErr_SetString(PyExc_TypeError, "Invalid dashes sequence"); - return 0; - } - - Py_ssize_t nentries = PySequence_Size(dashes_seq); - // If the dashpattern has odd length, iterate through it twice (in - // accordance with the pdf/ps/svg specs). - Py_ssize_t dash_pattern_length = (nentries % 2) ? 2 * nentries : nentries; - - for (Py_ssize_t i = 0; i < dash_pattern_length; ++i) { - PyObject *item; - double length; - double skip; - - item = PySequence_GetItem(dashes_seq, i % nentries); - if (item == NULL) { - return 0; - } - length = PyFloat_AsDouble(item); - if (PyErr_Occurred()) { - Py_DECREF(item); - return 0; - } - Py_DECREF(item); - - ++i; - - item = PySequence_GetItem(dashes_seq, i % nentries); - if (item == NULL) { - return 0; - } - skip = PyFloat_AsDouble(item); - if (PyErr_Occurred()) { - Py_DECREF(item); - return 0; - } - Py_DECREF(item); - - dashes->add_dash_pair(length, skip); - } - - dashes->set_dash_offset(dash_offset); - - return 1; -} - -int convert_dashes_vector(PyObject *obj, void *dashesp) -{ - DashesVector *dashes = (DashesVector *)dashesp; - - if (!PySequence_Check(obj)) { - return 0; - } - - Py_ssize_t n = PySequence_Size(obj); - - for (Py_ssize_t i = 0; i < n; ++i) { - PyObject *item; - Dashes subdashes; - - item = PySequence_GetItem(obj, i); - if (item == NULL) { - return 0; - } - - if (!convert_dashes(item, &subdashes)) { - Py_DECREF(item); - return 0; - } - Py_DECREF(item); - - dashes->push_back(subdashes); - } - - return 1; -} - -int convert_trans_affine(PyObject *obj, void *transp) -{ - agg::trans_affine *trans = (agg::trans_affine *)transp; - - /** If None assume identity transform. */ - if (obj == NULL || obj == Py_None) { - return 1; - } - - PyArrayObject *array = (PyArrayObject *)PyArray_ContiguousFromAny(obj, NPY_DOUBLE, 2, 2); - if (array == NULL) { - return 0; - } - - if (PyArray_DIM(array, 0) == 3 && PyArray_DIM(array, 1) == 3) { - double *buffer = (double *)PyArray_DATA(array); - trans->sx = buffer[0]; - trans->shx = buffer[1]; - trans->tx = buffer[2]; - - trans->shy = buffer[3]; - trans->sy = buffer[4]; - trans->ty = buffer[5]; - - Py_DECREF(array); - return 1; - } - - Py_DECREF(array); - PyErr_SetString(PyExc_ValueError, "Invalid affine transformation matrix"); - return 0; -} - -int convert_path(PyObject *obj, void *pathp) -{ - py::PathIterator *path = (py::PathIterator *)pathp; - - PyObject *vertices_obj = NULL; - PyObject *codes_obj = NULL; - PyObject *should_simplify_obj = NULL; - PyObject *simplify_threshold_obj = NULL; - bool should_simplify; - double simplify_threshold; - - int status = 0; - - if (obj == NULL || obj == Py_None) { - return 1; - } - - vertices_obj = PyObject_GetAttrString(obj, "vertices"); - if (vertices_obj == NULL) { - goto exit; - } - - codes_obj = PyObject_GetAttrString(obj, "codes"); - if (codes_obj == NULL) { - goto exit; - } - - should_simplify_obj = PyObject_GetAttrString(obj, "should_simplify"); - if (should_simplify_obj == NULL) { - goto exit; - } - switch (PyObject_IsTrue(should_simplify_obj)) { - case 0: should_simplify = 0; break; - case 1: should_simplify = 1; break; - default: goto exit; // errored. - } - - simplify_threshold_obj = PyObject_GetAttrString(obj, "simplify_threshold"); - if (simplify_threshold_obj == NULL) { - goto exit; - } - simplify_threshold = PyFloat_AsDouble(simplify_threshold_obj); - if (PyErr_Occurred()) { - goto exit; - } - - if (!path->set(vertices_obj, codes_obj, should_simplify, simplify_threshold)) { - goto exit; - } - - status = 1; - -exit: - Py_XDECREF(vertices_obj); - Py_XDECREF(codes_obj); - Py_XDECREF(should_simplify_obj); - Py_XDECREF(simplify_threshold_obj); - - return status; -} - -int convert_pathgen(PyObject *obj, void *pathgenp) -{ - py::PathGenerator *paths = (py::PathGenerator *)pathgenp; - if (!paths->set(obj)) { - PyErr_SetString(PyExc_TypeError, "Not an iterable of paths"); - return 0; - } - return 1; -} - -int convert_clippath(PyObject *clippath_tuple, void *clippathp) -{ - ClipPath *clippath = (ClipPath *)clippathp; - py::PathIterator path; - agg::trans_affine trans; - - if (clippath_tuple != NULL && clippath_tuple != Py_None) { - if (!PyArg_ParseTuple(clippath_tuple, - "O&O&:clippath", - &convert_path, - &clippath->path, - &convert_trans_affine, - &clippath->trans)) { - return 0; - } - } - - return 1; -} - -int convert_snap(PyObject *obj, void *snapp) -{ - e_snap_mode *snap = (e_snap_mode *)snapp; - if (obj == NULL || obj == Py_None) { - *snap = SNAP_AUTO; - } else { - switch (PyObject_IsTrue(obj)) { - case 0: *snap = SNAP_FALSE; break; - case 1: *snap = SNAP_TRUE; break; - default: return 0; // errored. - } - } - return 1; -} - -int convert_sketch_params(PyObject *obj, void *sketchp) -{ - SketchParams *sketch = (SketchParams *)sketchp; - - if (obj == NULL || obj == Py_None) { - sketch->scale = 0.0; - } else if (!PyArg_ParseTuple(obj, - "ddd:sketch_params", - &sketch->scale, - &sketch->length, - &sketch->randomness)) { - return 0; - } - - return 1; -} - -int convert_gcagg(PyObject *pygc, void *gcp) -{ - GCAgg *gc = (GCAgg *)gcp; - - if (!(convert_from_attr(pygc, "_linewidth", &convert_double, &gc->linewidth) && - convert_from_attr(pygc, "_alpha", &convert_double, &gc->alpha) && - convert_from_attr(pygc, "_forced_alpha", &convert_bool, &gc->forced_alpha) && - convert_from_attr(pygc, "_rgb", &convert_rgba, &gc->color) && - convert_from_attr(pygc, "_antialiased", &convert_bool, &gc->isaa) && - convert_from_attr(pygc, "_capstyle", &convert_cap, &gc->cap) && - convert_from_attr(pygc, "_joinstyle", &convert_join, &gc->join) && - convert_from_method(pygc, "get_dashes", &convert_dashes, &gc->dashes) && - convert_from_attr(pygc, "_cliprect", &convert_rect, &gc->cliprect) && - convert_from_method(pygc, "get_clip_path", &convert_clippath, &gc->clippath) && - convert_from_method(pygc, "get_snap", &convert_snap, &gc->snap_mode) && - convert_from_method(pygc, "get_hatch_path", &convert_path, &gc->hatchpath) && - convert_from_method(pygc, "get_hatch_color", &convert_rgba, &gc->hatch_color) && - convert_from_method(pygc, "get_hatch_linewidth", &convert_double, &gc->hatch_linewidth) && - convert_from_method(pygc, "get_sketch_params", &convert_sketch_params, &gc->sketch))) { - return 0; - } - - return 1; -} - -int convert_face(PyObject *color, GCAgg &gc, agg::rgba *rgba) -{ - if (!convert_rgba(color, rgba)) { - return 0; - } - - if (color != NULL && color != Py_None) { - if (gc.forced_alpha || PySequence_Size(color) == 3) { - rgba->a = gc.alpha; - } - } - - return 1; -} - -int convert_points(PyObject *obj, void *pointsp) -{ - numpy::array_view<double, 2> *points = (numpy::array_view<double, 2> *)pointsp; - if (obj == NULL || obj == Py_None) { - return 1; - } - if (!points->set(obj) - || (points->size() && !check_trailing_shape(*points, "points", 2))) { - return 0; - } - return 1; -} - -int convert_transforms(PyObject *obj, void *transp) -{ - numpy::array_view<double, 3> *trans = (numpy::array_view<double, 3> *)transp; - if (obj == NULL || obj == Py_None) { - return 1; - } - if (!trans->set(obj) - || (trans->size() && !check_trailing_shape(*trans, "transforms", 3, 3))) { - return 0; - } - return 1; -} - -int convert_bboxes(PyObject *obj, void *bboxp) -{ - numpy::array_view<double, 3> *bbox = (numpy::array_view<double, 3> *)bboxp; - if (obj == NULL || obj == Py_None) { - return 1; - } - if (!bbox->set(obj) - || (bbox->size() && !check_trailing_shape(*bbox, "bbox array", 2, 2))) { - return 0; - } - return 1; -} - -int convert_colors(PyObject *obj, void *colorsp) -{ - numpy::array_view<double, 2> *colors = (numpy::array_view<double, 2> *)colorsp; - if (obj == NULL || obj == Py_None) { - return 1; - } - if (!colors->set(obj) - || (colors->size() && !check_trailing_shape(*colors, "colors", 4))) { - return 0; - } - return 1; -} -} diff --git a/contrib/python/matplotlib/py3/src/py_converters.h b/contrib/python/matplotlib/py3/src/py_converters.h deleted file mode 100644 index 2c9dc6d1b8..0000000000 --- a/contrib/python/matplotlib/py3/src/py_converters.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_PY_CONVERTERS_H -#define MPL_PY_CONVERTERS_H - -/*************************************************************************** - * This module contains a number of conversion functions from Python types - * to C++ types. Most of them meet the Python "converter" signature: - * - * typedef int (*converter)(PyObject *, void *); - * - * and thus can be passed as conversion functions to PyArg_ParseTuple - * and friends. - */ - -#include <Python.h> -#include "_backend_agg_basic_types.h" - -extern "C" { -typedef int (*converter)(PyObject *, void *); - -int convert_from_attr(PyObject *obj, const char *name, converter func, void *p); -int convert_from_method(PyObject *obj, const char *name, converter func, void *p); - -int convert_double(PyObject *obj, void *p); -int convert_bool(PyObject *obj, void *p); -int convert_cap(PyObject *capobj, void *capp); -int convert_join(PyObject *joinobj, void *joinp); -int convert_rect(PyObject *rectobj, void *rectp); -int convert_rgba(PyObject *rgbaocj, void *rgbap); -int convert_dashes(PyObject *dashobj, void *gcp); -int convert_dashes_vector(PyObject *obj, void *dashesp); -int convert_trans_affine(PyObject *obj, void *transp); -int convert_path(PyObject *obj, void *pathp); -int convert_pathgen(PyObject *obj, void *pathgenp); -int convert_clippath(PyObject *clippath_tuple, void *clippathp); -int convert_snap(PyObject *obj, void *snapp); -int convert_sketch_params(PyObject *obj, void *sketchp); -int convert_gcagg(PyObject *pygc, void *gcp); -int convert_points(PyObject *pygc, void *pointsp); -int convert_transforms(PyObject *pygc, void *transp); -int convert_bboxes(PyObject *pygc, void *bboxp); -int convert_colors(PyObject *pygc, void *colorsp); - -int convert_face(PyObject *color, GCAgg &gc, agg::rgba *rgba); -} - -#endif diff --git a/contrib/python/matplotlib/py3/src/py_exceptions.h b/contrib/python/matplotlib/py3/src/py_exceptions.h deleted file mode 100644 index c4accf2634..0000000000 --- a/contrib/python/matplotlib/py3/src/py_exceptions.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- mode: c++; c-basic-offset: 4 -*- */ - -#ifndef MPL_PY_EXCEPTIONS_H -#define MPL_PY_EXCEPTIONS_H - -#include <exception> -#include <stdexcept> - -namespace py -{ -class exception : public std::exception -{ - public: - const char *what() const throw() - { - return "python error has been set"; - } -}; -} - -#define CALL_CPP_FULL(name, a, cleanup, errorcode) \ - try \ - { \ - a; \ - } \ - catch (const py::exception &) \ - { \ - { \ - cleanup; \ - } \ - return (errorcode); \ - } \ - catch (const std::bad_alloc &) \ - { \ - PyErr_Format(PyExc_MemoryError, "In %s: Out of memory", (name)); \ - { \ - cleanup; \ - } \ - return (errorcode); \ - } \ - catch (const std::overflow_error &e) \ - { \ - PyErr_Format(PyExc_OverflowError, "In %s: %s", (name), e.what()); \ - { \ - cleanup; \ - } \ - return (errorcode); \ - } \ - catch (const std::runtime_error &e) \ - { \ - PyErr_Format(PyExc_RuntimeError, "In %s: %s", (name), e.what()); \ - { \ - cleanup; \ - } \ - return (errorcode); \ - } \ - catch (...) \ - { \ - PyErr_Format(PyExc_RuntimeError, "Unknown exception in %s", (name)); \ - { \ - cleanup; \ - } \ - return (errorcode); \ - } - -#define CALL_CPP_CLEANUP(name, a, cleanup) CALL_CPP_FULL(name, a, cleanup, 0) - -#define CALL_CPP(name, a) CALL_CPP_FULL(name, a, , 0) - -#define CALL_CPP_INIT(name, a) CALL_CPP_FULL(name, a, , -1) - -#endif diff --git a/contrib/python/matplotlib/py3/src/tri/_tri.cpp b/contrib/python/matplotlib/py3/src/tri/_tri.cpp deleted file mode 100644 index 2674a3140b..0000000000 --- a/contrib/python/matplotlib/py3/src/tri/_tri.cpp +++ /dev/null @@ -1,2074 +0,0 @@ -/* This file contains liberal use of asserts to assist code development and - * debugging. Standard matplotlib builds disable asserts so they cause no - * performance reduction. To enable the asserts, you need to undefine the - * NDEBUG macro, which is achieved by adding the following - * undef_macros=['NDEBUG'] - * to the appropriate make_extension call in setupext.py, and then rebuilding. - */ -#include "../mplutils.h" -#include "_tri.h" - -#include <algorithm> -#include <random> -#include <set> - - -TriEdge::TriEdge() - : tri(-1), edge(-1) -{} - -TriEdge::TriEdge(int tri_, int edge_) - : tri(tri_), edge(edge_) -{} - -bool TriEdge::operator<(const TriEdge& other) const -{ - if (tri != other.tri) - return tri < other.tri; - else - return edge < other.edge; -} - -bool TriEdge::operator==(const TriEdge& other) const -{ - return tri == other.tri && edge == other.edge; -} - -bool TriEdge::operator!=(const TriEdge& other) const -{ - return !operator==(other); -} - -std::ostream& operator<<(std::ostream& os, const TriEdge& tri_edge) -{ - return os << tri_edge.tri << ' ' << tri_edge.edge; -} - - - -XY::XY() -{} - -XY::XY(const double& x_, const double& y_) - : x(x_), y(y_) -{} - -double XY::angle() const -{ - return atan2(y, x); -} - -double XY::cross_z(const XY& other) const -{ - return x*other.y - y*other.x; -} - -bool XY::is_right_of(const XY& other) const -{ - if (x == other.x) - return y > other.y; - else - return x > other.x; -} - -bool XY::operator==(const XY& other) const -{ - return x == other.x && y == other.y; -} - -bool XY::operator!=(const XY& other) const -{ - return x != other.x || y != other.y; -} - -XY XY::operator*(const double& multiplier) const -{ - return XY(x*multiplier, y*multiplier); -} - -const XY& XY::operator+=(const XY& other) -{ - x += other.x; - y += other.y; - return *this; -} - -const XY& XY::operator-=(const XY& other) -{ - x -= other.x; - y -= other.y; - return *this; -} - -XY XY::operator+(const XY& other) const -{ - return XY(x + other.x, y + other.y); -} - -XY XY::operator-(const XY& other) const -{ - return XY(x - other.x, y - other.y); -} - -std::ostream& operator<<(std::ostream& os, const XY& xy) -{ - return os << '(' << xy.x << ' ' << xy.y << ')'; -} - - - -XYZ::XYZ(const double& x_, const double& y_, const double& z_) - : x(x_), y(y_), z(z_) -{} - -XYZ XYZ::cross(const XYZ& other) const -{ - return XYZ(y*other.z - z*other.y, - z*other.x - x*other.z, - x*other.y - y*other.x); -} - -double XYZ::dot(const XYZ& other) const -{ - return x*other.x + y*other.y + z*other.z; -} - -XYZ XYZ::operator-(const XYZ& other) const -{ - return XYZ(x - other.x, y - other.y, z - other.z); -} - -std::ostream& operator<<(std::ostream& os, const XYZ& xyz) -{ - return os << '(' << xyz.x << ' ' << xyz.y << ' ' << xyz.z << ')'; -} - - - -BoundingBox::BoundingBox() - : empty(true), lower(0.0, 0.0), upper(0.0, 0.0) -{} - -void BoundingBox::add(const XY& point) -{ - if (empty) { - empty = false; - lower = upper = point; - } else { - if (point.x < lower.x) lower.x = point.x; - else if (point.x > upper.x) upper.x = point.x; - - if (point.y < lower.y) lower.y = point.y; - else if (point.y > upper.y) upper.y = point.y; - } -} - -void BoundingBox::expand(const XY& delta) -{ - if (!empty) { - lower -= delta; - upper += delta; - } -} - - - -ContourLine::ContourLine() - : std::vector<XY>() -{} - -void ContourLine::push_back(const XY& point) -{ - if (empty() || point != back()) - std::vector<XY>::push_back(point); -} - -void ContourLine::write() const -{ - std::cout << "ContourLine of " << size() << " points:"; - for (const_iterator it = begin(); it != end(); ++it) - std::cout << ' ' << *it; - std::cout << std::endl; -} - - - -void write_contour(const Contour& contour) -{ - std::cout << "Contour of " << contour.size() << " lines." << std::endl; - for (Contour::const_iterator it = contour.begin(); it != contour.end(); ++it) - it->write(); -} - - - -Triangulation::Triangulation(const CoordinateArray& x, - const CoordinateArray& y, - const TriangleArray& triangles, - const MaskArray& mask, - const EdgeArray& edges, - const NeighborArray& neighbors, - bool correct_triangle_orientations) - : _x(x), - _y(y), - _triangles(triangles), - _mask(mask), - _edges(edges), - _neighbors(neighbors) -{ - if (_x.ndim() != 1 || _y.ndim() != 1 || _x.shape(0) != _y.shape(0)) - throw std::invalid_argument("x and y must be 1D arrays of the same length"); - - if (_triangles.ndim() != 2 || _triangles.shape(1) != 3) - throw std::invalid_argument("triangles must be a 2D array of shape (?,3)"); - - // Optional mask. - if (_mask.size() > 0 && - (_mask.ndim() != 1 || _mask.shape(0) != _triangles.shape(0))) - throw std::invalid_argument( - "mask must be a 1D array with the same length as the triangles array"); - - // Optional edges. - if (_edges.size() > 0 && - (_edges.ndim() != 2 || _edges.shape(1) != 2)) - throw std::invalid_argument("edges must be a 2D array with shape (?,2)"); - - // Optional neighbors. - if (_neighbors.size() > 0 && - (_neighbors.ndim() != 2 || _neighbors.shape() != _triangles.shape())) - throw std::invalid_argument( - "neighbors must be a 2D array with the same shape as the triangles array"); - - if (correct_triangle_orientations) - correct_triangles(); -} - -void Triangulation::calculate_boundaries() -{ - get_neighbors(); // Ensure _neighbors has been created. - - // Create set of all boundary TriEdges, which are those which do not - // have a neighbor triangle. - typedef std::set<TriEdge> BoundaryEdges; - BoundaryEdges boundary_edges; - for (int tri = 0; tri < get_ntri(); ++tri) { - if (!is_masked(tri)) { - for (int edge = 0; edge < 3; ++edge) { - if (get_neighbor(tri, edge) == -1) { - boundary_edges.insert(TriEdge(tri, edge)); - } - } - } - } - - // Take any boundary edge and follow the boundary until return to start - // point, removing edges from boundary_edges as they are used. At the same - // time, initialise the _tri_edge_to_boundary_map. - while (!boundary_edges.empty()) { - // Start of new boundary. - BoundaryEdges::iterator it = boundary_edges.begin(); - int tri = it->tri; - int edge = it->edge; - _boundaries.push_back(Boundary()); - Boundary& boundary = _boundaries.back(); - - while (true) { - boundary.push_back(TriEdge(tri, edge)); - boundary_edges.erase(it); - _tri_edge_to_boundary_map[TriEdge(tri, edge)] = - BoundaryEdge(_boundaries.size()-1, boundary.size()-1); - - // Move to next edge of current triangle. - edge = (edge+1) % 3; - - // Find start point index of boundary edge. - int point = get_triangle_point(tri, edge); - - // Find next TriEdge by traversing neighbors until find one - // without a neighbor. - while (get_neighbor(tri, edge) != -1) { - tri = get_neighbor(tri, edge); - edge = get_edge_in_triangle(tri, point); - } - - if (TriEdge(tri,edge) == boundary.front()) - break; // Reached beginning of this boundary, so finished it. - else - it = boundary_edges.find(TriEdge(tri, edge)); - } - } -} - -void Triangulation::calculate_edges() -{ - assert(!has_edges() && "Expected empty edges array"); - - // Create set of all edges, storing them with start point index less than - // end point index. - typedef std::set<Edge> EdgeSet; - EdgeSet edge_set; - for (int tri = 0; tri < get_ntri(); ++tri) { - if (!is_masked(tri)) { - for (int edge = 0; edge < 3; edge++) { - int start = get_triangle_point(tri, edge); - int end = get_triangle_point(tri, (edge+1)%3); - edge_set.insert(start > end ? Edge(start,end) : Edge(end,start)); - } - } - } - - // Convert to python _edges array. - py::ssize_t dims[2] = {static_cast<py::ssize_t>(edge_set.size()), 2}; - _edges = EdgeArray(dims); - auto edges = _edges.mutable_data(); - - int i = 0; - for (EdgeSet::const_iterator it = edge_set.begin(); it != edge_set.end(); ++it) { - edges[i++] = it->start; - edges[i++] = it->end; - } -} - -void Triangulation::calculate_neighbors() -{ - assert(!has_neighbors() && "Expected empty neighbors array"); - - // Create _neighbors array with shape (ntri,3) and initialise all to -1. - py::ssize_t dims[2] = {get_ntri(), 3}; - _neighbors = NeighborArray(dims); - auto* neighbors = _neighbors.mutable_data(); - - int tri, edge; - std::fill(neighbors, neighbors+3*get_ntri(), -1); - - // For each triangle edge (start to end point), find corresponding neighbor - // edge from end to start point. Do this by traversing all edges and - // storing them in a map from edge to TriEdge. If corresponding neighbor - // edge is already in the map, don't need to store new edge as neighbor - // already found. - typedef std::map<Edge, TriEdge> EdgeToTriEdgeMap; - EdgeToTriEdgeMap edge_to_tri_edge_map; - for (tri = 0; tri < get_ntri(); ++tri) { - if (!is_masked(tri)) { - for (edge = 0; edge < 3; ++edge) { - int start = get_triangle_point(tri, edge); - int end = get_triangle_point(tri, (edge+1)%3); - EdgeToTriEdgeMap::iterator it = - edge_to_tri_edge_map.find(Edge(end,start)); - if (it == edge_to_tri_edge_map.end()) { - // No neighbor edge exists in the edge_to_tri_edge_map, so - // add this edge to it. - edge_to_tri_edge_map[Edge(start,end)] = TriEdge(tri,edge); - } else { - // Neighbor edge found, set the two elements of _neighbors - // and remove edge from edge_to_tri_edge_map. - neighbors[3*tri + edge] = it->second.tri; - neighbors[3*it->second.tri + it->second.edge] = tri; - edge_to_tri_edge_map.erase(it); - } - } - } - } - - // Note that remaining edges in the edge_to_tri_edge_map correspond to - // boundary edges, but the boundaries are calculated separately elsewhere. -} - -Triangulation::TwoCoordinateArray Triangulation::calculate_plane_coefficients( - const CoordinateArray& z) -{ - if (z.ndim() != 1 || z.shape(0) != _x.shape(0)) - throw std::invalid_argument( - "z must be a 1D array with the same length as the triangulation x and y arrays"); - - int dims[2] = {get_ntri(), 3}; - Triangulation::TwoCoordinateArray planes_array(dims); - auto planes = planes_array.mutable_unchecked<2>(); - auto triangles = _triangles.unchecked<2>(); - auto x = _x.unchecked<1>(); - auto y = _y.unchecked<1>(); - auto z_ptr = z.unchecked<1>(); - - int point; - for (int tri = 0; tri < get_ntri(); ++tri) { - if (is_masked(tri)) { - planes(tri, 0) = 0.0; - planes(tri, 1) = 0.0; - planes(tri, 2) = 0.0; - } - else { - // Equation of plane for all points r on plane is r.normal = p - // where normal is vector normal to the plane, and p is a - // constant. Rewrite as - // r_x*normal_x + r_y*normal_y + r_z*normal_z = p - // and rearrange to give - // r_z = (-normal_x/normal_z)*r_x + (-normal_y/normal_z)*r_y + - // p/normal_z - point = triangles(tri, 0); - XYZ point0(x(point), y(point), z_ptr(point)); - point = triangles(tri, 1); - XYZ side01 = XYZ(x(point), y(point), z_ptr(point)) - point0; - point = triangles(tri, 2); - XYZ side02 = XYZ(x(point), y(point), z_ptr(point)) - point0; - - XYZ normal = side01.cross(side02); - - if (normal.z == 0.0) { - // Normal is in x-y plane which means triangle consists of - // colinear points. To avoid dividing by zero, we use the - // Moore-Penrose pseudo-inverse. - double sum2 = (side01.x*side01.x + side01.y*side01.y + - side02.x*side02.x + side02.y*side02.y); - double a = (side01.x*side01.z + side02.x*side02.z) / sum2; - double b = (side01.y*side01.z + side02.y*side02.z) / sum2; - planes(tri, 0) = a; - planes(tri, 1) = b; - planes(tri, 2) = point0.z - a*point0.x - b*point0.y; - } - else { - planes(tri, 0) = -normal.x / normal.z; // x - planes(tri, 1) = -normal.y / normal.z; // y - planes(tri, 2) = normal.dot(point0) / normal.z; // constant - } - } - } - - return planes_array; -} - -void Triangulation::correct_triangles() -{ - auto triangles = _triangles.mutable_data(); - auto neighbors = _neighbors.mutable_data(); - - for (int tri = 0; tri < get_ntri(); ++tri) { - XY point0 = get_point_coords(triangles[3*tri]); - XY point1 = get_point_coords(triangles[3*tri+1]); - XY point2 = get_point_coords(triangles[3*tri+2]); - if ( (point1 - point0).cross_z(point2 - point0) < 0.0) { - // Triangle points are clockwise, so change them to anticlockwise. - std::swap(triangles[3*tri+1], triangles[3*tri+2]); - if (has_neighbors()) - std::swap(neighbors[3*tri+1], neighbors[3*tri+2]); - } - } -} - -const Triangulation::Boundaries& Triangulation::get_boundaries() const -{ - if (_boundaries.empty()) - const_cast<Triangulation*>(this)->calculate_boundaries(); - return _boundaries; -} - -void Triangulation::get_boundary_edge(const TriEdge& triEdge, - int& boundary, - int& edge) const -{ - get_boundaries(); // Ensure _tri_edge_to_boundary_map has been created. - TriEdgeToBoundaryMap::const_iterator it = - _tri_edge_to_boundary_map.find(triEdge); - assert(it != _tri_edge_to_boundary_map.end() && - "TriEdge is not on a boundary"); - boundary = it->second.boundary; - edge = it->second.edge; -} - -int Triangulation::get_edge_in_triangle(int tri, int point) const -{ - assert(tri >= 0 && tri < get_ntri() && "Triangle index out of bounds"); - assert(point >= 0 && point < get_npoints() && "Point index out of bounds."); - - auto triangles = _triangles.data(); - - for (int edge = 0; edge < 3; ++edge) { - if (triangles[3*tri + edge] == point) - return edge; - } - return -1; // point is not in triangle. -} - -Triangulation::EdgeArray& Triangulation::get_edges() -{ - if (!has_edges()) - calculate_edges(); - return _edges; -} - -int Triangulation::get_neighbor(int tri, int edge) const -{ - assert(tri >= 0 && tri < get_ntri() && "Triangle index out of bounds"); - assert(edge >= 0 && edge < 3 && "Edge index out of bounds"); - if (!has_neighbors()) - const_cast<Triangulation&>(*this).calculate_neighbors(); - return _neighbors.data()[3*tri + edge]; -} - -TriEdge Triangulation::get_neighbor_edge(int tri, int edge) const -{ - int neighbor_tri = get_neighbor(tri, edge); - if (neighbor_tri == -1) - return TriEdge(-1,-1); - else - return TriEdge(neighbor_tri, - get_edge_in_triangle(neighbor_tri, - get_triangle_point(tri, - (edge+1)%3))); -} - -Triangulation::NeighborArray& Triangulation::get_neighbors() -{ - if (!has_neighbors()) - calculate_neighbors(); - return _neighbors; -} - -int Triangulation::get_npoints() const -{ - return _x.shape(0); -} - -int Triangulation::get_ntri() const -{ - return _triangles.shape(0); -} - -XY Triangulation::get_point_coords(int point) const -{ - assert(point >= 0 && point < get_npoints() && "Point index out of bounds."); - return XY(_x.data()[point], _y.data()[point]); -} - -int Triangulation::get_triangle_point(int tri, int edge) const -{ - assert(tri >= 0 && tri < get_ntri() && "Triangle index out of bounds"); - assert(edge >= 0 && edge < 3 && "Edge index out of bounds"); - return _triangles.data()[3*tri + edge]; -} - -int Triangulation::get_triangle_point(const TriEdge& tri_edge) const -{ - return get_triangle_point(tri_edge.tri, tri_edge.edge); -} - -bool Triangulation::has_edges() const -{ - return _edges.size() > 0; -} - -bool Triangulation::has_mask() const -{ - return _mask.size() > 0; -} - -bool Triangulation::has_neighbors() const -{ - return _neighbors.size() > 0; -} - -bool Triangulation::is_masked(int tri) const -{ - assert(tri >= 0 && tri < get_ntri() && "Triangle index out of bounds."); - return has_mask() && _mask.data()[tri]; -} - -void Triangulation::set_mask(const MaskArray& mask) -{ - if (mask.size() > 0 && - (mask.ndim() != 1 || mask.shape(0) != _triangles.shape(0))) - throw std::invalid_argument( - "mask must be a 1D array with the same length as the triangles array"); - - _mask = mask; - - // Clear derived fields so they are recalculated when needed. - _edges = EdgeArray(); - _neighbors = NeighborArray(); - _boundaries.clear(); -} - -void Triangulation::write_boundaries() const -{ - const Boundaries& bs = get_boundaries(); - std::cout << "Number of boundaries: " << bs.size() << std::endl; - for (Boundaries::const_iterator it = bs.begin(); it != bs.end(); ++it) { - const Boundary& b = *it; - std::cout << " Boundary of " << b.size() << " points: "; - for (Boundary::const_iterator itb = b.begin(); itb != b.end(); ++itb) { - std::cout << *itb << ", "; - } - std::cout << std::endl; - } -} - - - -TriContourGenerator::TriContourGenerator(Triangulation& triangulation, - const CoordinateArray& z) - : _triangulation(triangulation), - _z(z), - _interior_visited(2*_triangulation.get_ntri()), - _boundaries_visited(0), - _boundaries_used(0) -{ - if (_z.ndim() != 1 || _z.shape(0) != _triangulation.get_npoints()) - throw std::invalid_argument( - "z must be a 1D array with the same length as the x and y arrays"); -} - -void TriContourGenerator::clear_visited_flags(bool include_boundaries) -{ - // Clear _interiorVisited. - std::fill(_interior_visited.begin(), _interior_visited.end(), false); - - if (include_boundaries) { - if (_boundaries_visited.empty()) { - const Boundaries& boundaries = get_boundaries(); - - // Initialise _boundaries_visited. - _boundaries_visited.reserve(boundaries.size()); - for (Boundaries::const_iterator it = boundaries.begin(); - it != boundaries.end(); ++it) - _boundaries_visited.push_back(BoundaryVisited(it->size())); - - // Initialise _boundaries_used. - _boundaries_used = BoundariesUsed(boundaries.size()); - } - - // Clear _boundaries_visited. - for (BoundariesVisited::iterator it = _boundaries_visited.begin(); - it != _boundaries_visited.end(); ++it) - std::fill(it->begin(), it->end(), false); - - // Clear _boundaries_used. - std::fill(_boundaries_used.begin(), _boundaries_used.end(), false); - } -} - -py::tuple TriContourGenerator::contour_line_to_segs_and_kinds(const Contour& contour) -{ - // Convert all of the lines generated by a call to create_contour() into - // their Python equivalents for return to the calling function. - // A line is either a closed line loop (in which case the last point is - // identical to the first) or an open line strip. Two NumPy arrays are - // created for each line: - // vertices is a double array of shape (npoints, 2) containing the (x, y) - // coordinates of the points in the line - // codes is a uint8 array of shape (npoints,) containing the 'kind codes' - // which are defined in the Path class - // and they are appended to the Python lists vertices_list and codes_list - // respectively for return to the Python calling function. - - py::list vertices_list(contour.size()); - py::list codes_list(contour.size()); - - for (Contour::size_type i = 0; i < contour.size(); ++i) { - const ContourLine& contour_line = contour[i]; - py::ssize_t npoints = static_cast<py::ssize_t>(contour_line.size()); - - py::ssize_t segs_dims[2] = {npoints, 2}; - CoordinateArray segs(segs_dims); - double* segs_ptr = segs.mutable_data(); - - py::ssize_t codes_dims[1] = {npoints}; - CodeArray codes(codes_dims); - unsigned char* codes_ptr = codes.mutable_data(); - - for (ContourLine::const_iterator it = contour_line.begin(); - it != contour_line.end(); ++it) { - *segs_ptr++ = it->x; - *segs_ptr++ = it->y; - *codes_ptr++ = (it == contour_line.begin() ? MOVETO : LINETO); - } - - // Closed line loop has identical first and last (x, y) points. - if (contour_line.size() > 1 && - contour_line.front() == contour_line.back()) - *(codes_ptr-1) = CLOSEPOLY; - - vertices_list[i] = segs; - codes_list[i] = codes; - } - - return py::make_tuple(vertices_list, codes_list); -} - -py::tuple TriContourGenerator::contour_to_segs_and_kinds(const Contour& contour) -{ - // Convert all of the polygons generated by a call to - // create_filled_contour() into their Python equivalents for return to the - // calling function. All of the polygons' points and kinds codes are - // combined into single NumPy arrays for each; this avoids having - // to determine which polygons are holes as this will be determined by the - // renderer. If there are ntotal points in all of the polygons, the two - // NumPy arrays created are: - // vertices is a double array of shape (ntotal, 2) containing the (x, y) - // coordinates of the points in the polygons - // codes is a uint8 array of shape (ntotal,) containing the 'kind codes' - // which are defined in the Path class - // and they are returned in the Python lists vertices_list and codes_list - // respectively. - - Contour::const_iterator line; - ContourLine::const_iterator point; - - // Find total number of points in all contour lines. - py::ssize_t n_points = 0; - for (line = contour.begin(); line != contour.end(); ++line) - n_points += static_cast<py::ssize_t>(line->size()); - - // Create segs array for point coordinates. - py::ssize_t segs_dims[2] = {n_points, 2}; - TwoCoordinateArray segs(segs_dims); - double* segs_ptr = segs.mutable_data(); - - // Create kinds array for code types. - py::ssize_t codes_dims[1] = {n_points}; - CodeArray codes(codes_dims); - unsigned char* codes_ptr = codes.mutable_data(); - - for (line = contour.begin(); line != contour.end(); ++line) { - for (point = line->begin(); point != line->end(); point++) { - *segs_ptr++ = point->x; - *segs_ptr++ = point->y; - *codes_ptr++ = (point == line->begin() ? MOVETO : LINETO); - } - - if (line->size() > 1) - *(codes_ptr-1) = CLOSEPOLY; - } - - py::list vertices_list(1); - vertices_list[0] = segs; - - py::list codes_list(1); - codes_list[0] = codes; - - return py::make_tuple(vertices_list, codes_list); -} - -py::tuple TriContourGenerator::create_contour(const double& level) -{ - clear_visited_flags(false); - Contour contour; - - find_boundary_lines(contour, level); - find_interior_lines(contour, level, false, false); - - return contour_line_to_segs_and_kinds(contour); -} - -py::tuple TriContourGenerator::create_filled_contour(const double& lower_level, - const double& upper_level) -{ - if (lower_level >= upper_level) - throw std::invalid_argument("filled contour levels must be increasing"); - - clear_visited_flags(true); - Contour contour; - - find_boundary_lines_filled(contour, lower_level, upper_level); - find_interior_lines(contour, lower_level, false, true); - find_interior_lines(contour, upper_level, true, true); - - return contour_to_segs_and_kinds(contour); -} - -XY TriContourGenerator::edge_interp(int tri, int edge, const double& level) -{ - return interp(_triangulation.get_triangle_point(tri, edge), - _triangulation.get_triangle_point(tri, (edge+1)%3), - level); -} - -void TriContourGenerator::find_boundary_lines(Contour& contour, - const double& level) -{ - // Traverse boundaries to find starting points for all contour lines that - // intersect the boundaries. For each starting point found, follow the - // line to its end before continuing. - const Triangulation& triang = _triangulation; - const Boundaries& boundaries = get_boundaries(); - for (Boundaries::const_iterator it = boundaries.begin(); - it != boundaries.end(); ++it) { - const Boundary& boundary = *it; - bool startAbove, endAbove = false; - for (Boundary::const_iterator itb = boundary.begin(); - itb != boundary.end(); ++itb) { - if (itb == boundary.begin()) - startAbove = get_z(triang.get_triangle_point(*itb)) >= level; - else - startAbove = endAbove; - endAbove = get_z(triang.get_triangle_point(itb->tri, - (itb->edge+1)%3)) >= level; - if (startAbove && !endAbove) { - // This boundary edge is the start point for a contour line, - // so follow the line. - contour.push_back(ContourLine()); - ContourLine& contour_line = contour.back(); - TriEdge tri_edge = *itb; - follow_interior(contour_line, tri_edge, true, level, false); - } - } - } -} - -void TriContourGenerator::find_boundary_lines_filled(Contour& contour, - const double& lower_level, - const double& upper_level) -{ - // Traverse boundaries to find starting points for all contour lines that - // intersect the boundaries. For each starting point found, follow the - // line to its end before continuing. - const Triangulation& triang = _triangulation; - const Boundaries& boundaries = get_boundaries(); - for (Boundaries::size_type i = 0; i < boundaries.size(); ++i) { - const Boundary& boundary = boundaries[i]; - for (Boundary::size_type j = 0; j < boundary.size(); ++j) { - if (!_boundaries_visited[i][j]) { - // z values of start and end of this boundary edge. - double z_start = get_z(triang.get_triangle_point(boundary[j])); - double z_end = get_z(triang.get_triangle_point( - boundary[j].tri, (boundary[j].edge+1)%3)); - - // Does this boundary edge's z increase through upper level - // and/or decrease through lower level? - bool incr_upper = (z_start < upper_level && z_end >= upper_level); - bool decr_lower = (z_start >= lower_level && z_end < lower_level); - - if (decr_lower || incr_upper) { - // Start point for contour line, so follow it. - contour.push_back(ContourLine()); - ContourLine& contour_line = contour.back(); - TriEdge start_tri_edge = boundary[j]; - TriEdge tri_edge = start_tri_edge; - - // Traverse interior and boundaries until return to start. - bool on_upper = incr_upper; - do { - follow_interior(contour_line, tri_edge, true, - on_upper ? upper_level : lower_level, on_upper); - on_upper = follow_boundary(contour_line, tri_edge, - lower_level, upper_level, on_upper); - } while (tri_edge != start_tri_edge); - - // Close polygon. - contour_line.push_back(contour_line.front()); - } - } - } - } - - // Add full boundaries that lie between the lower and upper levels. These - // are boundaries that have not been touched by an internal contour line - // which are stored in _boundaries_used. - for (Boundaries::size_type i = 0; i < boundaries.size(); ++i) { - if (!_boundaries_used[i]) { - const Boundary& boundary = boundaries[i]; - double z = get_z(triang.get_triangle_point(boundary[0])); - if (z >= lower_level && z < upper_level) { - contour.push_back(ContourLine()); - ContourLine& contour_line = contour.back(); - for (Boundary::size_type j = 0; j < boundary.size(); ++j) - contour_line.push_back(triang.get_point_coords( - triang.get_triangle_point(boundary[j]))); - - // Close polygon. - contour_line.push_back(contour_line.front()); - } - } - } -} - -void TriContourGenerator::find_interior_lines(Contour& contour, - const double& level, - bool on_upper, - bool filled) -{ - const Triangulation& triang = _triangulation; - int ntri = triang.get_ntri(); - for (int tri = 0; tri < ntri; ++tri) { - int visited_index = (on_upper ? tri+ntri : tri); - - if (_interior_visited[visited_index] || triang.is_masked(tri)) - continue; // Triangle has already been visited or is masked. - - _interior_visited[visited_index] = true; - - // Determine edge via which to leave this triangle. - int edge = get_exit_edge(tri, level, on_upper); - assert(edge >= -1 && edge < 3 && "Invalid exit edge"); - if (edge == -1) - continue; // Contour does not pass through this triangle. - - // Found start of new contour line loop. - contour.push_back(ContourLine()); - ContourLine& contour_line = contour.back(); - TriEdge tri_edge = triang.get_neighbor_edge(tri, edge); - follow_interior(contour_line, tri_edge, false, level, on_upper); - - // Close line loop - contour_line.push_back(contour_line.front()); - } -} - -bool TriContourGenerator::follow_boundary(ContourLine& contour_line, - TriEdge& tri_edge, - const double& lower_level, - const double& upper_level, - bool on_upper) -{ - const Triangulation& triang = _triangulation; - const Boundaries& boundaries = get_boundaries(); - - // Have TriEdge to start at, need equivalent boundary edge. - int boundary, edge; - triang.get_boundary_edge(tri_edge, boundary, edge); - _boundaries_used[boundary] = true; - - bool stop = false; - bool first_edge = true; - double z_start, z_end = 0; - while (!stop) - { - assert(!_boundaries_visited[boundary][edge] && "Boundary already visited"); - _boundaries_visited[boundary][edge] = true; - - // z values of start and end points of boundary edge. - if (first_edge) - z_start = get_z(triang.get_triangle_point(tri_edge)); - else - z_start = z_end; - z_end = get_z(triang.get_triangle_point(tri_edge.tri, - (tri_edge.edge+1)%3)); - - if (z_end > z_start) { // z increasing. - if (!(!on_upper && first_edge) && - z_end >= lower_level && z_start < lower_level) { - stop = true; - on_upper = false; - } else if (z_end >= upper_level && z_start < upper_level) { - stop = true; - on_upper = true; - } - } else { // z decreasing. - if (!(on_upper && first_edge) && - z_start >= upper_level && z_end < upper_level) { - stop = true; - on_upper = true; - } else if (z_start >= lower_level && z_end < lower_level) { - stop = true; - on_upper = false; - } - } - - first_edge = false; - - if (!stop) { - // Move to next boundary edge, adding point to contour line. - edge = (edge+1) % (int)boundaries[boundary].size(); - tri_edge = boundaries[boundary][edge]; - contour_line.push_back(triang.get_point_coords( - triang.get_triangle_point(tri_edge))); - } - } - - return on_upper; -} - -void TriContourGenerator::follow_interior(ContourLine& contour_line, - TriEdge& tri_edge, - bool end_on_boundary, - const double& level, - bool on_upper) -{ - int& tri = tri_edge.tri; - int& edge = tri_edge.edge; - - // Initial point. - contour_line.push_back(edge_interp(tri, edge, level)); - - while (true) { - int visited_index = tri; - if (on_upper) - visited_index += _triangulation.get_ntri(); - - // Check for end not on boundary. - if (!end_on_boundary && _interior_visited[visited_index]) - break; // Reached start point, so return. - - // Determine edge by which to leave this triangle. - edge = get_exit_edge(tri, level, on_upper); - assert(edge >= 0 && edge < 3 && "Invalid exit edge"); - - _interior_visited[visited_index] = true; - - // Append new point to point set. - assert(edge >= 0 && edge < 3 && "Invalid triangle edge"); - contour_line.push_back(edge_interp(tri, edge, level)); - - // Move to next triangle. - TriEdge next_tri_edge = _triangulation.get_neighbor_edge(tri,edge); - - // Check if ending on a boundary. - if (end_on_boundary && next_tri_edge.tri == -1) - break; - - tri_edge = next_tri_edge; - assert(tri_edge.tri != -1 && "Invalid triangle for internal loop"); - } -} - -const TriContourGenerator::Boundaries& TriContourGenerator::get_boundaries() const -{ - return _triangulation.get_boundaries(); -} - -int TriContourGenerator::get_exit_edge(int tri, - const double& level, - bool on_upper) const -{ - assert(tri >= 0 && tri < _triangulation.get_ntri() && - "Triangle index out of bounds."); - - unsigned int config = - (get_z(_triangulation.get_triangle_point(tri, 0)) >= level) | - (get_z(_triangulation.get_triangle_point(tri, 1)) >= level) << 1 | - (get_z(_triangulation.get_triangle_point(tri, 2)) >= level) << 2; - - if (on_upper) config = 7-config; - - switch (config) { - case 0: return -1; - case 1: return 2; - case 2: return 0; - case 3: return 2; - case 4: return 1; - case 5: return 1; - case 6: return 0; - case 7: return -1; - default: assert(0 && "Invalid config value"); return -1; - } -} - -const double& TriContourGenerator::get_z(int point) const -{ - assert(point >= 0 && point < _triangulation.get_npoints() && - "Point index out of bounds."); - return _z.data()[point]; -} - -XY TriContourGenerator::interp(int point1, - int point2, - const double& level) const -{ - assert(point1 >= 0 && point1 < _triangulation.get_npoints() && - "Point index 1 out of bounds."); - assert(point2 >= 0 && point2 < _triangulation.get_npoints() && - "Point index 2 out of bounds."); - assert(point1 != point2 && "Identical points"); - double fraction = (get_z(point2) - level) / (get_z(point2) - get_z(point1)); - return _triangulation.get_point_coords(point1)*fraction + - _triangulation.get_point_coords(point2)*(1.0 - fraction); -} - - - -TrapezoidMapTriFinder::TrapezoidMapTriFinder(Triangulation& triangulation) - : _triangulation(triangulation), - _points(0), - _tree(0) -{} - -TrapezoidMapTriFinder::~TrapezoidMapTriFinder() -{ - clear(); -} - -bool -TrapezoidMapTriFinder::add_edge_to_tree(const Edge& edge) -{ - std::vector<Trapezoid*> trapezoids; - if (!find_trapezoids_intersecting_edge(edge, trapezoids)) - return false; - assert(!trapezoids.empty() && "No trapezoids intersect edge"); - - const Point* p = edge.left; - const Point* q = edge.right; - Trapezoid* left_old = 0; // old trapezoid to the left. - Trapezoid* left_below = 0; // below trapezoid to the left. - Trapezoid* left_above = 0; // above trapezoid to the left. - - // Iterate through trapezoids intersecting edge from left to right. - // Replace each old trapezoid with 2+ new trapezoids, and replace its - // corresponding nodes in the search tree with new nodes. - size_t ntraps = trapezoids.size(); - for (size_t i = 0; i < ntraps; ++i) { - Trapezoid* old = trapezoids[i]; // old trapezoid to replace. - bool start_trap = (i == 0); - bool end_trap = (i == ntraps-1); - bool have_left = (start_trap && edge.left != old->left); - bool have_right = (end_trap && edge.right != old->right); - - // Old trapezoid is replaced by up to 4 new trapezoids: left is to the - // left of the start point p, below/above are below/above the edge - // inserted, and right is to the right of the end point q. - Trapezoid* left = 0; - Trapezoid* below = 0; - Trapezoid* above = 0; - Trapezoid* right = 0; - - // There are 4 different cases here depending on whether the old - // trapezoid in question is the start and/or end trapezoid of those - // that intersect the edge inserted. There is some code duplication - // here but it is much easier to understand this way rather than - // interleave the 4 different cases with many more if-statements. - if (start_trap && end_trap) { - // Edge intersects a single trapezoid. - if (have_left) - left = new Trapezoid(old->left, p, old->below, old->above); - below = new Trapezoid(p, q, old->below, edge); - above = new Trapezoid(p, q, edge, old->above); - if (have_right) - right = new Trapezoid(q, old->right, old->below, old->above); - - // Set pairs of trapezoid neighbours. - if (have_left) { - left->set_lower_left(old->lower_left); - left->set_upper_left(old->upper_left); - left->set_lower_right(below); - left->set_upper_right(above); - } - else { - below->set_lower_left(old->lower_left); - above->set_upper_left(old->upper_left); - } - - if (have_right) { - right->set_lower_right(old->lower_right); - right->set_upper_right(old->upper_right); - below->set_lower_right(right); - above->set_upper_right(right); - } - else { - below->set_lower_right(old->lower_right); - above->set_upper_right(old->upper_right); - } - } - else if (start_trap) { - // Old trapezoid is the first of 2+ trapezoids that the edge - // intersects. - if (have_left) - left = new Trapezoid(old->left, p, old->below, old->above); - below = new Trapezoid(p, old->right, old->below, edge); - above = new Trapezoid(p, old->right, edge, old->above); - - // Set pairs of trapezoid neighbours. - if (have_left) { - left->set_lower_left(old->lower_left); - left->set_upper_left(old->upper_left); - left->set_lower_right(below); - left->set_upper_right(above); - } - else { - below->set_lower_left(old->lower_left); - above->set_upper_left(old->upper_left); - } - - below->set_lower_right(old->lower_right); - above->set_upper_right(old->upper_right); - } - else if (end_trap) { - // Old trapezoid is the last of 2+ trapezoids that the edge - // intersects. - if (left_below->below == old->below) { - below = left_below; - below->right = q; - } - else - below = new Trapezoid(old->left, q, old->below, edge); - - if (left_above->above == old->above) { - above = left_above; - above->right = q; - } - else - above = new Trapezoid(old->left, q, edge, old->above); - - if (have_right) - right = new Trapezoid(q, old->right, old->below, old->above); - - // Set pairs of trapezoid neighbours. - if (have_right) { - right->set_lower_right(old->lower_right); - right->set_upper_right(old->upper_right); - below->set_lower_right(right); - above->set_upper_right(right); - } - else { - below->set_lower_right(old->lower_right); - above->set_upper_right(old->upper_right); - } - - // Connect to new trapezoids replacing prevOld. - if (below != left_below) { - below->set_upper_left(left_below); - if (old->lower_left == left_old) - below->set_lower_left(left_below); - else - below->set_lower_left(old->lower_left); - } - - if (above != left_above) { - above->set_lower_left(left_above); - if (old->upper_left == left_old) - above->set_upper_left(left_above); - else - above->set_upper_left(old->upper_left); - } - } - else { // Middle trapezoid. - // Old trapezoid is neither the first nor last of the 3+ trapezoids - // that the edge intersects. - if (left_below->below == old->below) { - below = left_below; - below->right = old->right; - } - else - below = new Trapezoid(old->left, old->right, old->below, edge); - - if (left_above->above == old->above) { - above = left_above; - above->right = old->right; - } - else - above = new Trapezoid(old->left, old->right, edge, old->above); - - // Connect to new trapezoids replacing prevOld. - if (below != left_below) { // below is new. - below->set_upper_left(left_below); - if (old->lower_left == left_old) - below->set_lower_left(left_below); - else - below->set_lower_left(old->lower_left); - } - - if (above != left_above) { // above is new. - above->set_lower_left(left_above); - if (old->upper_left == left_old) - above->set_upper_left(left_above); - else - above->set_upper_left(old->upper_left); - } - - below->set_lower_right(old->lower_right); - above->set_upper_right(old->upper_right); - } - - // Create new nodes to add to search tree. Below and above trapezoids - // may already have owning trapezoid nodes, in which case reuse them. - Node* new_top_node = new Node( - &edge, - below == left_below ? below->trapezoid_node : new Node(below), - above == left_above ? above->trapezoid_node : new Node(above)); - if (have_right) - new_top_node = new Node(q, new_top_node, new Node(right)); - if (have_left) - new_top_node = new Node(p, new Node(left), new_top_node); - - // Insert new_top_node in correct position or positions in search tree. - Node* old_node = old->trapezoid_node; - if (old_node == _tree) - _tree = new_top_node; - else - old_node->replace_with(new_top_node); - - // old_node has been removed from all of its parents and is no longer - // needed. - assert(old_node->has_no_parents() && "Node should have no parents"); - delete old_node; - - // Clearing up. - if (!end_trap) { - // Prepare for next loop. - left_old = old; - left_above = above; - left_below = below; - } - } - - return true; -} - -void -TrapezoidMapTriFinder::clear() -{ - delete [] _points; - _points = 0; - - _edges.clear(); - - delete _tree; - _tree = 0; -} - -TrapezoidMapTriFinder::TriIndexArray -TrapezoidMapTriFinder::find_many(const CoordinateArray& x, - const CoordinateArray& y) -{ - if (x.ndim() != 1 || x.shape(0) != y.shape(0)) - throw std::invalid_argument( - "x and y must be array-like with same shape"); - - // Create integer array to return. - auto n = x.shape(0); - TriIndexArray tri_indices_array(n); - auto tri_indices = tri_indices_array.mutable_unchecked<1>(); - auto x_data = x.data(); - auto y_data = y.data(); - - // Fill returned array. - for (py::ssize_t i = 0; i < n; ++i) - tri_indices(i) = find_one(XY(x_data[i], y_data[i])); - - return tri_indices_array; -} - -int -TrapezoidMapTriFinder::find_one(const XY& xy) -{ - const Node* node = _tree->search(xy); - assert(node != 0 && "Search tree for point returned null node"); - return node->get_tri(); -} - -bool -TrapezoidMapTriFinder::find_trapezoids_intersecting_edge( - const Edge& edge, - std::vector<Trapezoid*>& trapezoids) -{ - // This is the FollowSegment algorithm of de Berg et al, with some extra - // checks to deal with simple colinear (i.e. invalid) triangles. - trapezoids.clear(); - Trapezoid* trapezoid = _tree->search(edge); - if (trapezoid == 0) { - assert(trapezoid != 0 && "search(edge) returns null trapezoid"); - return false; - } - - trapezoids.push_back(trapezoid); - while (edge.right->is_right_of(*trapezoid->right)) { - int orient = edge.get_point_orientation(*trapezoid->right); - if (orient == 0) { - if (edge.point_below == trapezoid->right) - orient = +1; - else if (edge.point_above == trapezoid->right) - orient = -1; - else { - assert(0 && "Unable to deal with point on edge"); - return false; - } - } - - if (orient == -1) - trapezoid = trapezoid->lower_right; - else if (orient == +1) - trapezoid = trapezoid->upper_right; - - if (trapezoid == 0) { - assert(0 && "Expected trapezoid neighbor"); - return false; - } - trapezoids.push_back(trapezoid); - } - - return true; -} - -py::list -TrapezoidMapTriFinder::get_tree_stats() -{ - NodeStats stats; - _tree->get_stats(0, stats); - - py::list ret(7); - ret[0] = stats.node_count; - ret[1] = stats.unique_nodes.size(), - ret[2] = stats.trapezoid_count, - ret[3] = stats.unique_trapezoid_nodes.size(), - ret[4] = stats.max_parent_count, - ret[5] = stats.max_depth, - ret[6] = stats.sum_trapezoid_depth / stats.trapezoid_count; - return ret; -} - -void -TrapezoidMapTriFinder::initialize() -{ - clear(); - const Triangulation& triang = _triangulation; - - // Set up points array, which contains all of the points in the - // triangulation plus the 4 corners of the enclosing rectangle. - int npoints = triang.get_npoints(); - _points = new Point[npoints + 4]; - BoundingBox bbox; - for (int i = 0; i < npoints; ++i) { - XY xy = triang.get_point_coords(i); - // Avoid problems with -0.0 values different from 0.0 - if (xy.x == -0.0) - xy.x = 0.0; - if (xy.y == -0.0) - xy.y = 0.0; - _points[i] = Point(xy); - bbox.add(xy); - } - - // Last 4 points are corner points of enclosing rectangle. Enclosing - // rectangle made slightly larger in case corner points are already in the - // triangulation. - if (bbox.empty) { - bbox.add(XY(0.0, 0.0)); - bbox.add(XY(1.0, 1.0)); - } - else { - const double small = 0.1; // Any value > 0.0 - bbox.expand( (bbox.upper - bbox.lower)*small ); - } - _points[npoints ] = Point(bbox.lower); // SW point. - _points[npoints+1] = Point(bbox.upper.x, bbox.lower.y); // SE point. - _points[npoints+2] = Point(bbox.lower.x, bbox.upper.y); // NW point. - _points[npoints+3] = Point(bbox.upper); // NE point. - - // Set up edges array. - // First the bottom and top edges of the enclosing rectangle. - _edges.push_back(Edge(&_points[npoints], &_points[npoints+1],-1,-1,0,0)); - _edges.push_back(Edge(&_points[npoints+2],&_points[npoints+3],-1,-1,0,0)); - - // Add all edges in the triangulation that point to the right. Do not - // explicitly include edges that point to the left as the neighboring - // triangle will supply that, unless there is no such neighbor. - int ntri = triang.get_ntri(); - for (int tri = 0; tri < ntri; ++tri) { - if (!triang.is_masked(tri)) { - for (int edge = 0; edge < 3; ++edge) { - Point* start = _points + triang.get_triangle_point(tri,edge); - Point* end = _points + - triang.get_triangle_point(tri,(edge+1)%3); - Point* other = _points + - triang.get_triangle_point(tri,(edge+2)%3); - TriEdge neighbor = triang.get_neighbor_edge(tri,edge); - if (end->is_right_of(*start)) { - const Point* neighbor_point_below = (neighbor.tri == -1) ? - 0 : _points + triang.get_triangle_point( - neighbor.tri, (neighbor.edge+2)%3); - _edges.push_back(Edge(start, end, neighbor.tri, tri, - neighbor_point_below, other)); - } - else if (neighbor.tri == -1) - _edges.push_back(Edge(end, start, tri, -1, other, 0)); - - // Set triangle associated with start point if not already set. - if (start->tri == -1) - start->tri = tri; - } - } - } - - // Initial trapezoid is enclosing rectangle. - _tree = new Node(new Trapezoid(&_points[npoints], &_points[npoints+1], - _edges[0], _edges[1])); - _tree->assert_valid(false); - - // Randomly shuffle all edges other than first 2. - std::mt19937 rng(1234); - std::shuffle(_edges.begin()+2, _edges.end(), rng); - - // Add edges, one at a time, to tree. - size_t nedges = _edges.size(); - for (size_t index = 2; index < nedges; ++index) { - if (!add_edge_to_tree(_edges[index])) - throw std::runtime_error("Triangulation is invalid"); - _tree->assert_valid(index == nedges-1); - } -} - -void -TrapezoidMapTriFinder::print_tree() -{ - assert(_tree != 0 && "Null Node tree"); - _tree->print(); -} - -TrapezoidMapTriFinder::Edge::Edge(const Point* left_, - const Point* right_, - int triangle_below_, - int triangle_above_, - const Point* point_below_, - const Point* point_above_) - : left(left_), - right(right_), - triangle_below(triangle_below_), - triangle_above(triangle_above_), - point_below(point_below_), - point_above(point_above_) -{ - assert(left != 0 && "Null left point"); - assert(right != 0 && "Null right point"); - assert(right->is_right_of(*left) && "Incorrect point order"); - assert(triangle_below >= -1 && "Invalid triangle below index"); - assert(triangle_above >= -1 && "Invalid triangle above index"); -} - -int -TrapezoidMapTriFinder::Edge::get_point_orientation(const XY& xy) const -{ - double cross_z = (xy - *left).cross_z(*right - *left); - return (cross_z > 0.0) ? +1 : ((cross_z < 0.0) ? -1 : 0); -} - -double -TrapezoidMapTriFinder::Edge::get_slope() const -{ - // Divide by zero is acceptable here. - XY diff = *right - *left; - return diff.y / diff.x; -} - -double -TrapezoidMapTriFinder::Edge::get_y_at_x(const double& x) const -{ - if (left->x == right->x) { - // If edge is vertical, return lowest y from left point. - assert(x == left->x && "x outside of edge"); - return left->y; - } - else { - // Equation of line: left + lambda*(right - left) = xy. - // i.e. left.x + lambda(right.x - left.x) = x and similar for y. - double lambda = (x - left->x) / (right->x - left->x); - assert(lambda >= 0 && lambda <= 1.0 && "Lambda out of bounds"); - return left->y + lambda*(right->y - left->y); - } -} - -bool -TrapezoidMapTriFinder::Edge::has_point(const Point* point) const -{ - assert(point != 0 && "Null point"); - return (left == point || right == point); -} - -bool -TrapezoidMapTriFinder::Edge::operator==(const Edge& other) const -{ - return this == &other; -} - -void -TrapezoidMapTriFinder::Edge::print_debug() const -{ - std::cout << "Edge " << *this << " tri_below=" << triangle_below - << " tri_above=" << triangle_above << std::endl; -} - -TrapezoidMapTriFinder::Node::Node(const Point* point, Node* left, Node* right) - : _type(Type_XNode) -{ - assert(point != 0 && "Invalid point"); - assert(left != 0 && "Invalid left node"); - assert(right != 0 && "Invalid right node"); - _union.xnode.point = point; - _union.xnode.left = left; - _union.xnode.right = right; - left->add_parent(this); - right->add_parent(this); -} - -TrapezoidMapTriFinder::Node::Node(const Edge* edge, Node* below, Node* above) - : _type(Type_YNode) -{ - assert(edge != 0 && "Invalid edge"); - assert(below != 0 && "Invalid below node"); - assert(above != 0 && "Invalid above node"); - _union.ynode.edge = edge; - _union.ynode.below = below; - _union.ynode.above = above; - below->add_parent(this); - above->add_parent(this); -} - -TrapezoidMapTriFinder::Node::Node(Trapezoid* trapezoid) - : _type(Type_TrapezoidNode) -{ - assert(trapezoid != 0 && "Null Trapezoid"); - _union.trapezoid = trapezoid; - trapezoid->trapezoid_node = this; -} - -TrapezoidMapTriFinder::Node::~Node() -{ - switch (_type) { - case Type_XNode: - if (_union.xnode.left->remove_parent(this)) - delete _union.xnode.left; - if (_union.xnode.right->remove_parent(this)) - delete _union.xnode.right; - break; - case Type_YNode: - if (_union.ynode.below->remove_parent(this)) - delete _union.ynode.below; - if (_union.ynode.above->remove_parent(this)) - delete _union.ynode.above; - break; - case Type_TrapezoidNode: - delete _union.trapezoid; - break; - } -} - -void -TrapezoidMapTriFinder::Node::add_parent(Node* parent) -{ - assert(parent != 0 && "Null parent"); - assert(parent != this && "Cannot be parent of self"); - assert(!has_parent(parent) && "Parent already in collection"); - _parents.push_back(parent); -} - -void -TrapezoidMapTriFinder::Node::assert_valid(bool tree_complete) const -{ -#ifndef NDEBUG - // Check parents. - for (Parents::const_iterator it = _parents.begin(); - it != _parents.end(); ++it) { - Node* parent = *it; - assert(parent != this && "Cannot be parent of self"); - assert(parent->has_child(this) && "Parent missing child"); - } - - // Check children, and recurse. - switch (_type) { - case Type_XNode: - assert(_union.xnode.left != 0 && "Null left child"); - assert(_union.xnode.left->has_parent(this) && "Incorrect parent"); - assert(_union.xnode.right != 0 && "Null right child"); - assert(_union.xnode.right->has_parent(this) && "Incorrect parent"); - _union.xnode.left->assert_valid(tree_complete); - _union.xnode.right->assert_valid(tree_complete); - break; - case Type_YNode: - assert(_union.ynode.below != 0 && "Null below child"); - assert(_union.ynode.below->has_parent(this) && "Incorrect parent"); - assert(_union.ynode.above != 0 && "Null above child"); - assert(_union.ynode.above->has_parent(this) && "Incorrect parent"); - _union.ynode.below->assert_valid(tree_complete); - _union.ynode.above->assert_valid(tree_complete); - break; - case Type_TrapezoidNode: - assert(_union.trapezoid != 0 && "Null trapezoid"); - assert(_union.trapezoid->trapezoid_node == this && - "Incorrect trapezoid node"); - _union.trapezoid->assert_valid(tree_complete); - break; - } -#endif -} - -void -TrapezoidMapTriFinder::Node::get_stats(int depth, - NodeStats& stats) const -{ - stats.node_count++; - if (depth > stats.max_depth) - stats.max_depth = depth; - bool new_node = stats.unique_nodes.insert(this).second; - if (new_node) - stats.max_parent_count = std::max(stats.max_parent_count, - static_cast<long>(_parents.size())); - - switch (_type) { - case Type_XNode: - _union.xnode.left->get_stats(depth+1, stats); - _union.xnode.right->get_stats(depth+1, stats); - break; - case Type_YNode: - _union.ynode.below->get_stats(depth+1, stats); - _union.ynode.above->get_stats(depth+1, stats); - break; - default: // Type_TrapezoidNode: - stats.unique_trapezoid_nodes.insert(this); - stats.trapezoid_count++; - stats.sum_trapezoid_depth += depth; - break; - } -} - -int -TrapezoidMapTriFinder::Node::get_tri() const -{ - switch (_type) { - case Type_XNode: - return _union.xnode.point->tri; - case Type_YNode: - if (_union.ynode.edge->triangle_above != -1) - return _union.ynode.edge->triangle_above; - else - return _union.ynode.edge->triangle_below; - default: // Type_TrapezoidNode: - assert(_union.trapezoid->below.triangle_above == - _union.trapezoid->above.triangle_below && - "Inconsistent triangle indices from trapezoid edges"); - return _union.trapezoid->below.triangle_above; - } -} - -bool -TrapezoidMapTriFinder::Node::has_child(const Node* child) const -{ - assert(child != 0 && "Null child node"); - switch (_type) { - case Type_XNode: - return (_union.xnode.left == child || _union.xnode.right == child); - case Type_YNode: - return (_union.ynode.below == child || - _union.ynode.above == child); - default: // Type_TrapezoidNode: - return false; - } -} - -bool -TrapezoidMapTriFinder::Node::has_no_parents() const -{ - return _parents.empty(); -} - -bool -TrapezoidMapTriFinder::Node::has_parent(const Node* parent) const -{ - return (std::find(_parents.begin(), _parents.end(), parent) != - _parents.end()); -} - -void -TrapezoidMapTriFinder::Node::print(int depth /* = 0 */) const -{ - for (int i = 0; i < depth; ++i) std::cout << " "; - switch (_type) { - case Type_XNode: - std::cout << "XNode " << *_union.xnode.point << std::endl; - _union.xnode.left->print(depth + 1); - _union.xnode.right->print(depth + 1); - break; - case Type_YNode: - std::cout << "YNode " << *_union.ynode.edge << std::endl; - _union.ynode.below->print(depth + 1); - _union.ynode.above->print(depth + 1); - break; - case Type_TrapezoidNode: - std::cout << "Trapezoid ll=" - << _union.trapezoid->get_lower_left_point() << " lr=" - << _union.trapezoid->get_lower_right_point() << " ul=" - << _union.trapezoid->get_upper_left_point() << " ur=" - << _union.trapezoid->get_upper_right_point() << std::endl; - break; - } -} - -bool -TrapezoidMapTriFinder::Node::remove_parent(Node* parent) -{ - assert(parent != 0 && "Null parent"); - assert(parent != this && "Cannot be parent of self"); - Parents::iterator it = std::find(_parents.begin(), _parents.end(), parent); - assert(it != _parents.end() && "Parent not in collection"); - _parents.erase(it); - return _parents.empty(); -} - -void -TrapezoidMapTriFinder::Node::replace_child(Node* old_child, Node* new_child) -{ - switch (_type) { - case Type_XNode: - assert((_union.xnode.left == old_child || - _union.xnode.right == old_child) && "Not a child Node"); - assert(new_child != 0 && "Null child node"); - if (_union.xnode.left == old_child) - _union.xnode.left = new_child; - else - _union.xnode.right = new_child; - break; - case Type_YNode: - assert((_union.ynode.below == old_child || - _union.ynode.above == old_child) && "Not a child node"); - assert(new_child != 0 && "Null child node"); - if (_union.ynode.below == old_child) - _union.ynode.below = new_child; - else - _union.ynode.above = new_child; - break; - case Type_TrapezoidNode: - assert(0 && "Invalid type for this operation"); - break; - } - old_child->remove_parent(this); - new_child->add_parent(this); -} - -void -TrapezoidMapTriFinder::Node::replace_with(Node* new_node) -{ - assert(new_node != 0 && "Null replacement node"); - // Replace child of each parent with new_node. As each has parent has its - // child replaced it is removed from the _parents collection. - while (!_parents.empty()) - _parents.front()->replace_child(this, new_node); -} - -const TrapezoidMapTriFinder::Node* -TrapezoidMapTriFinder::Node::search(const XY& xy) -{ - switch (_type) { - case Type_XNode: - if (xy == *_union.xnode.point) - return this; - else if (xy.is_right_of(*_union.xnode.point)) - return _union.xnode.right->search(xy); - else - return _union.xnode.left->search(xy); - case Type_YNode: { - int orient = _union.ynode.edge->get_point_orientation(xy); - if (orient == 0) - return this; - else if (orient < 0) - return _union.ynode.above->search(xy); - else - return _union.ynode.below->search(xy); - } - default: // Type_TrapezoidNode: - return this; - } -} - -TrapezoidMapTriFinder::Trapezoid* -TrapezoidMapTriFinder::Node::search(const Edge& edge) -{ - switch (_type) { - case Type_XNode: - if (edge.left == _union.xnode.point) - return _union.xnode.right->search(edge); - else { - if (edge.left->is_right_of(*_union.xnode.point)) - return _union.xnode.right->search(edge); - else - return _union.xnode.left->search(edge); - } - case Type_YNode: - if (edge.left == _union.ynode.edge->left) { - // Coinciding left edge points. - if (edge.get_slope() == _union.ynode.edge->get_slope()) { - if (_union.ynode.edge->triangle_above == - edge.triangle_below) - return _union.ynode.above->search(edge); - else if (_union.ynode.edge->triangle_below == - edge.triangle_above) - return _union.ynode.below->search(edge); - else { - assert(0 && - "Invalid triangulation, common left points"); - return 0; - } - } - if (edge.get_slope() > _union.ynode.edge->get_slope()) - return _union.ynode.above->search(edge); - else - return _union.ynode.below->search(edge); - } - else if (edge.right == _union.ynode.edge->right) { - // Coinciding right edge points. - if (edge.get_slope() == _union.ynode.edge->get_slope()) { - if (_union.ynode.edge->triangle_above == - edge.triangle_below) - return _union.ynode.above->search(edge); - else if (_union.ynode.edge->triangle_below == - edge.triangle_above) - return _union.ynode.below->search(edge); - else { - assert(0 && - "Invalid triangulation, common right points"); - return 0; - } - } - if (edge.get_slope() > _union.ynode.edge->get_slope()) - return _union.ynode.below->search(edge); - else - return _union.ynode.above->search(edge); - } - else { - int orient = - _union.ynode.edge->get_point_orientation(*edge.left); - if (orient == 0) { - // edge.left lies on _union.ynode.edge - if (_union.ynode.edge->point_above != 0 && - edge.has_point(_union.ynode.edge->point_above)) - orient = -1; - else if (_union.ynode.edge->point_below != 0 && - edge.has_point(_union.ynode.edge->point_below)) - orient = +1; - else { - assert(0 && "Invalid triangulation, point on edge"); - return 0; - } - } - if (orient < 0) - return _union.ynode.above->search(edge); - else - return _union.ynode.below->search(edge); - } - default: // Type_TrapezoidNode: - return _union.trapezoid; - } -} - -TrapezoidMapTriFinder::Trapezoid::Trapezoid(const Point* left_, - const Point* right_, - const Edge& below_, - const Edge& above_) - : left(left_), right(right_), below(below_), above(above_), - lower_left(0), lower_right(0), upper_left(0), upper_right(0), - trapezoid_node(0) -{ - assert(left != 0 && "Null left point"); - assert(right != 0 && "Null right point"); - assert(right->is_right_of(*left) && "Incorrect point order"); -} - -void -TrapezoidMapTriFinder::Trapezoid::assert_valid(bool tree_complete) const -{ -#ifndef NDEBUG - assert(left != 0 && "Null left point"); - assert(right != 0 && "Null right point"); - - if (lower_left != 0) { - assert(lower_left->below == below && - lower_left->lower_right == this && - "Incorrect lower_left trapezoid"); - assert(get_lower_left_point() == lower_left->get_lower_right_point() && - "Incorrect lower left point"); - } - - if (lower_right != 0) { - assert(lower_right->below == below && - lower_right->lower_left == this && - "Incorrect lower_right trapezoid"); - assert(get_lower_right_point() == lower_right->get_lower_left_point() && - "Incorrect lower right point"); - } - - if (upper_left != 0) { - assert(upper_left->above == above && - upper_left->upper_right == this && - "Incorrect upper_left trapezoid"); - assert(get_upper_left_point() == upper_left->get_upper_right_point() && - "Incorrect upper left point"); - } - - if (upper_right != 0) { - assert(upper_right->above == above && - upper_right->upper_left == this && - "Incorrect upper_right trapezoid"); - assert(get_upper_right_point() == upper_right->get_upper_left_point() && - "Incorrect upper right point"); - } - - assert(trapezoid_node != 0 && "Null trapezoid_node"); - - if (tree_complete) { - assert(below.triangle_above == above.triangle_below && - "Inconsistent triangle indices from trapezoid edges"); - } -#endif -} - -XY -TrapezoidMapTriFinder::Trapezoid::get_lower_left_point() const -{ - double x = left->x; - return XY(x, below.get_y_at_x(x)); -} - -XY -TrapezoidMapTriFinder::Trapezoid::get_lower_right_point() const -{ - double x = right->x; - return XY(x, below.get_y_at_x(x)); -} - -XY -TrapezoidMapTriFinder::Trapezoid::get_upper_left_point() const -{ - double x = left->x; - return XY(x, above.get_y_at_x(x)); -} - -XY -TrapezoidMapTriFinder::Trapezoid::get_upper_right_point() const -{ - double x = right->x; - return XY(x, above.get_y_at_x(x)); -} - -void -TrapezoidMapTriFinder::Trapezoid::print_debug() const -{ - std::cout << "Trapezoid " << this - << " left=" << *left - << " right=" << *right - << " below=" << below - << " above=" << above - << " ll=" << lower_left - << " lr=" << lower_right - << " ul=" << upper_left - << " ur=" << upper_right - << " node=" << trapezoid_node - << " llp=" << get_lower_left_point() - << " lrp=" << get_lower_right_point() - << " ulp=" << get_upper_left_point() - << " urp=" << get_upper_right_point() << std::endl; -} - -void -TrapezoidMapTriFinder::Trapezoid::set_lower_left(Trapezoid* lower_left_) -{ - lower_left = lower_left_; - if (lower_left != 0) - lower_left->lower_right = this; -} - -void -TrapezoidMapTriFinder::Trapezoid::set_lower_right(Trapezoid* lower_right_) -{ - lower_right = lower_right_; - if (lower_right != 0) - lower_right->lower_left = this; -} - -void -TrapezoidMapTriFinder::Trapezoid::set_upper_left(Trapezoid* upper_left_) -{ - upper_left = upper_left_; - if (upper_left != 0) - upper_left->upper_right = this; -} - -void -TrapezoidMapTriFinder::Trapezoid::set_upper_right(Trapezoid* upper_right_) -{ - upper_right = upper_right_; - if (upper_right != 0) - upper_right->upper_left = this; -} diff --git a/contrib/python/matplotlib/py3/src/tri/_tri.h b/contrib/python/matplotlib/py3/src/tri/_tri.h deleted file mode 100644 index c176b4c0e8..0000000000 --- a/contrib/python/matplotlib/py3/src/tri/_tri.h +++ /dev/null @@ -1,799 +0,0 @@ -/* - * Unstructured triangular grid functions, particularly contouring. - * - * There are two main classes: Triangulation and TriContourGenerator. - * - * Triangulation - * ------------- - * Triangulation is an unstructured triangular grid with npoints and ntri - * triangles. It consists of point x and y coordinates, and information about - * the triangulation stored in an integer array of shape (ntri,3) called - * triangles. Each triangle is represented by three point indices (in the - * range 0 to npoints-1) that comprise the triangle, ordered anticlockwise. - * There is an optional mask of length ntri which can be used to mask out - * triangles and has the same result as removing those triangles from the - * 'triangles' array. - * - * A particular edge of a triangulation is termed a TriEdge, which is a - * triangle index and an edge index in the range 0 to 2. TriEdge(tri,edge) - * refers to the edge that starts at point index triangles(tri,edge) and ends - * at point index triangles(tri,(edge+1)%3). - * - * Various derived fields are calculated when they are first needed. The - * triangle connectivity is stored in a neighbors array of shape (ntri,3) such - * that neighbors(tri,edge) is the index of the triangle that adjoins the - * TriEdge(tri,edge), or -1 if there is no such neighbor. - * - * A triangulation has one or more boundaries, each of which is a 1D array of - * the TriEdges that comprise the boundary, in order following the boundary - * with non-masked triangles on the left. - * - * TriContourGenerator - * ------------------- - * A TriContourGenerator generates contours for a particular Triangulation. - * The process followed is different for non-filled and filled contours, with - * one and two contour levels respectively. In both cases boundary contour - * lines are found first, then interior lines. - * - * Boundary lines start and end on a boundary. They are found by traversing - * the triangulation boundary edges until a suitable start point is found, and - * then the contour line is followed across the interior of the triangulation - * until it ends on another boundary edge. For a non-filled contour this - * completes a line, whereas a filled contour continues by following the - * boundary around until either another boundary start point is found or the - * start of the contour line is reached. Filled contour generation stores - * boolean flags to indicate which boundary edges have already been traversed - * so that they are not dealt with twice. Similar flags are used to indicate - * which triangles have been used when following interior lines. - * - * Interior lines do not intersect any boundaries. They are found by - * traversing all triangles that have not yet been visited until a suitable - * starting point is found, and then the contour line is followed across the - * interior of the triangulation until it returns to the start point. For - * filled contours this process is repeated for both lower and upper contour - * levels, and the direction of traversal is reversed for upper contours. - * - * Working out in which direction a contour line leaves a triangle uses the - * a lookup table. A triangle has three points, each of which has a z-value - * which is either less than the contour level or not. Hence there are 8 - * configurations to deal with, 2 of which do not have a contour line (all - * points below or above (including the same as) the contour level) and 6 that - * do. See the function get_exit_edge for details. - */ -#ifndef MPL_TRI_H -#define MPL_TRI_H - -#include <pybind11/pybind11.h> -#include <pybind11/numpy.h> - -#include <iostream> -#include <list> -#include <map> -#include <set> -#include <vector> - -namespace py = pybind11; - - -/* An edge of a triangle consisting of an triangle index in the range 0 to - * ntri-1 and an edge index in the range 0 to 2. Edge i goes from the - * triangle's point i to point (i+1)%3. */ -struct TriEdge -{ - TriEdge(); - TriEdge(int tri_, int edge_); - bool operator<(const TriEdge& other) const; - bool operator==(const TriEdge& other) const; - bool operator!=(const TriEdge& other) const; - friend std::ostream& operator<<(std::ostream& os, const TriEdge& tri_edge); - - int tri, edge; -}; - -// 2D point with x,y coordinates. -struct XY -{ - XY(); - XY(const double& x_, const double& y_); - double angle() const; // Angle in radians with respect to x-axis. - double cross_z(const XY& other) const; // z-component of cross product. - bool is_right_of(const XY& other) const; // Compares x then y. - bool operator==(const XY& other) const; - bool operator!=(const XY& other) const; - XY operator*(const double& multiplier) const; - const XY& operator+=(const XY& other); - const XY& operator-=(const XY& other); - XY operator+(const XY& other) const; - XY operator-(const XY& other) const; - friend std::ostream& operator<<(std::ostream& os, const XY& xy); - - double x, y; -}; - -// 3D point with x,y,z coordinates. -struct XYZ -{ - XYZ(const double& x_, const double& y_, const double& z_); - XYZ cross(const XYZ& other) const; - double dot(const XYZ& other) const; - XYZ operator-(const XYZ& other) const; - friend std::ostream& operator<<(std::ostream& os, const XYZ& xyz); - - double x, y, z; -}; - -// 2D bounding box, which may be empty. -class BoundingBox -{ -public: - BoundingBox(); - void add(const XY& point); - void expand(const XY& delta); - - // Consider these member variables read-only. - bool empty; - XY lower, upper; -}; - -/* A single line of a contour, which may be a closed line loop or an open line - * strip. Identical adjacent points are avoided using push_back(), and a closed - * line loop should also not have identical first and last points. */ -class ContourLine : public std::vector<XY> -{ -public: - ContourLine(); - void push_back(const XY& point); - void write() const; -}; - -// A Contour is a collection of zero or more ContourLines. -typedef std::vector<ContourLine> Contour; - -// Debug contour writing function. -void write_contour(const Contour& contour); - - - - -/* Triangulation with npoints points and ntri triangles. Derived fields are - * calculated when they are first needed. */ -class Triangulation -{ -public: - typedef py::array_t<double, py::array::c_style | py::array::forcecast> CoordinateArray; - typedef py::array_t<double, py::array::c_style | py::array::forcecast> TwoCoordinateArray; - typedef py::array_t<int, py::array::c_style | py::array::forcecast> TriangleArray; - typedef py::array_t<bool, py::array::c_style | py::array::forcecast> MaskArray; - typedef py::array_t<int, py::array::c_style | py::array::forcecast> EdgeArray; - typedef py::array_t<int, py::array::c_style | py::array::forcecast> NeighborArray; - - /* A single boundary is a vector of the TriEdges that make up that boundary - * following it around with unmasked triangles on the left. */ - typedef std::vector<TriEdge> Boundary; - typedef std::vector<Boundary> Boundaries; - - /* Constructor with optional mask, edges and neighbors. The latter two - * are calculated when first needed. - * x: double array of shape (npoints) of points' x-coordinates. - * y: double array of shape (npoints) of points' y-coordinates. - * triangles: int array of shape (ntri,3) of triangle point indices. - * Those ordered clockwise are changed to be anticlockwise. - * mask: Optional bool array of shape (ntri) indicating which triangles - * are masked. - * edges: Optional int array of shape (?,2) of start and end point - * indices, each edge (start,end and end,start) appearing only - * once. - * neighbors: Optional int array of shape (ntri,3) indicating which - * triangles are the neighbors of which TriEdges, or -1 if - * there is no such neighbor. - * correct_triangle_orientations: Whether or not should correct triangle - * orientations so that vertices are - * ordered anticlockwise. */ - Triangulation(const CoordinateArray& x, - const CoordinateArray& y, - const TriangleArray& triangles, - const MaskArray& mask, - const EdgeArray& edges, - const NeighborArray& neighbors, - bool correct_triangle_orientations); - - /* Calculate plane equation coefficients for all unmasked triangles from - * the point (x,y) coordinates and point z-array of shape (npoints) passed - * in via the args. Returned array has shape (npoints,3) and allows - * z-value at (x,y) coordinates in triangle tri to be calculated using - * z = array[tri,0]*x + array[tri,1]*y + array[tri,2]. */ - TwoCoordinateArray calculate_plane_coefficients(const CoordinateArray& z); - - // Return the boundaries collection, creating it if necessary. - const Boundaries& get_boundaries() const; - - // Return which boundary and boundary edge the specified TriEdge is. - void get_boundary_edge(const TriEdge& triEdge, - int& boundary, - int& edge) const; - - /* Return the edges array, creating it if necessary. */ - EdgeArray& get_edges(); - - /* Return the triangle index of the neighbor of the specified triangle - * edge. */ - int get_neighbor(int tri, int edge) const; - - /* Return the TriEdge that is the neighbor of the specified triangle edge, - * or TriEdge(-1,-1) if there is no such neighbor. */ - TriEdge get_neighbor_edge(int tri, int edge) const; - - /* Return the neighbors array, creating it if necessary. */ - NeighborArray& get_neighbors(); - - // Return the number of points in this triangulation. - int get_npoints() const; - - // Return the number of triangles in this triangulation. - int get_ntri() const; - - /* Return the index of the point that is at the start of the specified - * triangle edge. */ - int get_triangle_point(int tri, int edge) const; - int get_triangle_point(const TriEdge& tri_edge) const; - - // Return the coordinates of the specified point index. - XY get_point_coords(int point) const; - - // Indicates if the specified triangle is masked or not. - bool is_masked(int tri) const; - - /* Set or clear the mask array. Clears various derived fields so they are - * recalculated when next needed. - * mask: bool array of shape (ntri) indicating which triangles are - * masked, or an empty array to clear mask. */ - void set_mask(const MaskArray& mask); - - // Debug function to write boundaries. - void write_boundaries() const; - -private: - // An edge of a triangulation, composed of start and end point indices. - struct Edge - { - Edge() : start(-1), end(-1) {} - Edge(int start_, int end_) : start(start_), end(end_) {} - bool operator<(const Edge& other) const { - return start != other.start ? start < other.start : end < other.end; - } - int start, end; - }; - - /* An edge of a boundary of a triangulation, composed of a boundary index - * and an edge index within that boundary. Used to index into the - * boundaries collection to obtain the corresponding TriEdge. */ - struct BoundaryEdge - { - BoundaryEdge() : boundary(-1), edge(-1) {} - BoundaryEdge(int boundary_, int edge_) - : boundary(boundary_), edge(edge_) {} - int boundary, edge; - }; - - /* Calculate the boundaries collection. Should normally be accessed via - * get_boundaries(), which will call this function if necessary. */ - void calculate_boundaries(); - - /* Calculate the edges array. Should normally be accessed via - * get_edges(), which will call this function if necessary. */ - void calculate_edges(); - - /* Calculate the neighbors array. Should normally be accessed via - * get_neighbors(), which will call this function if necessary. */ - void calculate_neighbors(); - - /* Correct each triangle so that the vertices are ordered in an - * anticlockwise manner. */ - void correct_triangles(); - - /* Determine which edge index (0,1 or 2) the specified point index is in - * the specified triangle, or -1 if the point is not in the triangle. */ - int get_edge_in_triangle(int tri, int point) const; - - bool has_edges() const; - - bool has_mask() const; - - bool has_neighbors() const; - - - // Variables shared with python, always set. - CoordinateArray _x, _y; // double array (npoints). - TriangleArray _triangles; // int array (ntri,3) of triangle point indices, - // ordered anticlockwise. - - // Variables shared with python, may be unset (size == 0). - MaskArray _mask; // bool array (ntri). - - // Derived variables shared with python, may be unset (size == 0). - // If unset, are recalculated when needed. - EdgeArray _edges; // int array (?,2) of start & end point indices. - NeighborArray _neighbors; // int array (ntri,3), neighbor triangle indices - // or -1 if no neighbor. - - // Variables internal to C++ only. - Boundaries _boundaries; - - // Map used to look up BoundaryEdges from TriEdges. Normally accessed via - // get_boundary_edge(). - typedef std::map<TriEdge, BoundaryEdge> TriEdgeToBoundaryMap; - TriEdgeToBoundaryMap _tri_edge_to_boundary_map; -}; - - - -// Contour generator for a triangulation. -class TriContourGenerator -{ -public: - typedef Triangulation::CoordinateArray CoordinateArray; - typedef Triangulation::TwoCoordinateArray TwoCoordinateArray; - typedef py::array_t<unsigned char> CodeArray; - - /* Constructor. - * triangulation: Triangulation to generate contours for. - * z: Double array of shape (npoints) of z-values at triangulation - * points. */ - TriContourGenerator(Triangulation& triangulation, - const CoordinateArray& z); - - /* Create and return a non-filled contour. - * level: Contour level. - * Returns new python list [segs0, segs1, ...] where - * segs0: double array of shape (?,2) of point coordinates of first - * contour line, etc. */ - py::tuple create_contour(const double& level); - - /* Create and return a filled contour. - * lower_level: Lower contour level. - * upper_level: Upper contour level. - * Returns new python tuple (segs, kinds) where - * segs: double array of shape (n_points,2) of all point coordinates, - * kinds: ubyte array of shape (n_points) of all point code types. */ - py::tuple create_filled_contour(const double& lower_level, - const double& upper_level); - -private: - typedef Triangulation::Boundary Boundary; - typedef Triangulation::Boundaries Boundaries; - - /* Clear visited flags. - * include_boundaries: Whether to clear boundary flags or not, which are - * only used for filled contours. */ - void clear_visited_flags(bool include_boundaries); - - /* Convert a non-filled Contour from C++ to Python. - * Returns new python tuple ([segs0, segs1, ...], [kinds0, kinds1...]) - * where - * segs0: double array of shape (n_points,2) of point coordinates of first - * contour line, etc. - * kinds0: ubyte array of shape (n_points) of kinds codes of first contour - * line, etc. */ - py::tuple contour_line_to_segs_and_kinds(const Contour& contour); - - /* Convert a filled Contour from C++ to Python. - * Returns new python tuple ([segs], [kinds]) where - * segs: double array of shape (n_points,2) of all point coordinates, - * kinds: ubyte array of shape (n_points) of all point code types. */ - py::tuple contour_to_segs_and_kinds(const Contour& contour); - - /* Return the point on the specified TriEdge that intersects the specified - * level. */ - XY edge_interp(int tri, int edge, const double& level); - - /* Find and follow non-filled contour lines that start and end on a - * boundary of the Triangulation. - * contour: Contour to add new lines to. - * level: Contour level. */ - void find_boundary_lines(Contour& contour, - const double& level); - - /* Find and follow filled contour lines at either of the specified contour - * levels that start and end of a boundary of the Triangulation. - * contour: Contour to add new lines to. - * lower_level: Lower contour level. - * upper_level: Upper contour level. */ - void find_boundary_lines_filled(Contour& contour, - const double& lower_level, - const double& upper_level); - - /* Find and follow lines at the specified contour level that are - * completely in the interior of the Triangulation and hence do not - * intersect any boundary. - * contour: Contour to add new lines to. - * level: Contour level. - * on_upper: Whether on upper or lower contour level. - * filled: Whether contours are filled or not. */ - void find_interior_lines(Contour& contour, - const double& level, - bool on_upper, - bool filled); - - /* Follow contour line around boundary of the Triangulation from the - * specified TriEdge to its end which can be on either the lower or upper - * levels. Only used for filled contours. - * contour_line: Contour line to append new points to. - * tri_edge: On entry, TriEdge to start from. On exit, TriEdge that is - * finished on. - * lower_level: Lower contour level. - * upper_level: Upper contour level. - * on_upper: Whether starts on upper level or not. - * Return true if finishes on upper level, false if lower. */ - bool follow_boundary(ContourLine& contour_line, - TriEdge& tri_edge, - const double& lower_level, - const double& upper_level, - bool on_upper); - - /* Follow contour line across interior of Triangulation. - * contour_line: Contour line to append new points to. - * tri_edge: On entry, TriEdge to start from. On exit, TriEdge that is - * finished on. - * end_on_boundary: Whether this line ends on a boundary, or loops back - * upon itself. - * level: Contour level to follow. - * on_upper: Whether following upper or lower contour level. */ - void follow_interior(ContourLine& contour_line, - TriEdge& tri_edge, - bool end_on_boundary, - const double& level, - bool on_upper); - - // Return the Triangulation boundaries. - const Boundaries& get_boundaries() const; - - /* Return the edge by which the a level leaves a particular triangle, - * which is 0, 1 or 2 if the contour passes through the triangle or -1 - * otherwise. - * tri: Triangle index. - * level: Contour level to follow. - * on_upper: Whether following upper or lower contour level. */ - int get_exit_edge(int tri, const double& level, bool on_upper) const; - - // Return the z-value at the specified point index. - const double& get_z(int point) const; - - /* Return the point at which the a level intersects the line connecting the - * two specified point indices. */ - XY interp(int point1, int point2, const double& level) const; - - - - // Variables shared with python, always set. - Triangulation _triangulation; - CoordinateArray _z; // double array (npoints). - - // Variables internal to C++ only. - typedef std::vector<bool> InteriorVisited; // Size 2*ntri - typedef std::vector<bool> BoundaryVisited; - typedef std::vector<BoundaryVisited> BoundariesVisited; - typedef std::vector<bool> BoundariesUsed; - - InteriorVisited _interior_visited; - BoundariesVisited _boundaries_visited; // Only used for filled contours. - BoundariesUsed _boundaries_used; // Only used for filled contours. -}; - - - -/* TriFinder class implemented using the trapezoid map algorithm from the book - * "Computational Geometry, Algorithms and Applications", second edition, by - * M. de Berg, M. van Kreveld, M. Overmars and O. Schwarzkopf. - * - * The domain of interest is composed of vertical-sided trapezoids that are - * bounded to the left and right by points of the triangulation, and below and - * above by edges of the triangulation. Each triangle is represented by 1 or - * more of these trapezoids. Edges are inserted one a time in a random order. - * - * As the trapezoid map is created, a search tree is also created which allows - * fast lookup O(log N) of the trapezoid containing the point of interest. - * There are 3 types of node in the search tree: all leaf nodes represent - * trapezoids and all branch nodes have 2 child nodes and are either x-nodes or - * y-nodes. X-nodes represent points in the triangulation, and their 2 children - * refer to those parts of the search tree to the left and right of the point. - * Y-nodes represent edges in the triangulation, and their 2 children refer to - * those parts of the search tree below and above the edge. - * - * Nodes can be repeated throughout the search tree, and each is reference - * counted through the multiple parent nodes it is a child of. - * - * The algorithm is only intended to work with valid triangulations, i.e. it - * must not contain duplicate points, triangles formed from colinear points, or - * overlapping triangles. It does have some tolerance to triangles formed from - * colinear points but only in the simplest of cases. No explicit testing of - * the validity of the triangulation is performed as this is a computationally - * more complex task than the trifinding itself. */ -class TrapezoidMapTriFinder -{ -public: - typedef Triangulation::CoordinateArray CoordinateArray; - typedef py::array_t<int, py::array::c_style | py::array::forcecast> TriIndexArray; - - /* Constructor. A separate call to initialize() is required to initialize - * the object before use. - * triangulation: Triangulation to find triangles in. */ - TrapezoidMapTriFinder(Triangulation& triangulation); - - ~TrapezoidMapTriFinder(); - - /* Return an array of triangle indices. Takes 1D arrays x and y of - * point coordinates, and returns an array of the same size containing the - * indices of the triangles at those points. */ - TriIndexArray find_many(const CoordinateArray& x, const CoordinateArray& y); - - /* Return a reference to a new python list containing the following - * statistics about the tree: - * 0: number of nodes (tree size) - * 1: number of unique nodes (number of unique Node objects in tree) - * 2: number of trapezoids (tree leaf nodes) - * 3: number of unique trapezoids - * 4: maximum parent count (max number of times a node is repeated in - * tree) - * 5: maximum depth of tree (one more than the maximum number of - * comparisons needed to search through the tree) - * 6: mean of all trapezoid depths (one more than the average number of - * comparisons needed to search through the tree) */ - py::list get_tree_stats(); - - /* Initialize this object before use. May be called multiple times, if, - * for example, the triangulation is changed by setting the mask. */ - void initialize(); - - // Print the search tree as text to stdout; useful for debug purposes. - void print_tree(); - -private: - /* A Point consists of x,y coordinates as well as the index of a triangle - * associated with the point, so that a search at this point's coordinates - * can return a valid triangle index. */ - struct Point : XY - { - Point() : XY(), tri(-1) {} - Point(const double& x, const double& y) : XY(x,y), tri(-1) {} - explicit Point(const XY& xy) : XY(xy), tri(-1) {} - - int tri; - }; - - /* An Edge connects two Points, left and right. It is always true that - * right->is_right_of(*left). Stores indices of triangles below and above - * the Edge which are used to map from trapezoid to triangle index. Also - * stores pointers to the 3rd points of the below and above triangles, - * which are only used to disambiguate triangles with colinear points. */ - struct Edge - { - Edge(const Point* left_, - const Point* right_, - int triangle_below_, - int triangle_above_, - const Point* point_below_, - const Point* point_above_); - - // Return -1 if point to left of edge, 0 if on edge, +1 if to right. - int get_point_orientation(const XY& xy) const; - - // Return slope of edge, even if vertical (divide by zero is OK here). - double get_slope() const; - - /* Return y-coordinate of point on edge with specified x-coordinate. - * x must be within the x-limits of this edge. */ - double get_y_at_x(const double& x) const; - - // Return true if the specified point is either of the edge end points. - bool has_point(const Point* point) const; - - bool operator==(const Edge& other) const; - - friend std::ostream& operator<<(std::ostream& os, const Edge& edge) - { - return os << *edge.left << "->" << *edge.right; - } - - void print_debug() const; - - - const Point* left; // Not owned. - const Point* right; // Not owned. - int triangle_below; // Index of triangle below (to right of) Edge. - int triangle_above; // Index of triangle above (to left of) Edge. - const Point* point_below; // Used only for resolving ambiguous cases; - const Point* point_above; // is 0 if corresponding triangle is -1 - }; - - class Node; // Forward declaration. - - // Helper structure used by TrapezoidMapTriFinder::get_tree_stats. - struct NodeStats - { - NodeStats() - : node_count(0), trapezoid_count(0), max_parent_count(0), - max_depth(0), sum_trapezoid_depth(0.0) - {} - - long node_count, trapezoid_count, max_parent_count, max_depth; - double sum_trapezoid_depth; - std::set<const Node*> unique_nodes, unique_trapezoid_nodes; - }; - - struct Trapezoid; // Forward declaration. - - /* Node of the trapezoid map search tree. There are 3 possible types: - * Type_XNode, Type_YNode and Type_TrapezoidNode. Data members are - * represented using a union: an XNode has a Point and 2 child nodes - * (left and right of the point), a YNode has an Edge and 2 child nodes - * (below and above the edge), and a TrapezoidNode has a Trapezoid. - * Each Node has multiple parents so it can appear in the search tree - * multiple times without having to create duplicate identical Nodes. - * The parent collection acts as a reference count to the number of times - * a Node occurs in the search tree. When the parent count is reduced to - * zero a Node can be safely deleted. */ - class Node - { - public: - Node(const Point* point, Node* left, Node* right);// Type_XNode. - Node(const Edge* edge, Node* below, Node* above); // Type_YNode. - Node(Trapezoid* trapezoid); // Type_TrapezoidNode. - - ~Node(); - - void add_parent(Node* parent); - - /* Recurse through the search tree and assert that everything is valid. - * Reduces to a no-op if NDEBUG is defined. */ - void assert_valid(bool tree_complete) const; - - // Recurse through the tree to return statistics about it. - void get_stats(int depth, NodeStats& stats) const; - - // Return the index of the triangle corresponding to this node. - int get_tri() const; - - bool has_child(const Node* child) const; - bool has_no_parents() const; - bool has_parent(const Node* parent) const; - - /* Recurse through the tree and print a textual representation to - * stdout. Argument depth used to indent for readability. */ - void print(int depth = 0) const; - - /* Remove a parent from this Node. Return true if no parents remain - * so that this Node can be deleted. */ - bool remove_parent(Node* parent); - - void replace_child(Node* old_child, Node* new_child); - - // Replace this node with the specified new_node in all parents. - void replace_with(Node* new_node); - - /* Recursive search through the tree to find the Node containing the - * specified XY point. */ - const Node* search(const XY& xy); - - /* Recursive search through the tree to find the Trapezoid containing - * the left endpoint of the specified Edge. Return 0 if fails, which - * can only happen if the triangulation is invalid. */ - Trapezoid* search(const Edge& edge); - - /* Copy constructor and assignment operator defined but not implemented - * to prevent objects being copied. */ - Node(const Node& other); - Node& operator=(const Node& other); - - private: - typedef enum { - Type_XNode, - Type_YNode, - Type_TrapezoidNode - } Type; - Type _type; - - union { - struct { - const Point* point; // Not owned. - Node* left; // Owned. - Node* right; // Owned. - } xnode; - struct { - const Edge* edge; // Not owned. - Node* below; // Owned. - Node* above; // Owned. - } ynode; - Trapezoid* trapezoid; // Owned. - } _union; - - typedef std::list<Node*> Parents; - Parents _parents; // Not owned. - }; - - /* A Trapezoid is bounded by Points to left and right, and Edges below and - * above. Has up to 4 neighboring Trapezoids to lower/upper left/right. - * Lower left neighbor is Trapezoid to left that shares the below Edge, or - * is 0 if there is no such Trapezoid (and similar for other neighbors). - * To obtain the index of the triangle corresponding to a particular - * Trapezoid, use the Edge member variables below.triangle_above or - * above.triangle_below. */ - struct Trapezoid - { - Trapezoid(const Point* left_, - const Point* right_, - const Edge& below_, - const Edge& above_); - - /* Assert that this Trapezoid is valid. Reduces to a no-op if NDEBUG - * is defined. */ - void assert_valid(bool tree_complete) const; - - /* Return one of the 4 corner points of this Trapezoid. Only used for - * debugging purposes. */ - XY get_lower_left_point() const; - XY get_lower_right_point() const; - XY get_upper_left_point() const; - XY get_upper_right_point() const; - - void print_debug() const; - - /* Set one of the 4 neighbor trapezoids and the corresponding reverse - * Trapezoid of the new neighbor (if it is not 0), so that they are - * consistent. */ - void set_lower_left(Trapezoid* lower_left_); - void set_lower_right(Trapezoid* lower_right_); - void set_upper_left(Trapezoid* upper_left_); - void set_upper_right(Trapezoid* upper_right_); - - /* Copy constructor and assignment operator defined but not implemented - * to prevent objects being copied. */ - Trapezoid(const Trapezoid& other); - Trapezoid& operator=(const Trapezoid& other); - - - const Point* left; // Not owned. - const Point* right; // Not owned. - const Edge& below; - const Edge& above; - - // 4 neighboring trapezoids, can be 0, not owned. - Trapezoid* lower_left; // Trapezoid to left that shares below - Trapezoid* lower_right; // Trapezoid to right that shares below - Trapezoid* upper_left; // Trapezoid to left that shares above - Trapezoid* upper_right; // Trapezoid to right that shares above - - Node* trapezoid_node; // Node that owns this Trapezoid. - }; - - - // Add the specified Edge to the search tree, returning true if successful. - bool add_edge_to_tree(const Edge& edge); - - // Clear all memory allocated by this object. - void clear(); - - // Return the triangle index at the specified point, or -1 if no triangle. - int find_one(const XY& xy); - - /* Determine the trapezoids that the specified Edge intersects, returning - * true if successful. */ - bool find_trapezoids_intersecting_edge(const Edge& edge, - std::vector<Trapezoid*>& trapezoids); - - - - // Variables shared with python, always set. - Triangulation& _triangulation; - - // Variables internal to C++ only. - Point* _points; // Array of all points in triangulation plus corners of - // enclosing rectangle. Owned. - - typedef std::vector<Edge> Edges; - Edges _edges; // All Edges in triangulation plus bottom and top Edges of - // enclosing rectangle. - - Node* _tree; // Root node of the trapezoid map search tree. Owned. -}; - -#endif diff --git a/contrib/python/matplotlib/py3/src/tri/_tri_wrapper.cpp b/contrib/python/matplotlib/py3/src/tri/_tri_wrapper.cpp deleted file mode 100644 index 1b0c3d7555..0000000000 --- a/contrib/python/matplotlib/py3/src/tri/_tri_wrapper.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "_tri.h" - -PYBIND11_MODULE(_tri, m) { - py::class_<Triangulation>(m, "Triangulation") - .def(py::init<const Triangulation::CoordinateArray&, - const Triangulation::CoordinateArray&, - const Triangulation::TriangleArray&, - const Triangulation::MaskArray&, - const Triangulation::EdgeArray&, - const Triangulation::NeighborArray&, - bool>(), - py::arg("x"), - py::arg("y"), - py::arg("triangles"), - py::arg("mask"), - py::arg("edges"), - py::arg("neighbors"), - py::arg("correct_triangle_orientations"), - "Create a new C++ Triangulation object.\n" - "This should not be called directly, use the python class\n" - "matplotlib.tri.Triangulation instead.\n") - .def("calculate_plane_coefficients", &Triangulation::calculate_plane_coefficients, - "Calculate plane equation coefficients for all unmasked triangles.") - .def("get_edges", &Triangulation::get_edges, - "Return edges array.") - .def("get_neighbors", &Triangulation::get_neighbors, - "Return neighbors array.") - .def("set_mask", &Triangulation::set_mask, - "Set or clear the mask array."); - - py::class_<TriContourGenerator>(m, "TriContourGenerator") - .def(py::init<Triangulation&, - const TriContourGenerator::CoordinateArray&>(), - py::arg("triangulation"), - py::arg("z"), - "Create a new C++ TriContourGenerator object.\n" - "This should not be called directly, use the functions\n" - "matplotlib.axes.tricontour and tricontourf instead.\n") - .def("create_contour", &TriContourGenerator::create_contour, - "Create and return a non-filled contour.") - .def("create_filled_contour", &TriContourGenerator::create_filled_contour, - "Create and return a filled contour."); - - py::class_<TrapezoidMapTriFinder>(m, "TrapezoidMapTriFinder") - .def(py::init<Triangulation&>(), - py::arg("triangulation"), - "Create a new C++ TrapezoidMapTriFinder object.\n" - "This should not be called directly, use the python class\n" - "matplotlib.tri.TrapezoidMapTriFinder instead.\n") - .def("find_many", &TrapezoidMapTriFinder::find_many, - "Find indices of triangles containing the point coordinates (x, y).") - .def("get_tree_stats", &TrapezoidMapTriFinder::get_tree_stats, - "Return statistics about the tree used by the trapezoid map.") - .def("initialize", &TrapezoidMapTriFinder::initialize, - "Initialize this object, creating the trapezoid map from the triangulation.") - .def("print_tree", &TrapezoidMapTriFinder::print_tree, - "Print the search tree as text to stdout; useful for debug purposes."); -} |