aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/contourpy/src
diff options
context:
space:
mode:
authormaxim-yurchuk <maxim-yurchuk@yandex-team.com>2025-02-11 13:26:52 +0300
committermaxim-yurchuk <maxim-yurchuk@yandex-team.com>2025-02-11 13:57:59 +0300
commitf895bba65827952ed934b2b46f9a45e30a191fd2 (patch)
tree03260c906d9ec41cdc03e2a496b15d407459cec0 /contrib/python/contourpy/src
parent5f7060466f7b9707818c2091e1a25c14f33c3474 (diff)
downloadydb-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/contourpy/src')
-rw-r--r--contrib/python/contourpy/src/base.h217
-rw-r--r--contrib/python/contourpy/src/base_impl.h2524
-rw-r--r--contrib/python/contourpy/src/chunk_local.cpp59
-rw-r--r--contrib/python/contourpy/src/chunk_local.h40
-rw-r--r--contrib/python/contourpy/src/common.h33
-rw-r--r--contrib/python/contourpy/src/contour_generator.cpp79
-rw-r--r--contrib/python/contourpy/src/contour_generator.h36
-rw-r--r--contrib/python/contourpy/src/converter.cpp155
-rw-r--r--contrib/python/contourpy/src/converter.h63
-rw-r--r--contrib/python/contourpy/src/fill_type.cpp31
-rw-r--r--contrib/python/contourpy/src/fill_type.h24
-rw-r--r--contrib/python/contourpy/src/line_type.cpp28
-rw-r--r--contrib/python/contourpy/src/line_type.h23
-rw-r--r--contrib/python/contourpy/src/mpl2005.cpp74
-rw-r--r--contrib/python/contourpy/src/mpl2005.h32
-rw-r--r--contrib/python/contourpy/src/mpl2005_original.cpp1526
-rw-r--r--contrib/python/contourpy/src/mpl2005_original.h56
-rw-r--r--contrib/python/contourpy/src/mpl2014.cpp1710
-rw-r--r--contrib/python/contourpy/src/mpl2014.h513
-rw-r--r--contrib/python/contourpy/src/mpl_kind_code.h17
-rw-r--r--contrib/python/contourpy/src/outer_or_hole.cpp19
-rw-r--r--contrib/python/contourpy/src/outer_or_hole.h18
-rw-r--r--contrib/python/contourpy/src/output_array.h70
-rw-r--r--contrib/python/contourpy/src/serial.cpp147
-rw-r--r--contrib/python/contourpy/src/serial.h40
-rw-r--r--contrib/python/contourpy/src/threaded.cpp316
-rw-r--r--contrib/python/contourpy/src/threaded.h69
-rw-r--r--contrib/python/contourpy/src/util.cpp30
-rw-r--r--contrib/python/contourpy/src/util.h31
-rw-r--r--contrib/python/contourpy/src/wrap.cpp419
-rw-r--r--contrib/python/contourpy/src/z_interp.cpp19
-rw-r--r--contrib/python/contourpy/src/z_interp.h23
32 files changed, 0 insertions, 8441 deletions
diff --git a/contrib/python/contourpy/src/base.h b/contrib/python/contourpy/src/base.h
deleted file mode 100644
index 6b4d99d17ea..00000000000
--- a/contrib/python/contourpy/src/base.h
+++ /dev/null
@@ -1,217 +0,0 @@
-// BaseContourGenerator class provides functionality common to multiple contouring algorithms.
-// Uses the Curiously Recurring Template Pattern idiom whereby classes derived from this are
-// declared as
-// class Derived : public BaseContourGenerator<Derived>
-//
-// It provides static polymorphism at compile-time rather than the more common dynamic polymorphism
-// at run-time using virtual functions. Hence it avoids the runtime overhead of calling virtual
-// functions.
-
-#ifndef CONTOURPY_BASE_H
-#define CONTOURPY_BASE_H
-
-#include "chunk_local.h"
-#include "contour_generator.h"
-#include "fill_type.h"
-#include "line_type.h"
-#include "outer_or_hole.h"
-#include "z_interp.h"
-#include <vector>
-
-namespace contourpy {
-
-template <typename Derived>
-class BaseContourGenerator : public ContourGenerator
-{
-public:
- virtual ~BaseContourGenerator();
-
- static FillType default_fill_type();
- static LineType default_line_type();
-
- py::tuple filled(double lower_level, double upper_level) override;
-
- py::tuple get_chunk_count() const; // Return (y_chunk_count, x_chunk_count)
- py::tuple get_chunk_size() const; // Return (y_chunk_size, x_chunk_size)
-
- bool get_corner_mask() const;
-
- FillType get_fill_type() const;
- LineType get_line_type() const;
-
- bool get_quad_as_tri() const;
-
- ZInterp get_z_interp() const;
-
- py::sequence lines(double level) override;
-
- py::list multi_filled(const LevelArray levels) override;
- py::list multi_lines(const LevelArray levels) override;
-
- static bool supports_fill_type(FillType fill_type);
- static bool supports_line_type(LineType line_type);
-
- void write_cache() const; // For debug purposes only.
-
-protected:
- BaseContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, LineType line_type, FillType fill_type,
- bool quad_as_tri, ZInterp z_interp, index_t x_chunk_size, index_t y_chunk_size);
-
- typedef uint32_t CacheItem;
- typedef CacheItem ZLevel;
-
- // C++11 scoped enum for direction of movement from one quad to the next.
- enum class Direction
- {
- Left = 1,
- Straight = 0,
- Right = -1
- };
-
- struct Location
- {
- Location(index_t quad_, index_t forward_, index_t left_, bool is_upper_, bool on_boundary_)
- : quad(quad_), forward(forward_), left(left_), is_upper(is_upper_),
- on_boundary(on_boundary_)
- {}
-
- friend std::ostream &operator<<(std::ostream &os, const Location& location)
- {
- os << "quad=" << location.quad << " forward=" << location.forward << " left="
- << location.left << " is_upper=" << location.is_upper << " on_boundary="
- << location.on_boundary;
- return os;
- }
-
- index_t quad, forward, left;
- bool is_upper, on_boundary;
- };
-
- // Calculate, set and return z-level at middle of quad.
- ZLevel calc_and_set_middle_z_level(index_t quad);
-
- // Calculate and return z at middle of quad.
- double calc_middle_z(index_t quad) const;
-
- void closed_line(const Location& start_location, OuterOrHole outer_or_hole, ChunkLocal& local);
-
- void closed_line_wrapper(
- const Location& start_location, OuterOrHole outer_or_hole, ChunkLocal& local);
-
- // If point/line/hole counts not consistent, throw runtime error.
- void check_consistent_counts(const ChunkLocal& local) const;
-
- index_t find_look_S(index_t look_N_quad) const;
-
- // Return true if finished (i.e. back to start quad, direction and upper).
- bool follow_boundary(
- Location& location, const Location& start_location, ChunkLocal& local,
- count_t& point_count);
-
- // Return true if finished (i.e. back to start quad, direction and upper).
- bool follow_interior(
- Location& location, const Location& start_location, ChunkLocal& local,
- count_t& point_count);
-
- index_t get_boundary_start_point(const Location& location) const;
-
- // These are quad chunk limits, not point chunk limits.
- // chunk is index in range 0.._n_chunks-1.
- void get_chunk_limits(index_t chunk, ChunkLocal& local) const;
-
- index_t get_interior_start_left_point(
- const Location& location, bool& start_corner_diagonal) const;
-
- double get_interp_fraction(double z0, double z1, double level) const;
-
- double get_middle_x(index_t quad) const;
- double get_middle_y(index_t quad) const;
-
- index_t get_n_chunks() const;
-
- void get_point_xy(index_t point, double*& points) const;
-
- double get_point_x(index_t point) const;
- double get_point_y(index_t point) const;
- double get_point_z(index_t point) const;
-
- bool has_direct_line_offsets() const;
- bool has_direct_outer_offsets() const;
- bool has_direct_points() const;
-
- void init_cache_grid(const MaskArray& mask);
-
- // Either for a single chunk, or the whole domain (all chunks) if local == nullptr.
- void init_cache_levels_and_starts(const ChunkLocal* local = nullptr);
-
- // Increments local.points twice.
- void interp(index_t point0, index_t point1, bool is_upper, double*& points) const;
-
- // Increments local.points twice.
- void interp(
- index_t point0, double x1, double y1, double z1, bool is_upper, double*& points) const;
-
- bool is_point_in_chunk(index_t point, const ChunkLocal& local) const;
-
- bool is_quad_in_bounds(
- index_t quad, index_t istart, index_t iend, index_t jstart, index_t jend) const;
-
- bool is_quad_in_chunk(index_t quad, const ChunkLocal& local) const;
-
- void line(const Location& start_location, ChunkLocal& local);
-
- void march_chunk(ChunkLocal& local, std::vector<py::list>& return_lists);
-
- py::sequence march_wrapper();
-
- void move_to_next_boundary_edge(index_t& quad, index_t& forward, index_t& left) const;
-
- // Common initialisation for filled/multi_filled and lines/multi_lines calls.
- void pre_filled();
- void pre_lines();
-
- void set_look_flags(index_t hole_start_quad);
-
- void write_cache_quad(index_t quad) const;
-
- ZLevel z_to_zlevel(double z_value) const;
-
-
-private:
- const CoordinateArray _x, _y, _z;
- const double* _xptr; // For quick access to _x.data().
- const double* _yptr;
- const double* _zptr;
- const index_t _nx, _ny; // Number of points in each direction.
- const index_t _n; // Total number of points (and quads).
- const index_t _x_chunk_size, _y_chunk_size;
- const index_t _nx_chunks, _ny_chunks; // Number of chunks in each direction.
- const index_t _n_chunks; // Total number of chunks.
- const bool _corner_mask;
- const LineType _line_type;
- const FillType _fill_type;
- const bool _quad_as_tri;
- const ZInterp _z_interp;
-
- CacheItem* _cache;
-
- // Current contouring operation.
- bool _filled;
- double _lower_level, _upper_level;
-
- // Current contouring operation, based on return type and filled or lines.
- bool _identify_holes;
- bool _output_chunked; // Implies empty chunks will have py::none().
- bool _direct_points; // Whether points array is written direct to Python.
- bool _direct_line_offsets; // Whether line offsets array is written direct to Python.
- bool _direct_outer_offsets; // Whether outer offsets array is written direct to Python.
- bool _outer_offsets_into_points; // Otherwise into line offsets. Only used if _identify_holes.
- bool _nan_separated; // Whether adjacent lines' points are separated by nans.
- unsigned int _return_list_count;
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_BASE_H
diff --git a/contrib/python/contourpy/src/base_impl.h b/contrib/python/contourpy/src/base_impl.h
deleted file mode 100644
index 80c9e51854a..00000000000
--- a/contrib/python/contourpy/src/base_impl.h
+++ /dev/null
@@ -1,2524 +0,0 @@
-#ifndef CONTOURPY_BASE_IMPL_H
-#define CONTOURPY_BASE_IMPL_H
-
-#include "base.h"
-#include "converter.h"
-#include "util.h"
-#include <iostream>
-
-namespace contourpy {
-
-// Point indices from current quad index.
-#define POINT_NE (quad)
-#define POINT_NW (quad-1)
-#define POINT_SE (quad-_nx)
-#define POINT_SW (quad-_nx-1)
-
-
-// CacheItem masks, only accessed directly to set. To read, use accessors detailed below.
-// 1 and 2 refer to level indices (lower and upper).
-#define MASK_Z_LEVEL_1 (0x1 << 0) // z > lower_level.
-#define MASK_Z_LEVEL_2 (0x1 << 1) // z > upper_level.
-#define MASK_Z_LEVEL (MASK_Z_LEVEL_1 | MASK_Z_LEVEL_2)
-#define MASK_MIDDLE_Z_LEVEL_1 (0x1 << 2) // middle z > lower_level
-#define MASK_MIDDLE_Z_LEVEL_2 (0x1 << 3) // middle z > upper_level
-#define MASK_MIDDLE (MASK_MIDDLE_Z_LEVEL_1 | MASK_MIDDLE_Z_LEVEL_2)
-#define MASK_BOUNDARY_E (0x1 << 4) // E edge of quad is a boundary.
-#define MASK_BOUNDARY_N (0x1 << 5) // N edge of quad is a boundary.
-// EXISTS_QUAD bit is always used, but the 4 EXISTS_CORNER are only used if _corner_mask is true.
-// Only one of EXISTS_QUAD or EXISTS_??_CORNER is ever set per quad.
-#define MASK_EXISTS_QUAD (0x1 << 6) // All of quad exists (is not masked).
-#define MASK_EXISTS_NE_CORNER (0x1 << 7) // NE corner exists, SW corner is masked.
-#define MASK_EXISTS_NW_CORNER (0x1 << 8)
-#define MASK_EXISTS_SE_CORNER (0x1 << 9)
-#define MASK_EXISTS_SW_CORNER (0x1 << 10)
-#define MASK_EXISTS_ANY_CORNER (MASK_EXISTS_NE_CORNER | MASK_EXISTS_NW_CORNER | MASK_EXISTS_SE_CORNER | MASK_EXISTS_SW_CORNER)
-#define MASK_EXISTS_ANY (MASK_EXISTS_QUAD | MASK_EXISTS_ANY_CORNER)
-#define MASK_START_E (0x1 << 11) // E to N, filled and lines.
-#define MASK_START_N (0x1 << 12) // N to E, filled and lines.
-#define MASK_START_BOUNDARY_E (0x1 << 13) // Lines only.
-#define MASK_START_BOUNDARY_N (0x1 << 14) // Lines only.
-#define MASK_START_BOUNDARY_S (0x1 << 15) // Filled and lines.
-#define MASK_START_BOUNDARY_W (0x1 << 16) // Filled and lines.
-#define MASK_START_CORNER (0x1 << 18) // Filled and lines.
-#define MASK_START_HOLE_N (0x1 << 17) // N boundary of EXISTS, E to W, filled only.
-#define MASK_ANY_START (MASK_START_N | MASK_START_E | MASK_START_BOUNDARY_N | MASK_START_BOUNDARY_E | MASK_START_BOUNDARY_S | MASK_START_BOUNDARY_W | MASK_START_HOLE_N | MASK_START_CORNER)
-#define MASK_LOOK_N (0x1 << 19)
-#define MASK_LOOK_S (0x1 << 20)
-#define MASK_NO_STARTS_IN_ROW (0x1 << 21)
-#define MASK_NO_MORE_STARTS (0x1 << 22)
-
-// Accessors for various CacheItem masks.
-#define Z_LEVEL(quad) (_cache[quad] & MASK_Z_LEVEL)
-#define Z_NE Z_LEVEL(POINT_NE)
-#define Z_NW Z_LEVEL(POINT_NW)
-#define Z_SE Z_LEVEL(POINT_SE)
-#define Z_SW Z_LEVEL(POINT_SW)
-#define MIDDLE_Z_LEVEL(quad) ((_cache[quad] & MASK_MIDDLE) >> 2)
-#define BOUNDARY_E(quad) (_cache[quad] & MASK_BOUNDARY_E)
-#define BOUNDARY_N(quad) (_cache[quad] & MASK_BOUNDARY_N)
-#define BOUNDARY_S(quad) (_cache[quad-_nx] & MASK_BOUNDARY_N)
-#define BOUNDARY_W(quad) (_cache[quad-1] & MASK_BOUNDARY_E)
-#define EXISTS_QUAD(quad) (_cache[quad] & MASK_EXISTS_QUAD)
-#define EXISTS_NE_CORNER(quad) (_cache[quad] & MASK_EXISTS_NE_CORNER)
-#define EXISTS_NW_CORNER(quad) (_cache[quad] & MASK_EXISTS_NW_CORNER)
-#define EXISTS_SE_CORNER(quad) (_cache[quad] & MASK_EXISTS_SE_CORNER)
-#define EXISTS_SW_CORNER(quad) (_cache[quad] & MASK_EXISTS_SW_CORNER)
-#define EXISTS_ANY(quad) (_cache[quad] & MASK_EXISTS_ANY)
-#define EXISTS_ANY_CORNER(quad) (_cache[quad] & MASK_EXISTS_ANY_CORNER)
-#define EXISTS_E_EDGE(quad) (_cache[quad] & (MASK_EXISTS_QUAD | MASK_EXISTS_NE_CORNER | MASK_EXISTS_SE_CORNER))
-#define EXISTS_N_EDGE(quad) (_cache[quad] & (MASK_EXISTS_QUAD | MASK_EXISTS_NW_CORNER | MASK_EXISTS_NE_CORNER))
-#define EXISTS_S_EDGE(quad) (_cache[quad] & (MASK_EXISTS_QUAD | MASK_EXISTS_SW_CORNER | MASK_EXISTS_SE_CORNER))
-#define EXISTS_W_EDGE(quad) (_cache[quad] & (MASK_EXISTS_QUAD | MASK_EXISTS_NW_CORNER | MASK_EXISTS_SW_CORNER))
-// Note that EXISTS_NE_CORNER(quad) is equivalent to BOUNDARY_SW(quad), etc.
-#define START_E(quad) (_cache[quad] & MASK_START_E)
-#define START_N(quad) (_cache[quad] & MASK_START_N)
-#define START_BOUNDARY_E(quad) (_cache[quad] & MASK_START_BOUNDARY_E)
-#define START_BOUNDARY_N(quad) (_cache[quad] & MASK_START_BOUNDARY_N)
-#define START_BOUNDARY_S(quad) (_cache[quad] & MASK_START_BOUNDARY_S)
-#define START_BOUNDARY_W(quad) (_cache[quad] & MASK_START_BOUNDARY_W)
-#define START_CORNER(quad) (_cache[quad] & MASK_START_CORNER)
-#define START_HOLE_N(quad) (_cache[quad] & MASK_START_HOLE_N)
-#define ANY_START(quad) ((_cache[quad] & MASK_ANY_START) != 0)
-#define LOOK_N(quad) (_cache[quad] & MASK_LOOK_N)
-#define LOOK_S(quad) (_cache[quad] & MASK_LOOK_S)
-#define NO_STARTS_IN_ROW(quad) (_cache[quad] & MASK_NO_STARTS_IN_ROW)
-#define NO_MORE_STARTS(quad) (_cache[quad] & MASK_NO_MORE_STARTS)
-// Contour line/fill goes to the left or right of quad middle (quad_as_tri only).
-#define LEFT_OF_MIDDLE(quad, is_upper) (MIDDLE_Z_LEVEL(quad) == (is_upper ? 2 : 0))
-
-
-template <typename Derived>
-BaseContourGenerator<Derived>::BaseContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, LineType line_type, FillType fill_type,
- bool quad_as_tri, ZInterp z_interp, index_t x_chunk_size, index_t y_chunk_size)
- : _x(x),
- _y(y),
- _z(z),
- _xptr(_x.data()),
- _yptr(_y.data()),
- _zptr(_z.data()),
- _nx(_z.ndim() > 1 ? _z.shape(1) : 0),
- _ny(_z.ndim() > 0 ? _z.shape(0) : 0),
- _n(_nx*_ny),
- _x_chunk_size(x_chunk_size > 0 ? std::min(x_chunk_size, _nx-1) : _nx-1),
- _y_chunk_size(y_chunk_size > 0 ? std::min(y_chunk_size, _ny-1) : _ny-1),
- _nx_chunks(static_cast<index_t>(std::ceil((_nx-1.0) / _x_chunk_size))),
- _ny_chunks(static_cast<index_t>(std::ceil((_ny-1.0) / _y_chunk_size))),
- _n_chunks(_nx_chunks*_ny_chunks),
- _corner_mask(corner_mask),
- _line_type(line_type),
- _fill_type(fill_type),
- _quad_as_tri(quad_as_tri),
- _z_interp(z_interp),
- _cache(new CacheItem[_n]),
- _filled(false),
- _lower_level(0.0),
- _upper_level(0.0),
- _identify_holes(false),
- _output_chunked(false),
- _direct_points(false),
- _direct_line_offsets(false),
- _direct_outer_offsets(false),
- _outer_offsets_into_points(false),
- _nan_separated(false),
- _return_list_count(0)
-{
- if (_x.ndim() != 2 || _y.ndim() != 2 || _z.ndim() != 2)
- throw std::invalid_argument("x, y and z must all be 2D arrays");
-
- if (_x.shape(1) != _nx || _x.shape(0) != _ny ||
- _y.shape(1) != _nx || _y.shape(0) != _ny)
- throw std::invalid_argument("x, y and z arrays must have the same shape");
-
- if (_nx < 2 || _ny < 2)
- throw std::invalid_argument("x, y and z must all be at least 2x2 arrays");
-
- if (mask.ndim() != 0) { // ndim == 0 if mask is not set, which is valid.
- if (mask.ndim() != 2)
- throw std::invalid_argument("mask array must be a 2D array");
-
- if (mask.shape(1) != _nx || mask.shape(0) != _ny)
- throw std::invalid_argument(
- "If mask is set it must be a 2D array with the same shape as z");
- }
-
- if (!supports_line_type(line_type))
- throw std::invalid_argument("Unsupported LineType");
-
- if (!supports_fill_type(fill_type))
- throw std::invalid_argument("Unsupported FillType");
-
- if (x_chunk_size < 0 || y_chunk_size < 0)
- throw std::invalid_argument("x_chunk_size and y_chunk_size cannot be negative");
-
- if (_z_interp == ZInterp::Log) {
- const bool* mask_ptr = (mask.ndim() == 0 ? nullptr : mask.data());
- for (index_t point = 0; point < _n; ++point) {
- if ( (mask_ptr == nullptr || !mask_ptr[point]) && _zptr[point] <= 0.0)
- throw std::invalid_argument("z values must be positive if using ZInterp.Log");
- }
- }
-
- init_cache_grid(mask);
-}
-
-template <typename Derived>
-BaseContourGenerator<Derived>::~BaseContourGenerator()
-{
- delete [] _cache;
-}
-
-template <typename Derived>
-typename BaseContourGenerator<Derived>::ZLevel
- BaseContourGenerator<Derived>::calc_and_set_middle_z_level(index_t quad)
-{
- ZLevel zlevel = z_to_zlevel(calc_middle_z(quad));
- _cache[quad] |= (zlevel << 2);
- return zlevel;
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::calc_middle_z(index_t quad) const
-{
- assert(quad >= 0 && quad < _n);
-
- switch (_z_interp) {
- case ZInterp::Log:
- return exp(0.25*(log(get_point_z(POINT_SW)) +
- log(get_point_z(POINT_SE)) +
- log(get_point_z(POINT_NW)) +
- log(get_point_z(POINT_NE))));
- default: // ZInterp::Linear
- return 0.25*(get_point_z(POINT_SW) +
- get_point_z(POINT_SE) +
- get_point_z(POINT_NW) +
- get_point_z(POINT_NE));
- }
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::check_consistent_counts(const ChunkLocal& local) const
-{
- if (local.total_point_count > 0) {
- if (local.points.size != 2*local.total_point_count ||
- local.points.current != local.points.start + 2*local.total_point_count) {
- throw std::runtime_error(
- "Inconsistent total_point_count for chunk " + std::to_string(local.chunk) +
- ". This may indicate a bug in ContourPy.");
- }
- }
- else {
- if (local.points.size != 0 ||
- local.points.start != nullptr || local.points.current != nullptr) {
- throw std::runtime_error(
- "Inconsistent zero total_point_count for chunk " + std::to_string(local.chunk) +
- ". This may indicate a bug in ContourPy.");
- }
- }
-
- if (local.line_count > 0) {
- if (local.line_offsets.size != local.line_count + 1 ||
- local.line_offsets.current == nullptr ||
- local.line_offsets.current != local.line_offsets.start + local.line_count + 1) {
- throw std::runtime_error(
- "Inconsistent line_count for chunk " + std::to_string(local.chunk) +
- ". This may indicate a bug in ContourPy.");
- }
- }
- else {
- if (local.line_offsets.size != 0 ||
- local.line_offsets.start != nullptr || local.line_offsets.current != nullptr) {
- throw std::runtime_error(
- "Inconsistent zero line_count for chunk " + std::to_string(local.chunk) +
- ". This may indicate a bug in ContourPy.");
- }
- }
-
- if (_identify_holes && local.line_count > 0) {
- if (local.outer_offsets.size != local.line_count - local.hole_count + 1 ||
- local.outer_offsets.current == nullptr ||
- local.outer_offsets.current != local.outer_offsets.start + local.line_count -
- local.hole_count + 1) {
- throw std::runtime_error(
- "Inconsistent hole_count for chunk " + std::to_string(local.chunk) +
- ". This may indicate a bug in ContourPy.");
- }
- }
- else {
- if (local.outer_offsets.size != 0 ||
- local.outer_offsets.start != nullptr || local.outer_offsets.current != nullptr) {
- throw std::runtime_error(
- "Inconsistent zero hole_count for chunk " + std::to_string(local.chunk) +
- ". This may indicate a bug in ContourPy.");
- }
- }
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::closed_line(
- const Location& start_location, OuterOrHole outer_or_hole, ChunkLocal& local)
-{
- assert(is_quad_in_chunk(start_location.quad, local));
-
- Location location = start_location;
- bool finished = false;
- count_t point_count = 0;
-
- if (outer_or_hole == Hole && local.pass == 0 && _identify_holes)
- set_look_flags(start_location.quad);
-
- while (!finished) {
- if (location.on_boundary)
- finished = follow_boundary(location, start_location, local, point_count);
- else
- finished = follow_interior(location, start_location, local, point_count);
- location.on_boundary = !location.on_boundary;
- }
-
- if (local.pass > 0) {
- assert(local.line_offsets.current = local.line_offsets.start + local.line_count);
- *local.line_offsets.current++ = local.total_point_count;
- if (outer_or_hole == Outer && _identify_holes) {
- assert(local.outer_offsets.current ==
- local.outer_offsets.start + local.line_count - local.hole_count);
- if (_outer_offsets_into_points)
- *local.outer_offsets.current++ = local.total_point_count;
- else
- *local.outer_offsets.current++ = local.line_count;
- }
- }
-
- local.total_point_count += point_count;
- local.line_count++;
- if (outer_or_hole == Hole)
- local.hole_count++;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::closed_line_wrapper(
- const Location& start_location, OuterOrHole outer_or_hole, ChunkLocal& local)
-{
- assert(is_quad_in_chunk(start_location.quad, local));
-
- if (local.pass == 0 || !_identify_holes) {
- closed_line(start_location, outer_or_hole, local);
- }
- else {
- assert(outer_or_hole == Outer);
- local.look_up_quads.clear();
-
- closed_line(start_location, outer_or_hole, local);
-
- for (py::size_t i = 0; i < local.look_up_quads.size(); ++i) {
- // Note that the collection can increase in size during this loop.
- index_t quad = local.look_up_quads[i];
-
- // Walk N to corresponding look S flag is reached.
- quad = find_look_S(quad);
-
- // Only 3 possible types of hole start: START_E, START_HOLE_N or START_CORNER for SW
- // corner.
- if (START_E(quad)) {
- closed_line(Location(quad, -1, -_nx, Z_NE > 0, false), Hole, local);
- }
- else if (START_HOLE_N(quad)) {
- closed_line(Location(quad, -1, -_nx, false, true), Hole, local);
- }
- else {
- assert(START_CORNER(quad) && EXISTS_SW_CORNER(quad));
- closed_line(Location(quad, _nx-1, -_nx-1, false, true), Hole, local);
- }
- }
- }
-}
-
-template <typename Derived>
-FillType BaseContourGenerator<Derived>::default_fill_type()
-{
- FillType fill_type = FillType::OuterOffset;
- assert(supports_fill_type(fill_type));
- return fill_type;
-}
-
-template <typename Derived>
-LineType BaseContourGenerator<Derived>::default_line_type()
-{
- LineType line_type = LineType::Separate;
- assert(supports_line_type(line_type));
- return line_type;
-}
-
-template <typename Derived>
-py::tuple BaseContourGenerator<Derived>::filled(double lower_level, double upper_level)
-{
- check_levels(lower_level, upper_level);
- pre_filled();
-
- _lower_level = lower_level;
- _upper_level = upper_level;
- return march_wrapper();
-}
-
-template <typename Derived>
-index_t BaseContourGenerator<Derived>::find_look_S(index_t look_N_quad) const
-{
- assert(_identify_holes);
-
- // Might need to be careful when looking in the same quad as the LOOK_UP.
- index_t quad = look_N_quad;
-
- // look_S quad must have 1 of only 3 possible types of hole start (START_E, START_HOLE_N,
- // START_CORNER for SW corner) but it may have other starts as well.
-
- // Start quad may be both a look_N and look_S quad. Only want to stop search here if look_S
- // hole start is N of look_N.
-
- if (!LOOK_S(quad)) {
- do
- {
- quad += _nx;
- assert(quad >= 0 && quad < _n);
- assert(EXISTS_ANY(quad));
- } while (!LOOK_S(quad));
- }
-
- return quad;
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::follow_boundary(
- Location& location, const Location& start_location, ChunkLocal& local, count_t& point_count)
-{
- // forward values for boundaries:
- // -1 = N boundary, E to W.
- // 1 = S boundary, W to E.
- // -_nx = W boundary, N to S.
- // _nx = E boundary, S to N.
- // -_nx+1 = NE corner, NW to SE.
- // _nx+1 = NW corner, SW to NE.
- // -_nx-1 = SE corner, NE to SW.
- // _nx-1 = SW corner, SE to NW.
-
- assert(is_quad_in_chunk(start_location.quad, local));
- assert(is_quad_in_chunk(location.quad, local));
-
- // Local variables for faster access.
- auto quad = location.quad;
- auto forward = location.forward;
- auto left = location.left;
- auto start_quad = start_location.quad;
- auto start_forward = start_location.forward;
- auto start_left = start_location.left;
- auto pass = local.pass;
- double*& points = local.points.current;
-
- auto start_point = get_boundary_start_point(location);
- auto end_point = start_point + forward;
-
- assert(is_point_in_chunk(start_point, local));
- assert(is_point_in_chunk(end_point, local));
-
- auto start_z = Z_LEVEL(start_point);
- auto end_z = Z_LEVEL(end_point);
-
- // Add new point, somewhere along start line. May be at start point of edge if this is a
- // boundary start.
- point_count++;
- if (pass > 0) {
- if (start_z == 1)
- get_point_xy(start_point, points);
- else // start_z != 1
- interp(start_point, end_point, location.is_upper, points);
- }
-
- bool finished = false;
- while (true) {
- assert(is_quad_in_chunk(quad, local));
-
- if (quad == start_quad && forward == start_forward && left == start_left) {
- if (start_location.on_boundary && point_count > 1) {
- // Polygon closed.
- finished = true;
- break;
- }
- }
- else if (pass == 0) {
- // Clear unwanted start locations.
- if (left == _nx) {
- if (START_BOUNDARY_S(quad)) {
- assert(forward == 1);
- _cache[quad] &= ~MASK_START_BOUNDARY_S;
- }
- }
- else if (forward == -_nx) {
- if (START_BOUNDARY_W(quad)) {
- assert(left == 1);
- _cache[quad] &= ~MASK_START_BOUNDARY_W;
- }
- }
- else if (left == -_nx) {
- if (START_HOLE_N(quad)) {
- assert(forward == -1);
- _cache[quad] &= ~MASK_START_HOLE_N;
- }
- }
- else {
- switch (EXISTS_ANY_CORNER(quad)) {
- case MASK_EXISTS_NE_CORNER:
- if (left == _nx+1) {
- assert(forward == -_nx+1);
- _cache[quad] &= ~MASK_START_CORNER;
- }
- break;
- case MASK_EXISTS_NW_CORNER:
- if (forward == _nx+1) {
- assert(left == _nx-1);
- _cache[quad] &= ~MASK_START_CORNER;
- }
- break;
- case MASK_EXISTS_SE_CORNER:
- if (forward == -_nx-1) {
- assert(left == -_nx+1);
- _cache[quad] &= ~MASK_START_CORNER;
- }
- break;
- case MASK_EXISTS_SW_CORNER:
- if (left == -_nx-1) {
- assert(forward == _nx-1);
- _cache[quad] &= ~MASK_START_CORNER;
- }
- break;
- default:
- // Not a corner.
- break;
- }
- }
- }
-
- // Check if need to leave boundary into interior.
- if (end_z != 1) {
- location.is_upper = (end_z == 2); // Leave via this level.
- auto temp = forward;
- forward = left;
- left = -temp;
- break;
- }
-
- // Add end point.
- point_count++;
- if (pass > 0) {
- get_point_xy(end_point, points);
-
- if (LOOK_N(quad) && _identify_holes &&
- (left == _nx || left == _nx+1 || forward == _nx+1)) {
- assert(BOUNDARY_N(quad-_nx) || EXISTS_NE_CORNER(quad) || EXISTS_NW_CORNER(quad));
- local.look_up_quads.push_back(quad);
- }
- }
-
- move_to_next_boundary_edge(quad, forward, left);
-
- start_point = end_point;
- start_z = end_z;
- end_point = start_point + forward;
- end_z = Z_LEVEL(end_point);
- }
-
- location.quad = quad;
- location.forward = forward;
- location.left = left;
-
- return finished;
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::follow_interior(
- Location& location, const Location& start_location, ChunkLocal& local, count_t& point_count)
-{
- // Adds the start point in each quad visited, but not the end point unless closing the polygon.
- // Only need to consider a single level of course.
- assert(is_quad_in_chunk(start_location.quad, local));
- assert(is_quad_in_chunk(location.quad, local));
-
- // Local variables for faster access.
- auto quad = location.quad;
- auto forward = location.forward;
- auto left = location.left;
- auto is_upper = location.is_upper;
- auto start_quad = start_location.quad;
- auto start_forward = start_location.forward;
- auto start_left = start_location.left;
- auto pass = local.pass;
- double*& points = local.points.current;
-
- // left direction, and indices of points on entry edge.
- bool start_corner_diagonal = false;
- auto left_point = get_interior_start_left_point(location, start_corner_diagonal);
- auto right_point = left_point - left;
- bool want_look_N = _identify_holes && pass > 0;
-
- bool finished = false; // Whether finished line, i.e. returned to start.
- while (true) {
- assert(is_quad_in_chunk(quad, local));
- assert(is_point_in_chunk(left_point, local));
- assert(is_point_in_chunk(right_point, local));
-
- if (pass > 0)
- interp(left_point, right_point, is_upper, points);
- point_count++;
-
- if (quad == start_quad && forward == start_forward &&
- left == start_left && is_upper == start_location.is_upper &&
- !start_location.on_boundary && point_count > 1) {
- finished = true; // Polygon closed, exit immediately.
- break;
- }
-
- // Indices of the opposite points.
- auto opposite_left_point = left_point + forward;
- auto opposite_right_point = right_point + forward;
- bool corner_opposite_is_right = false; // Only used for corners.
-
- if (start_corner_diagonal) {
- // To avoid dealing with diagonal forward and left below, switch to direction 45 degrees
- // to left, e.g. NW corner faces west using forward == -1.
- corner_opposite_is_right = true;
- switch (EXISTS_ANY_CORNER(quad)) {
- case MASK_EXISTS_NW_CORNER:
- forward = -1;
- left = -_nx;
- opposite_left_point = opposite_right_point = quad-1;
- break;
- case MASK_EXISTS_NE_CORNER:
- forward = _nx;
- left = -1;
- opposite_left_point = opposite_right_point = quad;
- break;
- case MASK_EXISTS_SW_CORNER:
- forward = -_nx;
- left = 1;
- opposite_left_point = opposite_right_point = quad-_nx-1;
- break;
- default:
- assert(EXISTS_SE_CORNER(quad));
- forward = 1;
- left = _nx;
- opposite_left_point = opposite_right_point = quad-_nx;
- break;
- }
- }
-
- // z-levels of the opposite points.
- ZLevel z_opposite_left = Z_LEVEL(opposite_left_point);
- ZLevel z_opposite_right = Z_LEVEL(opposite_right_point);
-
- Direction direction = Direction::Right;
- ZLevel z_test = is_upper ? 2 : 0;
-
- if (EXISTS_QUAD(quad)) {
- if (z_opposite_left == z_test) {
- if (z_opposite_right == z_test || MIDDLE_Z_LEVEL(quad) == z_test)
- direction = Direction::Left;
- }
- else if (z_opposite_right == z_test)
- direction = Direction::Straight;
- }
- else if (start_corner_diagonal) {
- direction = (z_opposite_left == z_test) ? Direction::Straight : Direction::Right;
- }
- else {
- switch (EXISTS_ANY_CORNER(quad)) {
- case MASK_EXISTS_NW_CORNER:
- corner_opposite_is_right = (forward == -_nx);
- break;
- case MASK_EXISTS_NE_CORNER:
- corner_opposite_is_right = (forward == -1);
- break;
- case MASK_EXISTS_SW_CORNER:
- corner_opposite_is_right = (forward == 1);
- break;
- default:
- assert(EXISTS_SE_CORNER(quad));
- corner_opposite_is_right = (forward == _nx);
- break;
- }
-
- if (corner_opposite_is_right)
- direction = (z_opposite_right == z_test) ? Direction::Straight : Direction::Right;
- else
- direction = (z_opposite_left == z_test) ? Direction::Left : Direction::Straight;
- }
-
- // Clear unwanted start locations.
- if (pass == 0 && !(quad == start_quad && forward == start_forward && left == start_left)) {
- if (START_E(quad) && forward == -1 && left == -_nx && direction == Direction::Right &&
- (is_upper ? Z_NE > 0 : Z_NE < 2)) {
- _cache[quad] &= ~MASK_START_E; // E high if is_upper else low.
-
- if (!_filled && quad < start_location.quad)
- // Already counted points from here onwards.
- break;
- }
- else if (START_N(quad) && forward == -_nx && left == 1 &&
- direction == Direction::Left && (is_upper ? Z_NW > 0 : Z_NW < 2)) {
- _cache[quad] &= ~MASK_START_N; // E high if is_upper else low.
-
- if (!_filled && quad < start_location.quad)
- // Already counted points from here onwards.
- break;
- }
- }
-
- // Extra quad_as_tri points.
- if (_quad_as_tri && EXISTS_QUAD(quad)) {
- if (pass == 0) {
- switch (direction) {
- case Direction::Left:
- point_count += (LEFT_OF_MIDDLE(quad, is_upper) ? 1 : 3);
- break;
- case Direction::Right:
- point_count += (LEFT_OF_MIDDLE(quad, is_upper) ? 3 : 1);
- break;
- case Direction::Straight:
- point_count += 2;
- break;
- }
- }
- else { // pass == 1
- auto mid_x = get_middle_x(quad);
- auto mid_y = get_middle_y(quad);
- auto mid_z = calc_middle_z(quad);
-
- switch (direction) {
- case Direction::Left:
- if (LEFT_OF_MIDDLE(quad, is_upper)) {
- interp(left_point, mid_x, mid_y, mid_z, is_upper, points);
- point_count++;
- }
- else {
- interp(right_point, mid_x, mid_y, mid_z, is_upper, points);
- interp(opposite_right_point, mid_x, mid_y, mid_z, is_upper, points);
- interp(opposite_left_point, mid_x, mid_y, mid_z, is_upper, points);
- point_count += 3;
- }
- break;
- case Direction::Right:
- if (LEFT_OF_MIDDLE(quad, is_upper)) {
- interp(left_point, mid_x, mid_y, mid_z, is_upper, points);
- interp(opposite_left_point, mid_x, mid_y, mid_z, is_upper, points);
- interp(opposite_right_point, mid_x, mid_y, mid_z, is_upper, points);
- point_count += 3;
- }
- else {
- interp(right_point, mid_x, mid_y, mid_z, is_upper, points);
- point_count++;
- }
- break;
- case Direction::Straight:
- if (LEFT_OF_MIDDLE(quad, is_upper)) {
- interp(left_point, mid_x, mid_y, mid_z, is_upper, points);
- interp(opposite_left_point, mid_x, mid_y, mid_z, is_upper, points);
- }
- else {
- interp(right_point, mid_x, mid_y, mid_z, is_upper, points);
- interp(opposite_right_point, mid_x, mid_y, mid_z, is_upper, points);
- }
- point_count += 2;
- break;
- }
- }
- }
-
- bool reached_boundary = false;
-
- // Determine entry edge and left and right points of next quad.
- // Do not update quad index yet.
- switch (direction) {
- case Direction::Left: {
- auto temp = forward;
- forward = left;
- left = -temp;
- // left_point unchanged.
- right_point = opposite_left_point;
- break;
- }
- case Direction::Right: {
- auto temp = forward;
- forward = -left;
- left = temp;
- left_point = opposite_right_point;
- // right_point unchanged.
- break;
- }
- case Direction::Straight: {
- if (EXISTS_QUAD(quad)) { // Straight on in quad.
- // forward and left stay the same.
- left_point = opposite_left_point;
- right_point = opposite_right_point;
- }
- else if (start_corner_diagonal) { // Straight on diagonal start corner.
- // left point unchanged.
- right_point = opposite_right_point;
- }
- else { // Straight on in a corner reaches boundary.
- assert(EXISTS_ANY_CORNER(quad));
- reached_boundary = true;
-
- if (corner_opposite_is_right) {
- // left_point unchanged.
- right_point = opposite_right_point;
- }
- else {
- left_point = opposite_left_point;
- // right_point unchanged.
- }
-
- // Set forward and left for correct exit along boundary.
- switch (EXISTS_ANY_CORNER(quad)) {
- case MASK_EXISTS_NW_CORNER:
- forward = _nx+1;
- left = _nx-1;
- break;
- case MASK_EXISTS_NE_CORNER:
- forward = -_nx+1;
- left = _nx+1;
- break;
- case MASK_EXISTS_SW_CORNER:
- forward = _nx-1;
- left = -_nx-1;
- break;
- default:
- assert(EXISTS_SE_CORNER(quad));
- forward = -_nx-1;
- left = -_nx+1;
- break;
- }
- }
- break;
- }
- }
-
- if (want_look_N && LOOK_N(quad) && forward == 1) {
- // Only consider look_N if pass across E edge of this quad.
- // Care needed if both look_N and look_S set in quad because this line corresponds to
- // only one of them, so want to ignore the look_N if it is the other line otherwise it
- // will be double counted.
- if (!LOOK_S(quad) || (is_upper ? Z_NE < 2 : Z_NE > 0))
- local.look_up_quads.push_back(quad);
- }
-
- // Check if reached NSEW boundary; already checked and noted if reached corner boundary.
- if (!reached_boundary) {
- if (forward > 0)
- reached_boundary = (forward == 1 ? BOUNDARY_E(quad) : BOUNDARY_N(quad));
- else // forward < 0
- reached_boundary = (forward == -1 ? BOUNDARY_W(quad) : BOUNDARY_S(quad));
-
- if (reached_boundary) {
- auto temp = forward;
- forward = left;
- left = -temp;
- }
- }
-
- // If reached a boundary, return.
- if (reached_boundary) {
- if (!_filled) {
- point_count++;
- if (pass > 0)
- interp(left_point, right_point, false, points);
- }
- break;
- }
-
- quad += forward;
- start_corner_diagonal = false;
- }
-
- location.quad = quad;
- location.forward = forward;
- location.left = left;
- location.is_upper = is_upper;
-
- return finished;
-}
-
-template <typename Derived>
-index_t BaseContourGenerator<Derived>::get_boundary_start_point(const Location& location) const
-{
- auto quad = location.quad;
- auto forward = location.forward;
- auto left = location.left;
- index_t start_point = -1;
-
- if (forward > 0) {
- if (forward == _nx) {
- assert(left == -1);
- start_point = quad-_nx;
- }
- else if (left == _nx) {
- assert(forward == 1);
- start_point = quad-_nx-1;
- }
- else if (EXISTS_SW_CORNER(quad)) {
- assert(forward == _nx-1 && left == -_nx-1);
- start_point = quad-_nx;
- }
- else {
- assert(EXISTS_NW_CORNER(quad) && forward == _nx+1 && left == _nx-1);
- start_point = quad-_nx-1;
- }
- }
- else { // forward < 0
- if (forward == -_nx) {
- assert(left == 1);
- start_point = quad-1;
- }
- else if (left == -_nx) {
- assert(forward == -1);
- start_point = quad;
- }
- else if (EXISTS_NE_CORNER(quad)) {
- assert(forward == -_nx+1 && left == _nx+1);
- start_point = quad-1;
- }
- else {
- assert(EXISTS_SE_CORNER(quad) && forward == -_nx-1 && left == -_nx+1);
- start_point = quad;
- }
- }
- return start_point;
-}
-
-template <typename Derived>
-py::tuple BaseContourGenerator<Derived>::get_chunk_count() const
-{
- return py::make_tuple(_ny_chunks, _nx_chunks);
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::get_chunk_limits(index_t chunk, ChunkLocal& local) const
-{
- assert(chunk >= 0 && chunk < _n_chunks && "chunk index out of bounds");
-
- local.chunk = chunk;
-
- auto ichunk = chunk % _nx_chunks;
- auto jchunk = chunk / _nx_chunks;
-
- local.istart = ichunk*_x_chunk_size + 1;
- local.iend = (ichunk < _nx_chunks-1 ? (ichunk+1)*_x_chunk_size : _nx-1);
-
- local.jstart = jchunk*_y_chunk_size + 1;
- local.jend = (jchunk < _ny_chunks-1 ? (jchunk+1)*_y_chunk_size : _ny-1);
-}
-
-template <typename Derived>
-py::tuple BaseContourGenerator<Derived>::get_chunk_size() const
-{
- return py::make_tuple(_y_chunk_size, _x_chunk_size);
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::get_corner_mask() const
-{
- return _corner_mask;
-}
-
-template <typename Derived>
-FillType BaseContourGenerator<Derived>::get_fill_type() const
-{
- return _fill_type;
-}
-
-template <typename Derived>
-index_t BaseContourGenerator<Derived>::get_interior_start_left_point(
- const Location& location, bool& start_corner_diagonal) const
-{
- auto quad = location.quad;
- auto forward = location.forward;
- auto left = location.left;
- index_t left_point = -1;
-
- if (forward > 0) {
- if (forward == _nx) {
- assert(left == -1);
- left_point = quad-_nx-1;
- }
- else if (left == _nx) {
- assert(forward == 1);
- left_point = quad-1;
- }
- else if (EXISTS_NW_CORNER(quad)) {
- assert(forward == _nx-1 && left == -_nx-1);
- left_point = quad-_nx-1;
- start_corner_diagonal = true;
- }
- else {
- assert(EXISTS_NE_CORNER(quad) && forward == _nx+1 && left == _nx-1);
- left_point = quad-1;
- start_corner_diagonal = true;
- }
- }
- else { // forward < 0
- if (forward == -_nx) {
- assert(left == 1);
- left_point = quad;
- }
- else if (left == -_nx) {
- assert(forward == -1);
- left_point = quad-_nx;
- }
- else if (EXISTS_SW_CORNER(quad)) {
- assert(forward == -_nx-1 && left == -_nx+1);
- left_point = quad-_nx;
- start_corner_diagonal = true;
- }
- else {
- assert(EXISTS_SE_CORNER(quad) && forward == -_nx+1 && left == _nx+1);
- left_point = quad;
- start_corner_diagonal = true;
- }
- }
- return left_point;
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::get_interp_fraction(double z0, double z1, double level) const
-{
- switch (_z_interp) {
- case ZInterp::Log:
- // Equivalent to
- // (log(z1) - log(level)) / (log(z1) - log(z0))
- // Same result obtained regardless of logarithm base.
- return log(z1/level) / log(z1/z0);
- default: // ZInterp::Linear
- return (z1 - level) / (z1 - z0);
- }
-}
-
-template <typename Derived>
-LineType BaseContourGenerator<Derived>::get_line_type() const
-{
- return _line_type;
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::get_middle_x(index_t quad) const
-{
- return 0.25*(get_point_x(POINT_SW) + get_point_x(POINT_SE) +
- get_point_x(POINT_NW) + get_point_x(POINT_NE));
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::get_middle_y(index_t quad) const
-{
- return 0.25*(get_point_y(POINT_SW) + get_point_y(POINT_SE) +
- get_point_y(POINT_NW) + get_point_y(POINT_NE));
-}
-
-template <typename Derived>
-index_t BaseContourGenerator<Derived>::get_n_chunks() const
-{
- return _n_chunks;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::get_point_xy(index_t point, double*& points) const
-{
- assert(point >= 0 && point < _n && "point index out of bounds");
- *points++ = _xptr[point];
- *points++ = _yptr[point];
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::get_point_x(index_t point) const
-{
- assert(point >= 0 && point < _n && "point index out of bounds");
- return _xptr[point];
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::get_point_y(index_t point) const
-{
- assert(point >= 0 && point < _n && "point index out of bounds");
- return _yptr[point];
-}
-
-template <typename Derived>
-double BaseContourGenerator<Derived>::get_point_z(index_t point) const
-{
- assert(point >= 0 && point < _n && "point index out of bounds");
- return _zptr[point];
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::get_quad_as_tri() const
-{
- return _quad_as_tri;
-}
-
-template <typename Derived>
-ZInterp BaseContourGenerator<Derived>::get_z_interp() const
-{
- return _z_interp;
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::has_direct_line_offsets() const
-{
- return _direct_line_offsets;
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::has_direct_outer_offsets() const
-{
- return _direct_outer_offsets;
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::has_direct_points() const
-{
- return _direct_points;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::init_cache_grid(const MaskArray& mask)
-{
- index_t i, j, quad;
- if (mask.ndim() == 0) {
- // No mask, easy to calculate quad existence and boundaries together.
- for (j = 0, quad = 0; j < _ny; ++j) {
- for (i = 0; i < _nx; ++i, ++quad) {
- _cache[quad] = 0;
-
- if (i > 0 && j > 0)
- _cache[quad] |= MASK_EXISTS_QUAD;
-
- if ((i % _x_chunk_size == 0 || i == _nx-1) && j > 0)
- _cache[quad] |= MASK_BOUNDARY_E;
-
- if ((j % _y_chunk_size == 0 || j == _ny-1) && i > 0)
- _cache[quad] |= MASK_BOUNDARY_N;
- }
- }
- }
- else {
- // Could maybe speed this up and just have a single pass.
- // Care would be needed with lookback of course.
- const bool* mask_ptr = mask.data();
-
- // Have mask so use two stages.
- // Stage 1, determine if quads/corners exist.
- quad = 0;
- for (j = 0; j < _ny; ++j) {
- for (i = 0; i < _nx; ++i, ++quad) {
- _cache[quad] = 0;
-
- if (i > 0 && j > 0) {
- unsigned int config = (mask_ptr[POINT_NW] << 3) |
- (mask_ptr[POINT_NE] << 2) |
- (mask_ptr[POINT_SW] << 1) |
- (mask_ptr[POINT_SE] << 0);
- if (_corner_mask) {
- switch (config) {
- case 0: _cache[quad] = MASK_EXISTS_QUAD; break;
- case 1: _cache[quad] = MASK_EXISTS_NW_CORNER; break;
- case 2: _cache[quad] = MASK_EXISTS_NE_CORNER; break;
- case 4: _cache[quad] = MASK_EXISTS_SW_CORNER; break;
- case 8: _cache[quad] = MASK_EXISTS_SE_CORNER; break;
- default:
- // Do nothing, quad is masked out.
- break;
- }
- }
- else if (config == 0)
- _cache[quad] = MASK_EXISTS_QUAD;
- }
- }
- }
-
- // Stage 2, calculate N and E boundaries.
- quad = 0;
- for (j = 0; j < _ny; ++j) {
- bool j_chunk_boundary = j % _y_chunk_size == 0;
-
- for (i = 0; i < _nx; ++i, ++quad) {
- bool i_chunk_boundary = i % _x_chunk_size == 0;
-
- if (_corner_mask) {
- bool exists_E_edge = EXISTS_E_EDGE(quad);
- bool E_exists_W_edge = (i < _nx-1 && EXISTS_W_EDGE(quad+1));
- bool exists_N_edge = EXISTS_N_EDGE(quad);
- bool N_exists_S_edge = (j < _ny-1 && EXISTS_S_EDGE(quad+_nx));
-
- if (exists_E_edge != E_exists_W_edge ||
- (i_chunk_boundary && exists_E_edge && E_exists_W_edge))
- _cache[quad] |= MASK_BOUNDARY_E;
-
- if (exists_N_edge != N_exists_S_edge ||
- (j_chunk_boundary && exists_N_edge && N_exists_S_edge))
- _cache[quad] |= MASK_BOUNDARY_N;
- }
- else {
- bool E_exists_quad = (i < _nx-1 && EXISTS_QUAD(quad+1));
- bool N_exists_quad = (j < _ny-1 && EXISTS_QUAD(quad+_nx));
- bool exists = EXISTS_QUAD(quad);
-
- if (exists != E_exists_quad || (i_chunk_boundary && exists && E_exists_quad))
- _cache[quad] |= MASK_BOUNDARY_E;
-
- if (exists != N_exists_quad || (j_chunk_boundary && exists && N_exists_quad))
- _cache[quad] |= MASK_BOUNDARY_N;
- }
- }
- }
- }
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::init_cache_levels_and_starts(const ChunkLocal* local)
-{
- bool ordered_chunks = (local == nullptr);
-
- // This function initialises the cache z-levels and starts for either a single chunk or the
- // whole domain. If a single chunk, only the quads contained in the chunk are calculated and
- // this includes the z-levels of the points that on the NE corners of those quads. In addition,
- // chunks that are on the W (starting at i=1) also calculate the most westerly points (i=0),
- // and similarly chunks that are on the S (starting at j=1) also calculate the most southerly
- // points (j=0). Non W/S chunks do not do this as their neighboring chunks to the W/S are
- // responsible for it. If ordered_chunks is true then those W/S points will already have had
- // their cache items set so that their z-levels can be read from the cache as usual. But if
- // ordered_chunks is false then we cannot rely upon those neighboring W/S points having their
- // cache items already set and so must temporarily calculate those z-levels rather than reading
- // the cache.
-
- constexpr CacheItem keep_mask = (MASK_EXISTS_ANY | MASK_BOUNDARY_N | MASK_BOUNDARY_E);
-
- index_t istart, iend, jstart, jend; // Loop indices.
- index_t chunk_istart; // Actual start i-index of chunk.
-
- if (local != nullptr) {
- chunk_istart = local->istart;
- istart = chunk_istart > 1 ? chunk_istart : 0;
- iend = local->iend;
- jstart = local->jstart > 1 ? local->jstart : 0;
- jend = local->jend;
- }
- else {
- chunk_istart = 1;
- istart = 0;
- iend = _nx-1;
- jstart = 0;
- jend = _ny-1;
- }
-
- index_t j_final_start = jstart - 1;
- bool calc_W_z_level = (!ordered_chunks && istart == chunk_istart);
-
- for (index_t j = jstart; j <= jend; ++j) {
- index_t quad = istart + j*_nx;
- const double* z_ptr = _zptr + quad;
- bool start_in_row = false;
- bool calc_S_z_level = (!ordered_chunks && j == jstart);
-
- // z-level of NW point not needed if i == 0.
- ZLevel z_nw = (istart == 0) ? 0 : (calc_W_z_level ? z_to_zlevel(*(z_ptr-1)) : Z_NW);
-
- // z-level of SW point not needed if i == 0 or j == 0.
- ZLevel z_sw = (istart == 0 || j == 0) ? 0 :
- ((calc_W_z_level || calc_S_z_level) ? z_to_zlevel(*(z_ptr-_nx-1)) : Z_SW);
-
- for (index_t i = istart; i <= iend; ++i, ++quad, ++z_ptr) {
- // z-level of SE point not needed if j == 0.
- ZLevel z_se = (j == 0) ? 0 : (calc_S_z_level ? z_to_zlevel(*(z_ptr-_nx)) : Z_SE);
-
- _cache[quad] &= keep_mask;
-
- // Calculate and cache z-level of NE point.
- ZLevel z_ne = z_to_zlevel(*z_ptr);
- _cache[quad] |= z_ne;
-
- switch (EXISTS_ANY(quad)) {
- case MASK_EXISTS_QUAD:
- if (_filled) {
- switch ((z_nw << 6) | (z_ne << 4) | (z_sw << 2) | z_se) { // config
- case 1: // 0001
- case 2: // 0002
- case 17: // 0101
- case 18: // 0102
- case 34: // 0202
- case 68: // 1010
- case 102: // 1212
- case 136: // 2020
- case 152: // 2120
- case 153: // 2121
- case 168: // 2220
- case 169: // 2221
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row = true;
- }
- break;
- case 4: // 0010
- case 5: // 0011
- case 6: // 0012
- case 8: // 0020
- case 9: // 0021
- case 21: // 0111
- case 22: // 0112
- case 25: // 0121
- case 38: // 0212
- case 72: // 1020
- case 98: // 1202
- case 132: // 2010
- case 145: // 2101
- case 148: // 2110
- case 149: // 2111
- case 161: // 2201
- case 162: // 2202
- case 164: // 2210
- case 165: // 2211
- case 166: // 2212
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row |= ANY_START(quad);
- break;
- case 10: // 0022
- case 26: // 0122
- case 42: // 0222
- case 64: // 1000
- case 106: // 1222
- case 128: // 2000
- case 144: // 2100
- case 160: // 2200
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_W(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row = true;
- }
- break;
- case 16: // 0100
- case 154: // 2122
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- _cache[quad] |= MASK_START_N;
- start_in_row = true;
- break;
- case 20: // 0110
- case 24: // 0120
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) == 0) _cache[quad] |= MASK_START_N;
- start_in_row |= ANY_START(quad);
- break;
- case 32: // 0200
- case 138: // 2022
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- _cache[quad] |= MASK_START_E;
- _cache[quad] |= MASK_START_N;
- start_in_row = true;
- break;
- case 33: // 0201
- case 69: // 1011
- case 70: // 1012
- case 100: // 1210
- case 101: // 1211
- case 137: // 2021
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- _cache[quad] |= MASK_START_E;
- start_in_row = true;
- break;
- case 36: // 0210
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) == 0) _cache[quad] |= MASK_START_N;
- _cache[quad] |= MASK_START_E;
- start_in_row = true;
- break;
- case 37: // 0211
- case 73: // 1021
- case 97: // 1201
- case 133: // 2011
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- _cache[quad] |= MASK_START_E;
- start_in_row = true;
- break;
- case 40: // 0220
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) < 2) _cache[quad] |= MASK_START_E;
- if (MIDDLE_Z_LEVEL(quad) == 0) _cache[quad] |= MASK_START_N;
- start_in_row |= ANY_START(quad);
- break;
- case 41: // 0221
- case 104: // 1220
- case 105: // 1221
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) < 2) _cache[quad] |= MASK_START_E;
- start_in_row |= ANY_START(quad);
- break;
- case 65: // 1001
- case 66: // 1002
- case 129: // 2001
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) > 0) _cache[quad] |= MASK_START_E;
- start_in_row |= ANY_START(quad);
- break;
- case 74: // 1022
- case 96: // 1200
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- _cache[quad] |= MASK_START_E;
- start_in_row = true;
- break;
- case 80: // 1100
- case 90: // 1122
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (BOUNDARY_N(quad) && !START_HOLE_N(quad-1) &&
- j % _y_chunk_size > 0 && j != _ny-1 && i % _x_chunk_size > 1)
- _cache[quad] |= MASK_START_HOLE_N;
- start_in_row |= ANY_START(quad);
- break;
- case 81: // 1101
- case 82: // 1102
- case 88: // 1120
- case 89: // 1121
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (BOUNDARY_N(quad) && !START_HOLE_N(quad-1) &&
- j % _y_chunk_size > 0 && j != _ny-1 && i % _x_chunk_size > 1)
- _cache[quad] |= MASK_START_HOLE_N;
- start_in_row |= ANY_START(quad);
- break;
- case 84: // 1110
- case 85: // 1111
- case 86: // 1112
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_N(quad) && !START_HOLE_N(quad-1) &&
- j % _y_chunk_size > 0 && j != _ny-1 && i % _x_chunk_size > 1)
- _cache[quad] |= MASK_START_HOLE_N;
- start_in_row |= ANY_START(quad);
- break;
- case 130: // 2002
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) > 0) _cache[quad] |= MASK_START_E;
- if (MIDDLE_Z_LEVEL(quad) == 2) _cache[quad] |= MASK_START_N;
- start_in_row |= ANY_START(quad);
- break;
- case 134: // 2012
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) == 2) _cache[quad] |= MASK_START_N;
- _cache[quad] |= MASK_START_E;
- start_in_row = true;
- break;
- case 146: // 2102
- case 150: // 2112
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (MIDDLE_Z_LEVEL(quad) == 2) _cache[quad] |= MASK_START_N;
- start_in_row |= ANY_START(quad);
- break;
- }
- }
- else { // !_filled quad
- switch ((z_nw << 3) | (z_ne << 2) | (z_sw << 1) | z_se) { // config
- case 1: // 0001
- case 3: // 0011
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_E(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_E;
- start_in_row = true;
- }
- break;
- case 2: // 0010
- case 10: // 1010
- case 14: // 1110
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_S(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row = true;
- }
- break;
- case 4: // 0100
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_N(quad))
- _cache[quad] |= MASK_START_BOUNDARY_N;
- else if (!BOUNDARY_E(quad))
- _cache[quad] |= MASK_START_N;
- start_in_row |= ANY_START(quad);
- break;
- case 5: // 0101
- case 7: // 0111
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_N(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_N;
- start_in_row = true;
- }
- break;
- case 6: // 0110
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_N(quad))
- _cache[quad] |= MASK_START_BOUNDARY_N;
- else if (!BOUNDARY_E(quad) && MIDDLE_Z_LEVEL(quad) == 0)
- _cache[quad] |= MASK_START_N;
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row |= ANY_START(quad);
- break;
- case 8: // 1000
- case 12: // 1100
- case 13: // 1101
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_W(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row = true;
- }
- break;
- case 9: // 1001
- calc_and_set_middle_z_level(quad);
- if (BOUNDARY_E(quad))
- _cache[quad] |= MASK_START_BOUNDARY_E;
- else if (!BOUNDARY_N(quad) && MIDDLE_Z_LEVEL(quad) == 1)
- _cache[quad] |= MASK_START_E;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row |= ANY_START(quad);
- break;
- case 11: // 1011
- if (_quad_as_tri) calc_and_set_middle_z_level(quad);
- if (BOUNDARY_E(quad))
- _cache[quad] |= MASK_START_BOUNDARY_E;
- else if (!BOUNDARY_N(quad))
- _cache[quad] |= MASK_START_E;
- start_in_row |= ANY_START(quad);
- break;
- }
- }
- break;
- case MASK_EXISTS_NW_CORNER:
- if (_filled) {
- switch ((z_nw << 4) | (z_ne << 2) | z_sw) { // config
- case 1: // 001
- case 5: // 011
- case 9: // 021
- case 10: // 022
- case 16: // 100
- case 17: // 101
- case 25: // 121
- case 26: // 122
- case 32: // 200
- case 33: // 201
- case 37: // 211
- case 41: // 221
- if (BOUNDARY_W(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row = true;
- }
- break;
- case 2: // 002
- case 6: // 012
- case 18: // 102
- case 24: // 120
- case 36: // 210
- case 40: // 220
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 4: // 010
- case 8: // 020
- case 34: // 202
- case 38: // 212
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 20: // 110
- case 22: // 112
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (BOUNDARY_N(quad) && !START_HOLE_N(quad-1) &&
- j % _y_chunk_size > 0 && j != _ny-1 && i % _x_chunk_size > 1)
- _cache[quad] |= MASK_START_HOLE_N;
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 21: // 111
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- if (BOUNDARY_N(quad) && !START_HOLE_N(quad-1) &&
- j % _y_chunk_size > 0 && j != _ny-1 && i % _x_chunk_size > 1)
- _cache[quad] |= MASK_START_HOLE_N;
- start_in_row |= ANY_START(quad);
- break;
- }
- }
- else { // !_filled NW corner.
- switch ((z_nw << 2) | (z_ne << 1) | z_sw) { // config
- case 1: // 001
- case 5: // 101
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 2: // 010
- case 3: // 011
- if (BOUNDARY_N(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_N;
- start_in_row = true;
- }
- break;
- case 4: // 100
- case 6: // 110
- if (BOUNDARY_W(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row = true;
- }
- break;
- }
- }
- break;
- case MASK_EXISTS_NE_CORNER:
- if (_filled) {
- switch ((z_nw << 4) | (z_ne << 2) | z_se) { // config
- case 1: // 001
- case 2: // 002
- case 5: // 011
- case 6: // 012
- case 10: // 022
- case 16: // 100
- case 26: // 122
- case 32: // 200
- case 36: // 210
- case 37: // 211
- case 40: // 220
- case 41: // 221
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 4: // 010
- case 38: // 212
- _cache[quad] |= MASK_START_N;
- start_in_row = true;
- break;
- case 8: // 020
- case 34: // 202
- _cache[quad] |= MASK_START_E;
- _cache[quad] |= MASK_START_N;
- start_in_row = true;
- break;
- case 9: // 021
- case 17: // 101
- case 18: // 102
- case 24: // 120
- case 25: // 121
- case 33: // 201
- _cache[quad] |= MASK_START_CORNER;
- _cache[quad] |= MASK_START_E;
- start_in_row = true;
- break;
- case 20: // 110
- case 21: // 111
- case 22: // 112
- if (BOUNDARY_N(quad) && !START_HOLE_N(quad-1) &&
- j % _y_chunk_size > 0 && j != _ny-1 && i % _x_chunk_size > 1)
- _cache[quad] |= MASK_START_HOLE_N;
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- }
- }
- else { // !_filled NE corner.
- switch ((z_nw << 2) | (z_ne << 1) | z_se) { // config
- case 1: // 001
- if (BOUNDARY_E(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_E;
- start_in_row = true;
- }
- break;
- case 2: // 010
- if (BOUNDARY_N(quad))
- _cache[quad] |= MASK_START_BOUNDARY_N;
- else if (!BOUNDARY_E(quad))
- _cache[quad] |= MASK_START_N;
- start_in_row |= ANY_START(quad);
- break;
- case 3: // 011
- if (BOUNDARY_N(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_N;
- start_in_row = true;
- }
- break;
- case 4: // 100
- case 6: // 110
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 5: // 101
- if (BOUNDARY_E(quad))
- _cache[quad] |= MASK_START_BOUNDARY_E;
- else if (!BOUNDARY_N(quad))
- _cache[quad] |= MASK_START_E;
- start_in_row |= ANY_START(quad);
- break;
- }
- }
- break;
- case MASK_EXISTS_SW_CORNER:
- if (_filled) {
- switch ((z_nw << 4) | (z_sw << 2) | z_se) { // config
- case 1: // 001
- case 2: // 002
- case 40: // 220
- case 41: // 221
- if (BOUNDARY_S(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row = true;
- }
- break;
- case 4: // 010
- case 5: // 011
- case 6: // 012
- case 8: // 020
- case 9: // 021
- case 18: // 102
- case 24: // 120
- case 33: // 201
- case 34: // 202
- case 36: // 210
- case 37: // 211
- case 38: // 212
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row |= ANY_START(quad);
- break;
- case 10: // 022
- case 16: // 100
- case 26: // 122
- case 32: // 200
- if (BOUNDARY_W(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row = true;
- }
- break;
- case 17: // 101
- case 25: // 121
- if (BOUNDARY_S(quad)) _cache[quad] |= MASK_START_BOUNDARY_S;
- if (BOUNDARY_W(quad)) _cache[quad] |= MASK_START_BOUNDARY_W;
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 20: // 110
- case 21: // 111
- case 22: // 112
- if (BOUNDARY_S(quad))
- _cache[quad] |= MASK_START_BOUNDARY_S;
- else
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- }
- }
- else { // !_filled SW corner.
- switch ((z_nw << 2) | (z_sw << 1) | z_se) { // config
- case 1: // 001
- case 3: // 011
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- case 2: // 010
- case 6: // 110
- if (BOUNDARY_S(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row = true;
- }
- break;
- case 4: // 100
- case 5: // 101
- if (BOUNDARY_W(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_W;
- start_in_row = true;
- }
- break;
- }
- }
- break;
- case MASK_EXISTS_SE_CORNER:
- if (_filled) {
- switch ((z_ne << 4) | (z_sw << 2) | z_se) { // config
- case 1: // 001
- case 2: // 002
- case 4: // 010
- case 5: // 011
- case 6: // 012
- case 8: // 020
- case 9: // 021
- case 17: // 101
- case 18: // 102
- case 20: // 110
- case 21: // 111
- case 22: // 112
- case 24: // 120
- case 25: // 121
- case 33: // 201
- case 34: // 202
- case 36: // 210
- case 37: // 211
- case 38: // 212
- case 40: // 220
- case 41: // 221
- if (BOUNDARY_S(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row = true;
- }
- break;
- case 10: // 022
- case 16: // 100
- case 26: // 122
- case 32: // 200
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- }
- }
- else { // !_filled SE corner.
- switch ((z_ne << 2) | (z_sw << 1) | z_se) { // config
- case 1: // 001
- case 3: // 011
- if (BOUNDARY_E(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_E;
- start_in_row = true;
- }
- break;
- case 2: // 010
- case 6: // 110
- if (BOUNDARY_S(quad)) {
- _cache[quad] |= MASK_START_BOUNDARY_S;
- start_in_row = true;
- }
- break;
- case 4: // 100
- case 5: // 101
- _cache[quad] |= MASK_START_CORNER;
- start_in_row = true;
- break;
- }
- }
- break;
- }
-
- z_nw = z_ne;
- z_sw = z_se;
- } // i-loop.
-
- if (start_in_row)
- j_final_start = j;
- else if (j > 0)
- _cache[chunk_istart + j*_nx] |= MASK_NO_STARTS_IN_ROW;
- } // j-loop.
-
- if (j_final_start < jend)
- _cache[chunk_istart + (j_final_start+1)*_nx] |= MASK_NO_MORE_STARTS;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::interp(
- index_t point0, index_t point1, bool is_upper, double*& points) const
-{
- auto frac = get_interp_fraction(
- get_point_z(point0), get_point_z(point1), is_upper ? _upper_level : _lower_level);
-
- assert(frac >= 0.0 && frac <= 1.0 && "Interp fraction out of bounds");
-
- *points++ = get_point_x(point0)*frac + get_point_x(point1)*(1.0 - frac);
- *points++ = get_point_y(point0)*frac + get_point_y(point1)*(1.0 - frac);
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::interp(
- index_t point0, double x1, double y1, double z1, bool is_upper, double*& points) const
-{
- auto frac = get_interp_fraction(
- get_point_z(point0), z1, is_upper ? _upper_level : _lower_level);
-
- assert(frac >= 0.0 && frac <= 1.0 && "Interp fraction out of bounds");
-
- *points++ = get_point_x(point0)*frac + x1*(1.0 - frac);
- *points++ = get_point_y(point0)*frac + y1*(1.0 - frac);
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::is_point_in_chunk(index_t point, const ChunkLocal& local) const
-{
- return is_quad_in_bounds(point, local.istart-1, local.iend, local.jstart-1, local.jend);
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::is_quad_in_bounds(
- index_t quad, index_t istart, index_t iend, index_t jstart, index_t jend) const
-{
- return (quad % _nx >= istart && quad % _nx <= iend &&
- quad / _nx >= jstart && quad / _nx <= jend);
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::is_quad_in_chunk(index_t quad, const ChunkLocal& local) const
-{
- return is_quad_in_bounds(quad, local.istart, local.iend, local.jstart, local.jend);
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::line(const Location& start_location, ChunkLocal& local)
-{
- // start_location.on_boundary indicates starts (and therefore also finishes)
-
- assert(is_quad_in_chunk(start_location.quad, local));
-
- Location location = start_location;
- count_t point_count = 0;
-
- // Insert nan if required before start of new line.
- if (_nan_separated and local.pass > 0 && local.line_count > 0) {
- *local.points.current++ = Util::nan;
- *local.points.current++ = Util::nan;
- }
-
- // finished == true indicates closed line loop.
- bool finished = follow_interior(location, start_location, local, point_count);
-
- if (local.pass > 0) {
- assert(local.line_offsets.current == local.line_offsets.start + local.line_count);
- *local.line_offsets.current++ = local.total_point_count;
- }
-
- if (local.pass == 0 && !start_location.on_boundary && !finished)
- // An internal start that isn't a line loop is part of a line strip that starts on a
- // boundary and will be traced later. Do not count it as a valid start in pass 0 and remove
- // the first point or it will be duplicated by the correct boundary-started line later.
- point_count--;
- else
- local.line_count++;
-
- local.total_point_count += point_count;
-}
-
-template <typename Derived>
-py::sequence BaseContourGenerator<Derived>::lines(double level)
-{
- pre_lines();
-
- _lower_level = _upper_level = level;
- return march_wrapper();
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::march_chunk(
- ChunkLocal& local, std::vector<py::list>& return_lists)
-{
- for (local.pass = 0; local.pass < 2; ++local.pass) {
- bool ignore_holes = (_identify_holes && local.pass == 1);
-
- index_t j_final_start = local.jstart;
- for (index_t j = local.jstart; j <= local.jend; ++j) {
- index_t quad = local.istart + j*_nx;
-
- if (NO_MORE_STARTS(quad))
- break;
-
- if (NO_STARTS_IN_ROW(quad))
- continue;
-
- // Want to count number of starts in this row, so store how many starts at start of row.
- auto prev_start_count =
- (_identify_holes ? local.line_count - local.hole_count : local.line_count);
-
- for (index_t i = local.istart; i <= local.iend; ++i, ++quad) {
- if (!ANY_START(quad))
- continue;
-
- assert(EXISTS_ANY(quad));
-
- if (_filled) {
- if (START_BOUNDARY_S(quad))
- closed_line_wrapper(Location(quad, 1, _nx, Z_SW == 2, true), Outer, local);
-
- if (START_BOUNDARY_W(quad))
- closed_line_wrapper(Location(quad, -_nx, 1, Z_NW == 2, true), Outer, local);
-
- if (START_CORNER(quad)) {
- switch (EXISTS_ANY_CORNER(quad)) {
- case MASK_EXISTS_NE_CORNER:
- closed_line_wrapper(
- Location(quad, -_nx+1, _nx+1, Z_NW == 2, true), Outer, local);
- break;
- case MASK_EXISTS_NW_CORNER:
- closed_line_wrapper(
- Location(quad, _nx+1, _nx-1, Z_SW == 2, true), Outer, local);
- break;
- case MASK_EXISTS_SE_CORNER:
- closed_line_wrapper(
- Location(quad, -_nx-1, -_nx+1, Z_NE == 2, true), Outer, local);
- break;
- default:
- assert(EXISTS_SW_CORNER(quad));
- if (!ignore_holes)
- closed_line_wrapper(
- Location(quad, _nx-1, -_nx-1, false, true), Hole, local);
- break;
- }
- }
-
- if (START_N(quad))
- closed_line_wrapper(Location(quad, -_nx, 1, Z_NW > 0, false), Outer, local);
-
- if (ignore_holes)
- continue;
-
- if (START_E(quad))
- closed_line_wrapper(Location(quad, -1, -_nx, Z_NE > 0, false), Hole, local);
-
- if (START_HOLE_N(quad))
- closed_line_wrapper(Location(quad, -1, -_nx, false, true), Hole, local);
- }
- else { // !_filled
- if (START_BOUNDARY_S(quad))
- line(Location(quad, _nx, -1, false, true), local);
-
- if (START_BOUNDARY_W(quad))
- line(Location(quad, 1, _nx, false, true), local);
-
- if (START_BOUNDARY_E(quad))
- line(Location(quad, -1, -_nx, false, true), local);
-
- if (START_BOUNDARY_N(quad))
- line(Location(quad, -_nx, 1, false, true), local);
-
- if (START_E(quad))
- line(Location(quad, -1, -_nx, false, false), local);
-
- if (START_N(quad))
- line(Location(quad, -_nx, 1, false, false), local);
-
- if (START_CORNER(quad)) {
- index_t forward, left;
- switch (EXISTS_ANY_CORNER(quad)) {
- case MASK_EXISTS_NE_CORNER:
- forward = _nx+1;
- left = _nx-1;
- break;
- case MASK_EXISTS_NW_CORNER:
- forward = _nx-1;
- left = -_nx-1;
- break;
- case MASK_EXISTS_SE_CORNER:
- forward = -_nx+1;
- left = _nx+1;
- break;
- default:
- assert(EXISTS_SW_CORNER(quad));
- forward = -_nx-1;
- left = -_nx+1;
- break;
- }
- line(Location(quad, forward, left, false, true), local);
- }
- } // _filled
- } // i
-
- // Number of starts at end of row.
- auto start_count =
- (_identify_holes ? local.line_count - local.hole_count : local.line_count);
- if (start_count > prev_start_count)
- j_final_start = j;
- else
- _cache[local.istart + j*_nx] |= MASK_NO_STARTS_IN_ROW;
- } // j
-
- if (j_final_start < local.jend)
- _cache[local.istart + (j_final_start+1)*_nx] |= MASK_NO_MORE_STARTS;
-
- if (_nan_separated && local.line_count > 1) {
- // If _nan_separated, each line after the first has an extra nan to separate it from the
- // previous line's points. If we were returning line offsets to the caller then this
- // would need to occur in line() where the line_count is incremented. But as we are not
- // returning line offsets it is faster just to add the extra here all at once.
- local.total_point_count += local.line_count - 1;
- }
-
- if (local.pass == 0) {
- if (local.total_point_count == 0) {
- local.points.clear();
- local.line_offsets.clear();
- local.outer_offsets.clear();
- break; // Do not need pass 1.
- }
-
- // Create arrays for points, line_offsets and optionally outer_offsets. Arrays may be
- // either C++ vectors or Python NumPy arrays. Want to group creation of the latter as
- // threaded code needs to lock creation of these to limit access to a single thread.
- if (_direct_points || _direct_line_offsets || _direct_outer_offsets) {
- typename Derived::Lock lock(static_cast<Derived&>(*this));
-
- // Strictly speaking adding the NumPy arrays to return_lists does not need to be
- // within the lock.
- if (_direct_points) {
- return_lists[0][local.chunk] =
- local.points.create_python(local.total_point_count, 2);
- }
- if (_direct_line_offsets) {
- return_lists[1][local.chunk] =
- local.line_offsets.create_python(local.line_count + 1);
- }
- if (_direct_outer_offsets) {
- return_lists[2][local.chunk] =
- local.outer_offsets.create_python(local.line_count - local.hole_count + 1);
- }
- }
-
- if (!_direct_points)
- local.points.create_cpp(2*local.total_point_count);
-
- if (!_direct_line_offsets)
- local.line_offsets.create_cpp(local.line_count + 1);
-
- if (!_direct_outer_offsets) {
- if (_identify_holes)
- local.outer_offsets.create_cpp( local.line_count - local.hole_count + 1);
- else
- local.outer_offsets.clear();
- }
-
- // Reset counts for pass 1.
- local.total_point_count = 0;
- local.line_count = 0;
- local.hole_count = 0;
- }
- } // pass
-
- // Set final line and outer offsets.
- if (local.line_count > 0) {
- *local.line_offsets.current++ = local.total_point_count;
-
- if (_identify_holes) {
- if (_outer_offsets_into_points)
- *local.outer_offsets.current++ = local.total_point_count;
- else
- *local.outer_offsets.current++ = local.line_count;
- }
- }
-
- // Throw exception if the two passes returned different number of points, lines, etc.
- check_consistent_counts(local);
-
- if (local.total_point_count == 0) {
- if (_output_chunked) {
- typename Derived::Lock lock(static_cast<Derived&>(*this));
- for (auto& list : return_lists)
- list[local.chunk] = py::none();
- }
- }
- else if (_filled)
- static_cast<Derived*>(this)->export_filled(local, return_lists);
- else
- static_cast<Derived*>(this)->export_lines(local, return_lists);
-}
-
-template <typename Derived>
-py::sequence BaseContourGenerator<Derived>::march_wrapper()
-{
- index_t list_len = _n_chunks;
- if ((_filled && (_fill_type == FillType::OuterCode|| _fill_type == FillType::OuterOffset)) ||
- (!_filled && (_line_type == LineType::Separate || _line_type == LineType::SeparateCode)))
- list_len = 0;
-
- // Prepare lists to return to python.
- std::vector<py::list> return_lists;
- return_lists.reserve(_return_list_count);
- for (decltype(_return_list_count) i = 0; i < _return_list_count; ++i)
- return_lists.emplace_back(list_len);
-
- static_cast<Derived*>(this)->march(return_lists);
-
- // Return to python objects.
- if (_return_list_count == 1) {
- assert(!_filled);
- if (_line_type == LineType::Separate)
- return return_lists[0];
- else {
- assert(_line_type == LineType::ChunkCombinedNan);
- return py::make_tuple(return_lists[0]);
- }
- }
- else if (_return_list_count == 2)
- return py::make_tuple(return_lists[0], return_lists[1]);
- else {
- assert(_return_list_count == 3);
- return py::make_tuple(return_lists[0], return_lists[1], return_lists[2]);
- }
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::move_to_next_boundary_edge(
- index_t& quad, index_t& forward, index_t& left) const
-{
- // edge == 0 for E edge (facing N), forward = +_nx
- // 2 for S edge (facing E), forward = +1
- // 4 for W edge (facing S), forward = -_nx
- // 6 for N edge (facing W), forward = -1
- // 1 for SE edge (NW corner) from SW facing NE, forward = +_nx+1
- // 3 for SW edge (NE corner) from NW facing SE, forward = -_nx+1
- // 5 for NW edge (SE corner) from NE facing SW, forward = -_nx-1
- // 7 for NE edge (SW corner) from SE facing NW, forward = +_nx-1
- int edge = 0;
-
- // Need index of quad that is the same as the end point, i.e. quad to SW of end point, as it is
- // this point which we need to find the next available boundary of, looking clockwise.
- if (forward > 0) {
- if (forward == _nx) {
- assert(left == -1);
- // W edge facing N, no change to quad or edge.
- }
- else if (left == _nx) {
- assert(forward == 1);
- quad -= _nx; // S edge facing E.
- edge = 2;
- }
- else if (EXISTS_SW_CORNER(quad)) {
- assert(forward == _nx-1 && left == -_nx-1);
- quad -= 1;
- edge = 7;
- }
- else {
- assert(EXISTS_NW_CORNER(quad) && forward == _nx+1 && _nx-1);
- // quad unchanged.
- edge = 1;
- }
- }
- else { // forward < 0
- if (forward == -_nx) {
- assert(left == 1);
- quad -= _nx+1; // W edge facing S.
- edge = 4;
- }
- else if (left == -_nx) {
- assert(forward == -1);
- quad -= 1; // N edge facing W.
- edge = 6;
- }
- else if (EXISTS_NE_CORNER(quad)) {
- assert(forward == -_nx+1 && left == _nx+1);
- quad -= _nx;
- edge = 3;
- }
- else {
- assert(EXISTS_SE_CORNER(quad) && forward == -_nx-1 && left == -_nx+1);
- quad -= _nx+1;
- edge = 5;
- }
- }
-
- // If _corner_mask not set, only need to consider odd edge in loop below.
- if (!_corner_mask)
- ++edge;
-
- while (true) {
- // Look at possible edges that leave NE point of quad.
- // If something is wrong here or in the setup of the boundary flags, can end up with an
- // infinite loop!
- switch (edge) {
- case 0:
- // Is there an edge to follow towards SW?
- if (EXISTS_SE_CORNER(quad)) { // Equivalent to BOUNDARY_NE.
- // quad unchanged.
- forward = -_nx-1;
- left = -_nx+1;
- return;
- }
- break;
- case 1:
- // Is there an edge to follow towards W?
- if (BOUNDARY_N(quad)) {
- // quad unchanged.
- forward = -1;
- left = -_nx;
- return;
- }
- break;
- case 2:
- // Is there an edge to follow towards NW?
- if (EXISTS_SW_CORNER(quad+_nx)) { // Equivalent to BOUNDARY_NE.
- quad += _nx;
- forward = _nx-1;
- left = -_nx-1;
- return;
- }
- break;
- case 3:
- // Is there an edge to follow towards N?
- if (BOUNDARY_E(quad+_nx)) { // Really a BOUNDARY_W check.
- quad += _nx;
- forward = _nx;
- left = -1;
- return;
- }
- break;
- case 4:
- // Is there an edge to follow towards NE?
- if (EXISTS_NW_CORNER(quad+_nx+1)) { // Equivalent to BOUNDARY_SE.
- quad += _nx+1;
- forward = _nx+1;
- left = _nx-1;
- return;
- }
- break;
- case 5:
- // Is there an edge to follow towards E?
- if (BOUNDARY_N(quad+1)) { // Really a BOUNDARY_S check
- quad += _nx+1;
- forward = 1;
- left = _nx;
- return;
- }
- break;
- case 6:
- // Is there an edge to follow towards SE?
- if (EXISTS_NE_CORNER(quad+1)) { // Equivalent to BOUNDARY_SW.
- quad += 1;
- forward = -_nx+1;
- left = _nx+1;
- return;
- }
- break;
- case 7:
- // Is there an edge to follow towards S?
- if (BOUNDARY_E(quad)) {
- quad += 1;
- forward = -_nx;
- left = 1;
- return;
- }
- break;
- default:
- assert(0 && "Invalid edge index");
- break;
- }
-
- edge = _corner_mask ? (edge + 1) % 8 : (edge + 2) % 8;
- }
-}
-
-template <typename Derived>
-py::list BaseContourGenerator<Derived>::multi_filled(const LevelArray levels)
-{
- check_levels(levels, true);
- pre_filled();
-
- auto levels_proxy = levels.unchecked<1>();
- auto n = levels_proxy.size();
-
- py::list ret(n-1);
- _lower_level = levels_proxy[0];
- for (decltype(n) i = 0; i < n-1; i++) {
- _upper_level = levels_proxy[i+1];
- ret[i] = march_wrapper();
- _lower_level = _upper_level;
- }
-
- return ret;
-}
-
-template <typename Derived>
-py::list BaseContourGenerator<Derived>::multi_lines(const LevelArray levels)
-{
- check_levels(levels, false);
- pre_lines();
-
- auto levels_proxy = levels.unchecked<1>();
- auto n = levels_proxy.size();
-
- py::list ret(n);
- for (decltype(n) i = 0; i < n; i++) {
- _lower_level = _upper_level = levels_proxy[i];
- ret[i] = march_wrapper();
- }
-
- return ret;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::pre_filled()
-{
- _filled = true;
-
- _identify_holes = !(_fill_type == FillType::ChunkCombinedCode ||
- _fill_type == FillType::ChunkCombinedOffset);
- _output_chunked = !(_fill_type == FillType::OuterCode || _fill_type == FillType::OuterOffset);
- _direct_points = _output_chunked;
- _direct_line_offsets = (_fill_type == FillType::ChunkCombinedOffset||
- _fill_type == FillType::ChunkCombinedOffsetOffset);
- _direct_outer_offsets = (_fill_type == FillType::ChunkCombinedCodeOffset ||
- _fill_type == FillType::ChunkCombinedOffsetOffset);
- _outer_offsets_into_points = (_fill_type == FillType::ChunkCombinedCodeOffset);
- _nan_separated = false;
- _return_list_count = (_fill_type == FillType::ChunkCombinedCodeOffset ||
- _fill_type == FillType::ChunkCombinedOffsetOffset) ? 3 : 2;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::pre_lines()
-{
- _filled = false;
-
- _identify_holes = false;
- _output_chunked = !(_line_type == LineType::Separate || _line_type == LineType::SeparateCode);
- _direct_points = _output_chunked;
- _direct_line_offsets = (_line_type == LineType::ChunkCombinedOffset);
- _direct_outer_offsets = false;
- _outer_offsets_into_points = false;
- _return_list_count = (_line_type == LineType::Separate ||
- _line_type == LineType::ChunkCombinedNan) ? 1 : 2;
- _nan_separated = (_line_type == LineType::ChunkCombinedNan);
-
- if (_nan_separated)
- Util::ensure_nan_loaded();
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::set_look_flags(index_t hole_start_quad)
-{
- assert(_identify_holes);
-
- // The only possible hole starts are START_E (from E to N), START_HOLE_N (on N boundary, E to W)
- // and START_CORNER for SW corner (on boundary, SE to NW).
- assert(hole_start_quad >= 0 && hole_start_quad < _n);
- assert(EXISTS_N_EDGE(hole_start_quad) || EXISTS_SW_CORNER(hole_start_quad));
- assert(!LOOK_S(hole_start_quad) && "Look S already set");
-
- _cache[hole_start_quad] |= MASK_LOOK_S;
-
- // Walk S until find place to mark corresponding look N.
- auto quad = hole_start_quad;
-
- while (true) {
- assert(quad >= 0 && quad < _n);
- assert(EXISTS_N_EDGE(quad) || (quad == hole_start_quad && EXISTS_SW_CORNER(quad)));
-
- if (BOUNDARY_S(quad) || EXISTS_NE_CORNER(quad) || EXISTS_NW_CORNER(quad) || Z_SE != 1) {
- assert(!LOOK_N(quad) && "Look N already set");
- _cache[quad] |= MASK_LOOK_N;
- break;
- }
-
- quad -= _nx;
- }
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::supports_fill_type(FillType fill_type)
-{
- switch (fill_type) {
- case FillType::OuterCode:
- case FillType::OuterOffset:
- case FillType::ChunkCombinedCode:
- case FillType::ChunkCombinedOffset:
- case FillType::ChunkCombinedCodeOffset:
- case FillType::ChunkCombinedOffsetOffset:
- return true;
- default:
- return false;
- }
-}
-
-template <typename Derived>
-bool BaseContourGenerator<Derived>::supports_line_type(LineType line_type)
-{
- switch (line_type) {
- case LineType::Separate:
- case LineType::SeparateCode:
- case LineType::ChunkCombinedCode:
- case LineType::ChunkCombinedOffset:
- case LineType::ChunkCombinedNan:
- return true;
- default:
- return false;
- }
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::write_cache() const
-{
- std::cout << "---------- Cache ----------" << std::endl;
- index_t ny = _n / _nx;
- for (index_t j = ny-1; j >= 0; --j) {
- std::cout << "j=" << j << " ";
- for (index_t i = 0; i < _nx; ++i) {
- index_t quad = i + j*_nx;
- write_cache_quad(quad);
- }
- std::cout << std::endl;
- }
- std::cout << " ";
- for (index_t i = 0; i < _nx; ++i)
- std::cout << "i=" << i << " ";
- std::cout << std::endl;
- std::cout << "---------------------------" << std::endl;
-}
-
-template <typename Derived>
-void BaseContourGenerator<Derived>::write_cache_quad(index_t quad) const
-{
- assert(quad >= 0 && quad < _n && "quad index out of bounds");
- std::cout << (NO_MORE_STARTS(quad) ? 'x' :
- (NO_STARTS_IN_ROW(quad) ? 'i' : '.'));
- std::cout << (EXISTS_QUAD(quad) ? "Q_" :
- (EXISTS_NW_CORNER(quad) ? "NW" :
- (EXISTS_NE_CORNER(quad) ? "NE" :
- (EXISTS_SW_CORNER(quad) ? "SW" :
- (EXISTS_SE_CORNER(quad) ? "SE" : "..")))));
- std::cout << (BOUNDARY_N(quad) && BOUNDARY_E(quad) ? 'b' : (
- BOUNDARY_N(quad) ? 'n' : (BOUNDARY_E(quad) ? 'e' : '.')));
- std::cout << Z_LEVEL(quad);
- std::cout << ((_cache[quad] & MASK_MIDDLE) >> 2);
- std::cout << (START_BOUNDARY_S(quad) ? 's' : '.');
- std::cout << (START_BOUNDARY_W(quad) ? 'w' : '.');
- if (!_filled) {
- std::cout << (START_BOUNDARY_E(quad) ? 'e' : '.');
- std::cout << (START_BOUNDARY_N(quad) ? 'n' : '.');
- }
- std::cout << (START_E(quad) ? 'E' : '.');
- std::cout << (START_N(quad) ? 'N' : '.');
- if (_filled)
- std::cout << (START_HOLE_N(quad) ? 'h' : '.');
- std::cout << (START_CORNER(quad) ? 'c' : '.');
- if (_filled)
- std::cout << (LOOK_N(quad) && LOOK_S(quad) ? 'B' :
- (LOOK_N(quad) ? '^' : (LOOK_S(quad) ? 'v' : '.')));
- std::cout << ' ';
-}
-
-template <typename Derived>
-typename BaseContourGenerator<Derived>::ZLevel BaseContourGenerator<Derived>::z_to_zlevel(
- double z_value) const
-{
- return (_filled && z_value > _upper_level) ? 2 : (z_value > _lower_level ? 1 : 0);
-}
-
-} // namespace contourpy
-
-#endif // CONTOURPY_BASE_IMPL_H
diff --git a/contrib/python/contourpy/src/chunk_local.cpp b/contrib/python/contourpy/src/chunk_local.cpp
deleted file mode 100644
index 1c8833ab0b5..00000000000
--- a/contrib/python/contourpy/src/chunk_local.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#include "chunk_local.h"
-#include <iostream>
-#include <limits>
-
-namespace contourpy {
-
-ChunkLocal::ChunkLocal()
-{
- look_up_quads.reserve(100);
- clear();
-}
-
-void ChunkLocal::clear()
-{
- chunk = -1;
- istart = iend = jstart = jend = -1;
- pass = -1;
-
- total_point_count = 0;
- line_count = 0;
- hole_count = 0;
-
- points.clear();
- line_offsets.clear();
- outer_offsets.clear();
-
- look_up_quads.clear();
-}
-
-std::ostream &operator<<(std::ostream &os, const ChunkLocal& local)
-{
- os << "ChunkLocal:"
- << " chunk=" << local.chunk
- << " istart=" << local.istart
- << " iend=" << local.iend
- << " jstart=" << local.jstart
- << " jend=" << local.jend
- << " total_point_count=" << local.total_point_count
- << " line_count=" << local.line_count
- << " hole_count=" << local.hole_count;
-
- if (local.line_offsets.start != nullptr) {
- os << " line_offsets=";
- for (count_t i = 0; i < local.line_count + 1; ++i) {
- os << local.line_offsets.start[i] << " ";
- }
- }
-
- if (local.outer_offsets.start != nullptr) {
- os << " outer_offsets=";
- for (count_t i = 0; i < local.line_count - local.hole_count + 1; ++i) {
- os << local.outer_offsets.start[i] << " ";
- }
- }
-
- return os;
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/chunk_local.h b/contrib/python/contourpy/src/chunk_local.h
deleted file mode 100644
index 0298ea4814b..00000000000
--- a/contrib/python/contourpy/src/chunk_local.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef CONTOURPY_CHUNK_LOCAL_H
-#define CONTOURPY_CHUNK_LOCAL_H
-
-#include "output_array.h"
-#include <iosfwd>
-
-namespace contourpy {
-
-struct ChunkLocal
-{
- ChunkLocal();
-
- void clear();
-
- friend std::ostream &operator<<(std::ostream &os, const ChunkLocal& local);
-
-
-
- index_t chunk; // Index in range 0 to _n_chunks-1.
- index_t istart, iend, jstart, jend; // Chunk limits, inclusive.
- int pass;
-
- // Data for whole pass.
- count_t total_point_count; // Includes nan separators if used.
- count_t line_count; // Count of all lines
- count_t hole_count; // Count of holes only.
-
- // Output arrays that are initialised at the end of pass 0 and written to during pass 1.
- OutputArray<double> points;
- OutputArray<offset_t> line_offsets; // Into array of points.
- OutputArray<offset_t> outer_offsets; // Into array of points or line offsets depending on
- // fill_type.
-
- // Data for current outer.
- std::vector<index_t> look_up_quads; // To find holes of current outer.
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_CHUNK_LOCAL_H
diff --git a/contrib/python/contourpy/src/common.h b/contrib/python/contourpy/src/common.h
deleted file mode 100644
index 68c522d1b6e..00000000000
--- a/contrib/python/contourpy/src/common.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef CONTOURPY_COMMON_H
-#define CONTOURPY_COMMON_H
-
-#include <pybind11/pybind11.h>
-#include <pybind11/numpy.h>
-
-namespace contourpy {
-
-namespace py = pybind11;
-
-// quad/point index type, the same as for numpy array shape/indices (== npy_intp). Also used for
-// chunks.
-typedef py::ssize_t index_t;
-
-// Count of points, lines and holes.
-typedef py::size_t count_t;
-
-// Offsets into point arrays.
-typedef uint32_t offset_t;
-
-// Input numpy array classes.
-typedef py::array_t<double, py::array::c_style | py::array::forcecast> CoordinateArray;
-typedef py::array_t<bool, py::array::c_style | py::array::forcecast> MaskArray;
-typedef py::array_t<double> LevelArray; // Doesn't have to be contiguous.
-
-// Output numpy array classes.
-typedef py::array_t<double> PointArray;
-typedef py::array_t<uint8_t> CodeArray;
-typedef py::array_t<offset_t> OffsetArray;
-
-} // namespace contourpy
-
-#endif // CONTOURPY_COMMON_H
diff --git a/contrib/python/contourpy/src/contour_generator.cpp b/contrib/python/contourpy/src/contour_generator.cpp
deleted file mode 100644
index 5ede6fa46ee..00000000000
--- a/contrib/python/contourpy/src/contour_generator.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-#include "contour_generator.h"
-#include "util.h"
-
-namespace contourpy {
-
-void ContourGenerator::check_levels(const LevelArray& levels, bool filled) const
-{
- if (levels.ndim() != 1) {
- throw std::domain_error(
- "Levels array must be 1D not " + std::to_string(levels.ndim()) + "D");
- }
-
- if (filled) {
- auto n = levels.size();
- if (n < 2) {
- throw std::invalid_argument(
- "Levels array must have at least 2 elements, not " + std::to_string(n));
- }
-
- auto levels_proxy = levels.unchecked<1>();
-
- // Check levels are not NaN.
- for (decltype(n) i = 0; i < n; i++) {
- if (Util::is_nan(levels_proxy[i]))
- throw std::invalid_argument("Levels must not contain any NaN");
- }
-
- // Check levels are increasing.
- auto lower_level = levels_proxy[0];
- for (decltype(n) i = 0; i < n-1; i++) {
- auto upper_level = levels_proxy[i+1];
- if (lower_level >= upper_level)
- throw std::invalid_argument("Levels must be increasing");
- lower_level = upper_level;
- }
- }
-}
-
-void ContourGenerator::check_levels(double lower_level, double upper_level) const
-{
- if (Util::is_nan(lower_level) || Util::is_nan(upper_level))
- throw std::invalid_argument("lower_level and upper_level cannot be NaN");
- if (lower_level >= upper_level)
- throw std::invalid_argument("upper_level must be larger than lower_level");
-}
-
-py::list ContourGenerator::multi_filled(const LevelArray levels)
-{
- check_levels(levels, true);
-
- auto levels_proxy = levels.unchecked<1>();
- auto n = levels_proxy.size();
-
- py::list ret(n-1);
- auto lower_level = levels_proxy[0];
- for (decltype(n) i = 0; i < n-1; i++) {
- auto upper_level = levels_proxy[i+1];
- ret[i] = filled(lower_level, upper_level);
- lower_level = upper_level;
- }
-
- return ret;
-}
-
-py::list ContourGenerator::multi_lines(const LevelArray levels)
-{
- check_levels(levels, false);
-
- auto levels_proxy = levels.unchecked<1>();
- auto n = levels_proxy.size();
-
- py::list ret(n);
- for (decltype(n) i = 0; i < n; i++)
- ret[i] = lines(levels_proxy[i]);
-
- return ret;
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/contour_generator.h b/contrib/python/contourpy/src/contour_generator.h
deleted file mode 100644
index c4375800239..00000000000
--- a/contrib/python/contourpy/src/contour_generator.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef CONTOURPY_CONTOUR_GENERATOR_H
-#define CONTOURPY_CONTOUR_GENERATOR_H
-
-#include "common.h"
-
-namespace contourpy {
-
-class ContourGenerator
-{
-public:
- // Non-copyable and non-moveable.
- ContourGenerator(const ContourGenerator& other) = delete;
- ContourGenerator(const ContourGenerator&& other) = delete;
- ContourGenerator& operator=(const ContourGenerator& other) = delete;
- ContourGenerator& operator=(const ContourGenerator&& other) = delete;
-
- virtual ~ContourGenerator() = default;
-
- virtual py::tuple filled(double lower_level, double upper_level) = 0;
-
- virtual py::sequence lines(double level) = 0;
-
- virtual py::list multi_filled(const LevelArray levels);
- virtual py::list multi_lines(const LevelArray levels);
-
-protected:
- ContourGenerator() = default;
-
- // Check levels are acceptable, throw exception if not.
- void check_levels(const LevelArray& levels, bool filled) const;
- void check_levels(double lower_level, double upper_level) const;
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_CONTOUR_GENERATOR_H
diff --git a/contrib/python/contourpy/src/converter.cpp b/contrib/python/contourpy/src/converter.cpp
deleted file mode 100644
index 6a800e2ec86..00000000000
--- a/contrib/python/contourpy/src/converter.cpp
+++ /dev/null
@@ -1,155 +0,0 @@
-#include "converter.h"
-#include "mpl_kind_code.h"
-#include <limits>
-
-namespace contourpy {
-
-void Converter::check_max_offset(count_t max_offset)
-{
- if (max_offset > std::numeric_limits<OffsetArray::value_type>::max())
- throw std::range_error("Max offset too large to fit in np.uint32. Use smaller chunks.");
-}
-
-CodeArray Converter::convert_codes(
- count_t point_count, count_t cut_count, const offset_t* cut_start, offset_t subtract)
-{
- assert(point_count > 0 && cut_count > 0);
- assert(cut_start != nullptr);
-
- index_t codes_shape = static_cast<index_t>(point_count);
- CodeArray py_codes(codes_shape);
- convert_codes(point_count, cut_count, cut_start, subtract, py_codes.mutable_data());
- return py_codes;
-}
-
-void Converter::convert_codes(
- count_t point_count, count_t cut_count, const offset_t* cut_start, offset_t subtract,
- CodeArray::value_type* codes)
-{
- assert(point_count > 0 && cut_count > 0);
- assert(cut_start != nullptr);
- assert(codes != nullptr);
-
- std::fill(codes + 1, codes + point_count - 1, LINETO);
- for (decltype(cut_count) i = 0; i < cut_count-1; ++i) {
- codes[cut_start[i] - subtract] = MOVETO;
- codes[cut_start[i+1] - 1 - subtract] = CLOSEPOLY;
- }
-}
-
-CodeArray Converter::convert_codes_check_closed(
- count_t point_count, count_t cut_count, const offset_t* cut_start, const double* points)
-{
- assert(point_count > 0 && cut_count > 0);
- assert(cut_start != nullptr);
- assert(points != nullptr);
-
- index_t codes_shape = static_cast<index_t>(point_count);
- CodeArray codes(codes_shape);
- convert_codes_check_closed(point_count, cut_count, cut_start, points, codes.mutable_data());
- return codes;
-}
-
-void Converter::convert_codes_check_closed(
- count_t point_count, count_t cut_count, const offset_t* cut_start, const double* points,
- CodeArray::value_type* codes)
-{
- assert(point_count > 0 && cut_count > 0);
- assert(cut_start != nullptr);
- assert(points != nullptr);
- assert(codes != nullptr);
-
- std::fill(codes + 1, codes + point_count, LINETO);
- for (decltype(cut_count) i = 0; i < cut_count-1; ++i) {
- auto start = cut_start[i];
- auto end = cut_start[i+1];
- codes[start] = MOVETO;
- bool closed = points[2*start] == points[2*end-2] &&
- points[2*start+1] == points[2*end-1];
- if (closed)
- codes[end-1] = CLOSEPOLY;
- }
-}
-
-CodeArray Converter::convert_codes_check_closed_single(
- count_t point_count, const double* points)
-{
- assert(point_count > 0);
- assert(points != nullptr);
-
- index_t codes_shape = static_cast<index_t>(point_count);
- CodeArray py_codes(codes_shape);
- convert_codes_check_closed_single(point_count, points, py_codes.mutable_data());
- return py_codes;
-}
-
-void Converter::convert_codes_check_closed_single(
- count_t point_count, const double* points, CodeArray::value_type* codes)
-{
- assert(point_count > 0);
- assert(points != nullptr);
- assert(codes != nullptr);
-
- codes[0] = MOVETO;
- auto start = points;
- auto end = points + 2*point_count;
- bool closed = *start == *(end-2) && *(start+1) == *(end-1);
- if (closed) {
- std::fill(codes + 1, codes + point_count - 1, LINETO);
- codes[point_count-1] = CLOSEPOLY;
- }
- else
- std::fill(codes + 1, codes + point_count, LINETO);
-}
-
-OffsetArray Converter::convert_offsets(
- count_t offset_count, const offset_t* start, offset_t subtract)
-{
- assert(offset_count > 0);
- assert(start != nullptr);
-
- index_t offsets_shape = static_cast<index_t>(offset_count);
- OffsetArray py_offsets(offsets_shape);
- convert_offsets(offset_count, start, subtract, py_offsets.mutable_data());
- return py_offsets;
-}
-
-void Converter::convert_offsets(
- count_t offset_count, const offset_t* start, offset_t subtract,
- OffsetArray::value_type* offsets)
-{
- assert(offset_count > 0);
- assert(start != nullptr);
- assert(offsets != nullptr);
-
- check_max_offset(*(start + offset_count - 1) - subtract);
-
- if (subtract == 0)
- std::copy(start, start + offset_count, offsets);
- else {
- for (decltype(offset_count) i = 0; i < offset_count; ++i)
- *offsets++ = start[i] - subtract;
- }
-}
-
-PointArray Converter::convert_points(count_t point_count, const double* start)
-{
- assert(point_count > 0);
- assert(start != nullptr);
-
- index_t points_shape[2] = {static_cast<index_t>(point_count), 2};
- PointArray py_points(points_shape);
- convert_points(point_count, start, py_points.mutable_data());
- return py_points;
-}
-
-void Converter::convert_points(count_t point_count, const double* start, double* points)
-{
- assert(point_count > 0);
- assert(start != nullptr);
- assert(points != nullptr);
-
- std::copy(start, start + 2*point_count, points);
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/converter.h b/contrib/python/contourpy/src/converter.h
deleted file mode 100644
index 5bf9ab5119d..00000000000
--- a/contrib/python/contourpy/src/converter.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef CONTOURPY_CONVERTER_H
-#define CONTOURPY_CONVERTER_H
-
-#include "common.h"
-
-namespace contourpy {
-
-// Conversion of C++ point/code/offset array to return to Python as a NumPy array.
-// There are two versions of each function, the first creates, populates and returns a NumPy array
-// whereas the second populates one that has already been created. The former are used in serial
-// code and the latter in threaded code where the creation and manipulation of NumPy arrays needs to
-// be threadlocked whereas the population of those arrays does not.
-class Converter
-{
-public:
- // Create and populate codes array,
- static CodeArray convert_codes(
- count_t point_count, count_t cut_count, const offset_t* cut_start, offset_t subtract);
-
- // Populate codes array that has already been created.
- static void convert_codes(
- count_t point_count, count_t cut_count, const offset_t* cut_start, offset_t subtract,
- CodeArray::value_type* codes);
-
- // Create and populate codes array,
- static CodeArray convert_codes_check_closed(
- count_t point_count, count_t cut_count, const offset_t* cut_start, const double* points);
-
- // Populate codes array that has already been created.
- static void convert_codes_check_closed(
- count_t point_count, count_t cut_count, const offset_t* cut_start, const double* points,
- CodeArray::value_type* codes);
-
- // Create and populate codes array (single line loop/strip).
- static CodeArray convert_codes_check_closed_single(
- count_t point_count, const double* points);
-
- // Populate codes array that has already been created (single line loop/strip).
- static void convert_codes_check_closed_single(
- count_t point_count, const double* points, CodeArray::value_type* codes);
-
- // Create and populate offsets array,
- static OffsetArray convert_offsets(
- count_t offset_count, const offset_t* start, offset_t subtract);
-
- // Populate offsets array that has already been created.
- static void convert_offsets(
- count_t offset_count, const offset_t* start, offset_t subtract,
- OffsetArray::value_type* offsets);
-
- // Create and populate points array,
- static PointArray convert_points(count_t point_count, const double* start);
-
- // Populate points array that has already been created.
- static void convert_points(count_t point_count, const double* start, double* points);
-
-private:
- static void check_max_offset(count_t max_offset);
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_CONVERTER_H
diff --git a/contrib/python/contourpy/src/fill_type.cpp b/contrib/python/contourpy/src/fill_type.cpp
deleted file mode 100644
index 79129e29092..00000000000
--- a/contrib/python/contourpy/src/fill_type.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#include "fill_type.h"
-#include <iostream>
-
-namespace contourpy {
-
-std::ostream &operator<<(std::ostream &os, const FillType& fill_type)
-{
- switch (fill_type) {
- case FillType::OuterCode:
- os << "OuterCode";
- break;
- case FillType::OuterOffset:
- os << "OuterOffset";
- break;
- case FillType::ChunkCombinedCode:
- os << "ChunkCombinedCode";
- break;
- case FillType::ChunkCombinedOffset:
- os << "ChunkCombinedOffset";
- break;
- case FillType::ChunkCombinedCodeOffset:
- os << "ChunkCombinedCodeOffset";
- break;
- case FillType::ChunkCombinedOffsetOffset:
- os << "ChunkCombinedOffsetOffset";
- break;
- }
- return os;
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/fill_type.h b/contrib/python/contourpy/src/fill_type.h
deleted file mode 100644
index 20d41aeb3b2..00000000000
--- a/contrib/python/contourpy/src/fill_type.h
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef CONTOURPY_FILL_TYPE_H
-#define CONTOURPY_FILL_TYPE_H
-
-#include <iosfwd>
-#include <string>
-
-namespace contourpy {
-
-// C++11 scoped enum, must be fully qualified to use.
-enum class FillType
-{
- OuterCode = 201,
- OuterOffset = 202,
- ChunkCombinedCode = 203,
- ChunkCombinedOffset = 204,
- ChunkCombinedCodeOffset = 205,
- ChunkCombinedOffsetOffset = 206,
-};
-
-std::ostream &operator<<(std::ostream &os, const FillType& fill_type);
-
-} // namespace contourpy
-
-#endif // CONTOURPY_FILL_TYPE_H
diff --git a/contrib/python/contourpy/src/line_type.cpp b/contrib/python/contourpy/src/line_type.cpp
deleted file mode 100644
index fbd97ea78ce..00000000000
--- a/contrib/python/contourpy/src/line_type.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "line_type.h"
-#include <iostream>
-
-namespace contourpy {
-
-std::ostream &operator<<(std::ostream &os, const LineType& line_type)
-{
- switch (line_type) {
- case LineType::Separate:
- os << "Separate";
- break;
- case LineType::SeparateCode:
- os << "SeparateCode";
- break;
- case LineType::ChunkCombinedCode:
- os << "ChunkCombinedCode";
- break;
- case LineType::ChunkCombinedOffset:
- os << "ChunkCombinedOffset";
- break;
- case LineType::ChunkCombinedNan:
- os << "ChunkCombinedNan";
- break;
- }
- return os;
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/line_type.h b/contrib/python/contourpy/src/line_type.h
deleted file mode 100644
index c1e5dda702e..00000000000
--- a/contrib/python/contourpy/src/line_type.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef CONTOURPY_LINE_TYPE_H
-#define CONTOURPY_LINE_TYPE_H
-
-#include <iosfwd>
-#include <string>
-
-namespace contourpy {
-
-// C++11 scoped enum, must be fully qualified to use.
-enum class LineType
-{
- Separate = 101,
- SeparateCode = 102,
- ChunkCombinedCode = 103,
- ChunkCombinedOffset = 104,
- ChunkCombinedNan = 105,
-};
-
-std::ostream &operator<<(std::ostream &os, const LineType& line_type);
-
-} // namespace contourpy
-
-#endif // CONTOURPY_LINE_TYPE_H
diff --git a/contrib/python/contourpy/src/mpl2005.cpp b/contrib/python/contourpy/src/mpl2005.cpp
deleted file mode 100644
index d94432a44f0..00000000000
--- a/contrib/python/contourpy/src/mpl2005.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-#include "mpl2005.h"
-
-namespace contourpy {
-
-Mpl2005ContourGenerator::Mpl2005ContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, index_t x_chunk_size, index_t y_chunk_size)
- : _x(x),
- _y(y),
- _z(z),
- _site(cntr_new())
-{
- if (_x.ndim() != 2 || _y.ndim() != 2 || _z.ndim() != 2)
- throw std::invalid_argument("x, y and z must all be 2D arrays");
-
- auto nx = _z.shape(1);
- auto ny = _z.shape(0);
-
- if (_x.shape(1) != nx || _x.shape(0) != ny ||
- _y.shape(1) != nx || _y.shape(0) != ny)
- throw std::invalid_argument("x, y and z arrays must have the same shape");
-
- if (nx < 2 || ny < 2)
- throw std::invalid_argument("x, y and z must all be at least 2x2 arrays");
-
- if (mask.ndim() != 0) { // ndim == 0 if mask is not set, which is valid.
- if (mask.ndim() != 2)
- throw std::invalid_argument("mask array must be a 2D array");
-
- if (mask.shape(1) != nx || mask.shape(0) != ny)
- throw std::invalid_argument(
- "If mask is set it must be a 2D array with the same shape as z");
- }
-
- if (x_chunk_size < 0 || y_chunk_size < 0)
- throw std::invalid_argument("x_chunk_size and y_chunk_size cannot be negative");
-
- const bool* mask_data = (mask.ndim() > 0 ? mask.data() : nullptr);
-
- cntr_init(
- _site, nx, ny, _x.data(), _y.data(), _z.data(), mask_data, x_chunk_size, y_chunk_size);
-}
-
-Mpl2005ContourGenerator::~Mpl2005ContourGenerator()
-{
- cntr_del(_site);
-}
-
-py::tuple Mpl2005ContourGenerator::filled(double lower_level, double upper_level)
-{
- check_levels(lower_level, upper_level);
- double levels[2] = {lower_level, upper_level};
- return cntr_trace(_site, levels, 2);
-}
-
-py::tuple Mpl2005ContourGenerator::get_chunk_count() const
-{
- long nx_chunks = (long)(ceil((_site->imax-1.0) / _site->i_chunk_size));
- long ny_chunks = (long)(ceil((_site->jmax-1.0) / _site->j_chunk_size));
- return py::make_tuple(ny_chunks, nx_chunks);
-}
-
-py::tuple Mpl2005ContourGenerator::get_chunk_size() const
-{
- return py::make_tuple(_site->j_chunk_size, _site->i_chunk_size);
-}
-
-py::sequence Mpl2005ContourGenerator::lines(double level)
-{
- double levels[2] = {level, 0.0};
- return cntr_trace(_site, levels, 1);
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/mpl2005.h b/contrib/python/contourpy/src/mpl2005.h
deleted file mode 100644
index 5801f5d17b3..00000000000
--- a/contrib/python/contourpy/src/mpl2005.h
+++ /dev/null
@@ -1,32 +0,0 @@
-#ifndef CONTOURPY_MPL_2005_H
-#define CONTOURPY_MPL_2005_H
-
-#include "contour_generator.h"
-#include "mpl2005_original.h"
-
-namespace contourpy {
-
-class Mpl2005ContourGenerator : public ContourGenerator
-{
-public:
- Mpl2005ContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, index_t x_chunk_size, index_t y_chunk_size);
-
- virtual ~Mpl2005ContourGenerator();
-
- py::tuple filled(double lower_level, double upper_level) override;
-
- py::tuple get_chunk_count() const; // Return (y_chunk_count, x_chunk_count)
- py::tuple get_chunk_size() const; // Return (y_chunk_size, x_chunk_size)
-
- py::sequence lines(double level) override;
-
-private:
- CoordinateArray _x, _y, _z;
- Csite *_site;
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_MPL_2005_H
diff --git a/contrib/python/contourpy/src/mpl2005_original.cpp b/contrib/python/contourpy/src/mpl2005_original.cpp
deleted file mode 100644
index f2f0e8567c3..00000000000
--- a/contrib/python/contourpy/src/mpl2005_original.cpp
+++ /dev/null
@@ -1,1526 +0,0 @@
-/*
- cntr.c
- General purpose contour tracer for quadrilateral meshes.
- Handles single level contours, or region between a pair of levels.
-
- The routines that do all the work, as well as the explanatory
- comments, came from gcntr.c, part of the GIST package. The
- original mpl interface was also based on GIST. The present
- interface uses parts of the original, but places them in
- the entirely different framework of a Python type. It
- was written by following the Python "Extending and Embedding"
- tutorial.
- */
-
-#include "mpl2005_original.h"
-#include "mpl_kind_code.h"
-
-/* Note that all arrays in these routines are Fortran-style,
- in the sense that the "i" index varies fastest; the dimensions
- of the corresponding C array are z[jmax][imax] in the notation
- used here. We can identify i and j with the x and y dimensions,
- respectively.
-
-*/
-
-/* What is a contour?
- *
- * Given a quadrilateral mesh (x,y), and values of a z at the points
- * of that mesh, we seek a set of polylines connecting points at a
- * particular value of z. Each point on such a contour curve lies
- * on an edge of the mesh, at a point linearly interpolated to the
- * contour level z0 between the given values of z at the endpoints
- * of the edge.
- *
- * Identifying these points is easy. Figuring out how to connect them
- * into a curve -- or possibly a set of disjoint curves -- is difficult.
- * Each disjoint curve may be either a closed circuit, or it may begin
- * and end on a mesh boundary.
- *
- * One of the problems with a quadrilateral mesh is that when the z
- * values at one pair of diagonally opposite points lie below z0, and
- * the values at the other diagonal pair of the same zone lie above z0,
- * all four edges of the zone are cut, and there is an ambiguity in
- * how we should connect the points. I call this a saddle zone.
- * The problem is that two disjoint curves cut through a saddle zone
- * (I reject the alternative of connecting the opposite points to make
- * a single self-intersecting curve, since those make ugly contour plots
- * -- I've tried it). The solution is to determine the z value of the
- * centre of the zone, which is the mean of the z values of the four
- * corner points. If the centre z is higher than the contour level of
- * interest and you are moving along the line with higher values on the
- * left, turn right to leave the saddle zone. If the centre z is lower
- * than the contour level turn left. Whether the centre z is higher
- * than the 1 or 2 contour levels is stored in the saddle array so that
- * it does not need to be recalculated in subsequent passes.
- *
- * Another complicating factor is that there may be logical holes in
- * the mesh -- zones which do not exist. We want our contours to stop
- * if they hit the edge of such a zone, just as if they'd hit the edge
- * of the whole mesh. The input region array addresses this issue.
- *
- * Yet another complication: We may want a list of closed polygons which
- * outline the region between two contour levels z0 and z1. These may
- * include sections of the mesh boundary (including edges of logical
- * holes defined by the region array), in addition to sections of the
- * contour curves at one or both levels. This introduces a huge
- * topological problem -- if one of the closed contours (possibly
- * including an interior logical hole in the mesh, but not any part of
- * the boundary of the whole mesh) encloses a region which is not
- * between z0 and z1, that curve must be connected by a slit (or "branch
- * cut") to the enclosing curve, so that the list of disjoint polygons
- * we return is each simply connected.
- *
- * Okay, one final stunning difficulty: For the two level case, no
- * individual polygon should have more than a few thousand sides, since
- * huge filled polygons place an inordinate load on rendering software,
- * which needs an amount of scratch space proportional to the number
- * of sides it needs to fill. So in the two level case, we want to
- * chunk the mesh into rectangular pieces of no more than, say, 30x30
- * zones, which keeps each returned polygon to less than a few thousand
- * sides (the worst case is very very bad -- you can easily write down
- * a function and two level values which produce a polygon that cuts
- * every edge of the mesh twice).
- */
-
-/*
- * Here is the numbering scheme for points, edges, and zones in
- * the mesh -- note that each ij corresponds to one point, one zone,
- * one i-edge (i=constant edge) and one j-edge (j=constant edge):
- *
- * (ij-1)-------(ij)-------(ij)
- * | |
- * | |
- * | |
- * (ij-1) (ij) (ij)
- * | |
- * | |
- * | |
- * (ij-iX-1)----(ij-iX)----(ij-iX)
- *
- * At each point, the function value is either 0, 1, or 2, depending
- * on whether it is below z0, between z0 and z1, or above z1.
- * Each zone either exists (1) or not (0).
- * From these three bits of data, all of the curve connectivity follows.
- *
- * The tracing algorithm is naturally edge-based: Either you are at a
- * point where a level cuts an edge, ready to step across a zone to
- * another edge, or you are drawing the edge itself, if it happens to
- * be a boundary with at least one section between z0 and z1.
- *
- * In either case, the edge is a directed edge -- either the zone
- * you are advancing into is to its left or right, or you are actually
- * drawing it. I always trace curves keeping the region between z0 and
- * z1 to the left of the curve. If I'm tracing a boundary, I'm always
- * moving CCW (counter clockwise) around the zone that exists. And if
- * I'm about to cross a zone, I'll make the direction of the edge I'm
- * sitting on be such that the zone I'm crossing is to its left.
- *
- * I start tracing each curve near its lower left corner (mesh oriented
- * as above), which is the first point I encounter scanning through the
- * mesh in order. When I figure the 012 z values and zonal existence,
- * I also mark the potential starting points: Each edge may harbor a
- * potential starting point corresponding to either direction, so there
- * are four start possibilities at each ij point. Only the following
- * possibilities need to be marked as potential starting edges:
- *
- * +-+-+-+
- * | | | |
- * A-0-C-+ One or both levels cut E and have z=1 above them, and
- * | EZ| | 0A is cut and either 0C is cut or CD is cut.
- * +-B-D-+ Or, one or both levels cut E and E is a boundary edge.
- * | | | | (and Z exists)
- * +-+-+-+
- *
- * +-+-+-+
- * | | | |
- * +-A-0-C One or both levels cut E and have z=1 below them, and
- * | |ZE | 0A is cut and either 0C is cut or CD is cut.
- * +-+-B-D Or, one or both levels cut E and E is a boundary edge.
- * | | | | (and Z exists)
- * +-+-+-+
- *
- * +-+-+-+
- * | | | |
- * +-+-+-+ E is a boundary edge, Z exists, at some point on E
- * | |Z| | lies between the levels.
- * +-+E+-+
- * | | | |
- * +-+-+-+
- *
- * +-+-+-+
- * | | | |
- * +-+E+-+ E is a boundary edge, Z exists, at some point on E
- * | |Z| | lies between the levels.
- * +-+-+-+
- * | | | |
- * +-+-+-+
- *
- * During the first tracing pass, the start mark is erased whenever
- * any non-starting edge is encountered, reducing the number of points
- * that need to be considered for the second pass. The first pass
- * makes the basic connectivity decisions. It figures out how many
- * disjoint curves there will be, and identifies slits for the two level
- * case or open contours for the single level case, and removes all but
- * the actual start markers. A second tracing pass can perform the
- * actual final trace.
- */
-
-/* ------------------------------------------------------------------------ */
-
-namespace contourpy {
-
-void print_Csite(Csite *Csite)
-{
- Cdata *data = Csite->data;
- int i, j, ij;
- int nd = Csite->imax * (Csite->jmax + 1) + 1;
- printf("zlevels: %8.2lg %8.2lg\n", Csite->zlevel[0], Csite->zlevel[1]);
- printf("edge %ld, left %ld, n %ld, count %ld, edge0 %ld, left0 %ld\n",
- Csite->edge, Csite->left, Csite->n, Csite->count,
- Csite->edge0, Csite->left0);
- printf(" level0 %d, edge00 %ld\n", Csite->level0, Csite->edge00);
- printf("%04x\n", data[nd-1]);
- for (j = Csite->jmax; j >= 0; j--)
- {
- for (i=0; i < Csite->imax; i++)
- {
- ij = i + j * Csite->imax;
- printf("%04x ", data[ij]);
- }
- printf("\n");
- }
- printf("\n");
-}
-
-
-/* the Cdata array consists of the following bits:
- * Z_VALUE (2 bits) 0, 1, or 2 function value at point
- * ZONE_EX 1 zone exists, 0 zone doesn't exist
- * I_BNDY this i-edge (i=constant edge) is a mesh boundary
- * J_BNDY this j-edge (i=constant edge) is a mesh boundary
- * I0_START this i-edge is a start point into zone to left
- * I1_START this i-edge is a start point into zone to right
- * J0_START this j-edge is a start point into zone below
- * J1_START this j-edge is a start point into zone above
- * START_ROW next start point is in current row (accelerates 2nd pass)
- * SLIT_UP marks this i-edge as the beginning of a slit upstroke
- * SLIT_DN marks this i-edge as the beginning of a slit downstroke
- * OPEN_END marks an i-edge start point whose other endpoint is
- * on a boundary for the single level case
- * ALL_DONE marks final start point
- * SLIT_DN_VISITED this slit downstroke hasn't/has been visited in pass 2
- */
-#define Z_VALUE 0x0003
-#define ZONE_EX 0x0004
-#define I_BNDY 0x0008
-#define J_BNDY 0x0010
-#define I0_START 0x0020
-#define I1_START 0x0040
-#define J0_START 0x0080
-#define J1_START 0x0100
-#define START_ROW 0x0200
-#define SLIT_UP 0x0400
-#define SLIT_DN 0x0800
-#define OPEN_END 0x1000
-#define ALL_DONE 0x2000
-#define SLIT_DN_VISITED 0x4000
-
-/* some helpful macros to find points relative to a given directed
- * edge -- points are designated 0, 1, 2, 3 CCW around zone with 0 and
- * 1 the endpoints of the current edge */
-#define FORWARD(left,ix) ((left)>0?((left)>1?1:-(ix)):((left)<-1?-1:(ix)))
-#define POINT0(edge,fwd) ((edge)-((fwd)>0?fwd:0))
-#define POINT1(edge,fwd) ((edge)+((fwd)<0?fwd:0))
-#define IS_JEDGE(edge,left) ((left)>0?((left)>1?1:0):((left)<-1?1:0))
-#define ANY_START (I0_START|I1_START|J0_START|J1_START)
-#define START_MARK(left) \
- ((left)>0?((left)>1?J1_START:I1_START):((left)<-1?J0_START:I0_START))
-
-enum {kind_zone, kind_edge1, kind_edge2,
- kind_slit_up, kind_slit_down, kind_start_slit=16};
-
-/* Saddle zone array consists of the following bits:
- * SADDLE_SET whether zone's saddle data has been set.
- * SADDLE_GT0 whether z of centre of zone is higher than site->level[0].
- * SADDLE_GT1 whether z of centre of zone is higher than site->level[1].
- */
-#define SADDLE_SET 0x01
-#define SADDLE_GT0 0x02
-#define SADDLE_GT1 0x04
-
-/* ------------------------------------------------------------------------ */
-
-/* these actually mark points */
-static int zone_crosser (Csite * site, int level, int pass2);
-static int edge_walker (Csite * site, int pass2);
-static int slit_cutter (Csite * site, int up, int pass2);
-
-/* this calls the first three to trace the next disjoint curve
- * -- return value is number of points on this curve, or
- * 0 if there are no more curves this pass
- * -(number of points) on first pass if:
- * this is two level case, and the curve closed on a hole
- * this is single level case, curve is open, and will start from
- * a different point on the second pass
- * -- in both cases, this curve will be combined with another
- * on the second pass */
-static long curve_tracer (Csite * site, int pass2);
-
-/* this initializes the data array for curve_tracer */
-static void data_init (Csite * site);
-
-/* ------------------------------------------------------------------------ */
-
-/* zone_crosser assumes you are sitting at a cut edge about to cross
- * the current zone. It always marks the initial point, crosses at
- * least one zone, and marks the final point. On non-boundary i-edges,
- * it is responsible for removing start markers on the first pass. */
-static int
-zone_crosser (Csite * site, int level, int pass2)
-{
- Cdata * data = site->data;
- long edge = site->edge;
- long left = site->left;
- long n = site->n;
- long fwd = FORWARD (left, site->imax);
- long p0, p1;
- int jedge = IS_JEDGE (edge, left);
- long edge0 = site->edge0;
- long left0 = site->left0;
- int level0 = site->level0 == level;
- int two_levels = site->zlevel[1] > site->zlevel[0];
- Saddle* saddle = site->saddle;
-
- const double *x = pass2 ? site->x : 0;
- const double *y = pass2 ? site->y : 0;
- const double *z = site->z;
- double zlevel = site->zlevel[level];
- double *xcp = pass2 ? site->xcp : 0;
- double *ycp = pass2 ? site->ycp : 0;
- short *kcp = pass2 ? site->kcp : 0;
-
- int z0, z1, z2, z3;
- int done = 0;
- int n_kind;
-
- if (level)
- level = 2;
-
- for (;;)
- {
- n_kind = 0;
- /* set edge endpoints */
- p0 = POINT0 (edge, fwd);
- p1 = POINT1 (edge, fwd);
-
- /* always mark cut on current edge */
- if (pass2)
- {
- /* second pass actually computes and stores the point */
- double zcp = (zlevel - z[p0]) / (z[p1] - z[p0]);
- xcp[n] = zcp * (x[p1] - x[p0]) + x[p0];
- ycp[n] = zcp * (y[p1] - y[p0]) + y[p0];
- kcp[n] = kind_zone;
- n_kind = n;
- }
- if (!done && !jedge)
- {
- if (n)
- {
- /* if this is not the first point on the curve, and we're
- * not done, and this is an i-edge, check several things */
- if (!two_levels && !pass2 && (data[edge] & OPEN_END))
- {
- /* reached an OPEN_END mark, skip the n++ */
- done = 4; /* same return value 4 used below */
- break;
- }
-
- /* check for curve closure -- if not, erase any start mark */
- if (edge == edge0 && left == left0)
- {
- /* may signal closure on a downstroke */
- if (level0)
- done = (!pass2 && two_levels && left < 0) ? 5 : 3;
- }
- else if (!pass2)
- {
- Cdata start =
- data[edge] & (fwd > 0 ? I0_START : I1_START);
- if (start)
- {
- data[edge] &= ~start;
- site->count--;
- }
- if (!two_levels)
- {
- start = data[edge] & (fwd > 0 ? I1_START : I0_START);
- if (start)
- {
- data[edge] &= ~start;
- site->count--;
- }
- }
- }
- }
- }
- n++;
- if (done)
- break;
-
- /* cross current zone to another cut edge */
- z0 = (data[p0] & Z_VALUE) != level; /* 1 if fill toward p0 */
- z1 = !z0; /* know level cuts edge */
- z2 = (data[p1 + left] & Z_VALUE) != level;
- z3 = (data[p0 + left] & Z_VALUE) != level;
- if (z0 == z2)
- {
- if (z1 == z3)
- {
- /* this is a saddle zone, determine whether to turn left or
- * right depending on height of centre of zone relative to
- * contour level. Set saddle[zone] if not already decided. */
- int turnRight;
- long zone = edge + (left > 0 ? left : 0);
- if (!(saddle[zone] & SADDLE_SET))
- {
- double zcentre;
- saddle[zone] = SADDLE_SET;
- zcentre = (z[p0] + z[p0+left] + z[p1] + z[p1+left])/4.0;
- if (zcentre > site->zlevel[0])
- saddle[zone] |=
- (two_levels && zcentre > site->zlevel[1])
- ? SADDLE_GT0 | SADDLE_GT1 : SADDLE_GT0;
- }
-
- turnRight = level == 2 ? (saddle[zone] & SADDLE_GT1)
- : (saddle[zone] & SADDLE_GT0);
- if (z1 ^ (level == 2))
- turnRight = !turnRight;
- if (!turnRight)
- goto bkwd;
- }
- /* bend forward (right along curve) */
- jedge = !jedge;
- edge = p1 + (left > 0 ? left : 0);
- {
- long tmp = fwd;
- fwd = -left;
- left = tmp;
- }
- }
- else if (z1 == z3)
- {
- bkwd:
- /* bend backward (left along curve) */
- jedge = !jedge;
- edge = p0 + (left > 0 ? left : 0);
- {
- long tmp = fwd;
- fwd = left;
- left = -tmp;
- }
- }
- else
- {
- /* straight across to opposite edge */
- edge += left;
- }
- /* after crossing zone, edge/left/fwd is oriented CCW relative to
- * the next zone, assuming we will step there */
-
- /* now that we've taken a step, check for the downstroke
- * of a slit on the second pass (upstroke checked above)
- * -- taking step first avoids a race condition */
- if (pass2 && two_levels && !jedge)
- {
- if (left > 0)
- {
- if (data[edge] & SLIT_UP)
- done = 6;
- }
- else
- {
- if (data[edge] & SLIT_DN)
- done = 5;
- }
- }
-
- if (!done)
- {
- /* finally, check if we are on a boundary */
- if (data[edge] & (jedge ? J_BNDY : I_BNDY))
- {
- done = two_levels ? 2 : 4;
- /* flip back into the zone that exists */
- left = -left;
- fwd = -fwd;
- if (!pass2 && (edge != edge0 || left != left0))
- {
- Cdata start = data[edge] & START_MARK (left);
- if (start)
- {
- data[edge] &= ~start;
- site->count--;
- }
- }
- }
- }
- }
-
- site->edge = edge;
- site->n = n;
- site->left = left;
- if (done <= 4)
- {
- return done;
- }
- if (pass2 && n_kind)
- {
- kcp[n_kind] += kind_start_slit;
- }
- return slit_cutter (site, done - 5, pass2);
-}
-
-/* edge_walker assumes that the current edge is being drawn CCW
- * around the current zone. Since only boundary edges are drawn
- * and we always walk around with the filled region to the left,
- * no edge is ever drawn CW. We attempt to advance to the next
- * edge on this boundary, but if current second endpoint is not
- * between the two contour levels, we exit back to zone_crosser.
- * Note that we may wind up marking no points.
- * -- edge_walker is never called for single level case */
-static int
-edge_walker (Csite * site, int pass2)
-{
- Cdata * data = site->data;
- long edge = site->edge;
- long left = site->left;
- long n = site->n;
- long fwd = FORWARD (left, site->imax);
- long p0 = POINT0 (edge, fwd);
- long p1 = POINT1 (edge, fwd);
- int jedge = IS_JEDGE (edge, left);
- long edge0 = site->edge0;
- long left0 = site->left0;
- int level0 = site->level0 == 2;
- int marked;
- int n_kind = 0;
-
- const double *x = pass2 ? site->x : 0;
- const double *y = pass2 ? site->y : 0;
- double *xcp = pass2 ? site->xcp : 0;
- double *ycp = pass2 ? site->ycp : 0;
- short *kcp = pass2 ? site->kcp : 0;
-
- int z0, z1, heads_up = 0;
-
- for (;;)
- {
- /* mark endpoint 0 only if value is 1 there, and this is a
- * two level task */
- z0 = data[p0] & Z_VALUE;
- z1 = data[p1] & Z_VALUE;
- marked = 0;
- n_kind = 0;
- if (z0 == 1)
- {
- /* mark current boundary point */
- if (pass2)
- {
- xcp[n] = x[p0];
- ycp[n] = y[p0];
- kcp[n] = kind_edge1;
- n_kind = n;
- }
- marked = 1;
- }
- else if (!n)
- {
- /* if this is the first point is not between the levels
- * must do the job of the zone_crosser and mark the first cut here,
- * so that it will be marked again by zone_crosser as it closes */
- if (pass2)
- {
- double zcp = site->zlevel[(z0 != 0)];
- zcp = (zcp - site->z[p0]) / (site->z[p1] - site->z[p0]);
- xcp[n] = zcp * (x[p1] - x[p0]) + x[p0];
- ycp[n] = zcp * (y[p1] - y[p0]) + y[p0];
- kcp[n] = kind_edge2;
- n_kind = n;
- }
- marked = 1;
- }
- if (n)
- {
- /* check for closure */
- if (level0 && edge == edge0 && left == left0)
- {
- site->edge = edge;
- site->left = left;
- site->n = n + marked;
- /* if the curve is closing on a hole, need to make a downslit */
- if (fwd < 0 && !(data[edge] & (jedge ? J_BNDY : I_BNDY)))
- {
- if (n_kind) kcp[n_kind] += kind_start_slit;
- return slit_cutter (site, 0, pass2);
- }
- if (fwd < 0 && level0 && left < 0)
- {
- /* remove J0_START from this boundary edge as boundary is
- * included by the upwards slit from contour line below. */
- data[edge] &= ~J0_START;
- if (n_kind) kcp[n_kind] += kind_start_slit;
- return slit_cutter (site, 0, pass2);
- }
- return 3;
- }
- else if (pass2)
- {
- if (heads_up || (fwd < 0 && (data[edge] & SLIT_DN)))
- {
- if (!heads_up && !(data[edge] & SLIT_DN_VISITED))
- data[edge] |= SLIT_DN_VISITED;
- else
- {
- site->edge = edge;
- site->left = left;
- site->n = n + marked;
- if (n_kind) kcp[n_kind] += kind_start_slit;
- return slit_cutter (site, heads_up, pass2);
- }
- }
- }
- else
- {
- /* if this is not first point, clear start mark for this edge */
- Cdata start = data[edge] & START_MARK (left);
- if (start)
- {
- data[edge] &= ~start;
- site->count--;
- }
- }
- }
- if (marked)
- n++;
-
- /* if next endpoint not between levels, need to exit to zone_crosser */
- if (z1 != 1)
- {
- site->edge = edge;
- site->left = left;
- site->n = n;
- return (z1 != 0); /* return level closest to p1 */
- }
-
- /* step to p1 and find next edge
- * -- turn left if possible, else straight, else right
- * -- check for upward slit beginning at same time */
- edge = p1 + (left > 0 ? left : 0);
- if (pass2 && jedge && fwd > 0 && (data[edge] & SLIT_UP))
- {
- jedge = !jedge;
- heads_up = 1;
- }
- else if (data[edge] & (jedge ? I_BNDY : J_BNDY))
- {
- long tmp = fwd;
- fwd = left;
- left = -tmp;
- jedge = !jedge;
- }
- else
- {
- edge = p1 + (fwd > 0 ? fwd : 0);
- if (pass2 && !jedge && fwd > 0 && (data[edge] & SLIT_UP))
- {
- heads_up = 1;
- }
- else if (!(data[edge] & (jedge ? J_BNDY : I_BNDY)))
- {
- edge = p1 - (left < 0 ? left : 0);
- jedge = !jedge;
- {
- long tmp = fwd;
- fwd = -left;
- left = tmp;
- }
- }
- }
- p0 = p1;
- p1 = POINT1 (edge, fwd);
- }
-}
-
-/* -- slit_cutter is never called for single level case */
-static int
-slit_cutter (Csite * site, int up, int pass2)
-{
- Cdata * data = site->data;
- long imax = site->imax;
- long n = site->n;
-
- const double *x = pass2 ? site->x : 0;
- const double *y = pass2 ? site->y : 0;
- double *xcp = pass2 ? site->xcp : 0;
- double *ycp = pass2 ? site->ycp : 0;
- short *kcp = pass2 ? site->kcp : 0;
-
- if (up && pass2)
- {
- /* upward stroke of slit proceeds up left side of slit until
- * it hits a boundary or a point not between the contour levels
- * -- this never happens on the first pass */
- long p1 = site->edge;
- int z1;
-
- for (;;)
- {
- z1 = data[p1] & Z_VALUE;
- if (z1 != 1)
- {
- site->edge = p1;
- site->left = -1;
- site->n = n;
- return (z1 != 0);
- }
- else if (data[p1] & J_BNDY)
- {
- /* this is very unusual case of closing on a mesh hole */
- site->edge = p1;
- site->left = -imax;
- site->n = n;
- return 2;
- }
- xcp[n] = x[p1];
- ycp[n] = y[p1];
- kcp[n] = kind_slit_up;
- n++;
- p1 += imax;
- }
-
- }
- else
- {
- /* downward stroke proceeds down right side of slit until it
- * hits a boundary or point not between the contour levels */
- long p0 = site->edge;
- int z0;
- /* at beginning of first pass, mark first i-edge with SLIT_DN */
- data[p0] |= SLIT_DN;
- p0 -= imax;
- for (;;)
- {
- z0 = data[p0] & Z_VALUE;
- if (!pass2)
- {
- if (z0 != 1 || (data[p0] & I_BNDY) || (data[p0 + 1] & J_BNDY))
- {
- /* at end of first pass, mark final i-edge with SLIT_UP */
- data[p0 + imax] |= SLIT_UP;
- /* one extra count for splicing at outer curve */
- site->n = n + 1;
- return 4; /* return same special value as for OPEN_END */
- }
- }
- else
- {
- if (z0 != 1)
- {
- site->edge = p0 + imax;
- site->left = 1;
- site->n = n;
- return (z0 != 0);
- }
- else if (data[p0 + 1] & J_BNDY)
- {
- site->edge = p0 + 1;
- site->left = imax;
- site->n = n;
- return 2;
- }
- else if (data[p0] & I_BNDY)
- {
- site->edge = p0;
- site->left = 1;
- site->n = n;
- return 2;
- }
- }
- if (pass2)
- {
- xcp[n] = x[p0];
- ycp[n] = y[p0];
- kcp[n] = kind_slit_down;
- n++;
- }
- else
- {
- /* on first pass need to count for upstroke as well */
- n += 2;
- }
- p0 -= imax;
- }
- }
-}
-
-/* ------------------------------------------------------------------------ */
-
-/* curve_tracer finds the next starting point, then traces the curve,
- * returning the number of points on this curve
- * -- in a two level trace, the return value is negative on the
- * first pass if the curve closed on a hole
- * -- in a single level trace, the return value is negative on the
- * first pass if the curve is an incomplete open curve
- * -- a return value of 0 indicates no more curves */
-static long
-curve_tracer (Csite * site, int pass2)
-{
- Cdata * data = site->data;
- long imax = site->imax;
- long edge0 = site->edge0;
- long left0 = site->left0;
- long edge00 = site->edge00;
- int two_levels = site->zlevel[1] > site->zlevel[0];
- int level, level0, mark_row;
- long n;
-
- /* it is possible for a single i-edge to serve as two actual start
- * points, one to the right and one to the left
- * -- for the two level case, this happens on the first pass for
- * a doubly cut edge, or on a chunking boundary
- * -- for single level case, this is impossible, but a similar
- * situation involving open curves is handled below
- * a second two start possibility is when the edge0 zone does not
- * exist and both the i-edge and j-edge boundaries are cut
- * yet another possibility is three start points at a junction
- * of chunk cuts
- * -- sigh, several other rare possibilities,
- * allow for general case, just go in order i1, i0, j1, j0 */
- int two_starts;
- /* printf("curve_tracer pass %d\n", pass2); */
- /* print_Csite(site); */
- if (left0 == 1)
- two_starts = data[edge0] & (I0_START | J1_START | J0_START);
- else if (left0 == -1)
- two_starts = data[edge0] & (J1_START | J0_START);
- else if (left0 == imax)
- two_starts = data[edge0] & J0_START;
- else
- two_starts = 0;
-
- if (pass2 || edge0 == 0)
- {
- /* zip up to row marked on first pass (or by data_init if edge0==0)
- * -- but not for double start case */
- if (!two_starts)
- {
- /* final start point marked by ALL_DONE marker */
- int first = (edge0 == 0 && !pass2);
- long e0 = edge0;
- if (data[edge0] & ALL_DONE)
- return 0;
- while (!(data[edge0] & START_ROW))
- edge0 += imax;
- if (e0 == edge0)
- edge0++; /* two starts handled specially */
- if (first)
- /* if this is the very first start point, we want to remove
- * the START_ROW marker placed by data_init */
- data[edge0 - edge0 % imax] &= ~START_ROW;
- }
-
- }
- else
- {
- /* first pass ends when all potential start points visited */
- if (site->count <= 0)
- {
- /* place ALL_DONE marker for second pass */
- data[edge00] |= ALL_DONE;
- /* reset initial site for second pass */
- site->edge0 = site->edge00 = site->left0 = 0;
- return 0;
- }
- if (!two_starts)
- edge0++;
- }
-
- if (two_starts)
- {
- /* trace second curve with this start immediately */
- if (left0 == 1 && (data[edge0] & I0_START))
- {
- left0 = -1;
- level = (data[edge0] & I_BNDY) ? 2 : 0;
- }
- else if ((left0 == 1 || left0 == -1) && (data[edge0] & J1_START))
- {
- left0 = imax;
- level = 2;
- }
- else
- {
- left0 = -imax;
- level = 2;
- }
-
- }
- else
- {
- /* usual case is to scan for next start marker
- * -- on second pass, this is at most one row of mesh, but first
- * pass hits nearly every point of the mesh, since it can't
- * know in advance which potential start marks removed */
- while (!(data[edge0] & ANY_START))
- edge0++;
-
- if (data[edge0] & I1_START)
- left0 = 1;
- else if (data[edge0] & I0_START)
- left0 = -1;
- else if (data[edge0] & J1_START)
- left0 = imax;
- else /*data[edge0]&J0_START */
- left0 = -imax;
-
- if (data[edge0] & (I1_START | I0_START))
- level = (data[edge0] & I_BNDY) ? 2 : 0;
- else
- level = 2;
- }
-
- /* this start marker will not be unmarked, but it has been visited */
- if (!pass2)
- site->count--;
-
- /* if this curve starts on a non-boundary i-edge, we need to
- * determine the level */
- if (!level && two_levels)
- level = left0 > 0 ?
- ((data[edge0 - imax] & Z_VALUE) !=
- 0) : ((data[edge0] & Z_VALUE) != 0);
-
- /* initialize site for this curve */
- site->edge = site->edge0 = edge0;
- site->left = site->left0 = left0;
- site->level0 = level0 = level; /* for open curve detection only */
-
- /* single level case just uses zone_crosser */
- if (!two_levels)
- level = 0;
-
- /* to generate the curve, alternate between zone_crosser and
- * edge_walker until closure or first call to edge_walker in
- * single level case */
- site->n = 0;
- for (;;)
- {
- if (level < 2)
- level = zone_crosser (site, level, pass2);
- else if (level < 3)
- level = edge_walker (site, pass2);
- else
- break;
- }
- n = site->n;
-
- /* single level case may have ended at a boundary rather than closing
- * -- need to recognize this case here in order to place the
- * OPEN_END mark for zone_crosser, remove this start marker,
- * and be sure not to make a START_ROW mark for this case
- * two level case may close with slit_cutter, in which case start
- * must also be removed and no START_ROW mark made
- * -- change sign of return n to inform caller */
- if (!pass2 && level > 3 && (two_levels || level0 == 0))
- {
- if (!two_levels)
- data[edge0] |= OPEN_END;
- data[edge0] &= ~(left0 > 0 ? I1_START : I0_START);
- mark_row = 0; /* do not mark START_ROW */
- n = -n;
- }
- else
- {
- if (two_levels)
- mark_row = !two_starts;
- else
- mark_row = 1;
- }
-
- /* on first pass, must apply START_ROW mark in column above previous
- * start marker
- * -- but skip if we just did second of two start case */
- if (!pass2 && mark_row)
- {
- data[edge0 - (edge0 - edge00) % imax] |= START_ROW;
- site->edge00 = edge0;
- }
-
- return n;
-}
-
-/* ------------------------------------------------------------------------ */
-
-static void
-data_init (Csite * site)
-{
- Cdata * data = site->data;
- long imax = site->imax;
- long jmax = site->jmax;
- long ijmax = imax * jmax;
- const double *z = site->z;
- double zlev0 = site->zlevel[0];
- double zlev1 = site->zlevel[1];
- int two_levels = zlev1 > zlev0;
- char *reg = site->reg;
- long count = 0;
- int started = 0;
- int ibndy, jbndy, i_was_chunk;
-
- long ichunk, jchunk, i, j, ij;
- long i_chunk_size = site->i_chunk_size;
- long j_chunk_size = site->j_chunk_size;
-
- if (!two_levels)
- {
- /* Chunking not used for lines as start points are not correct. */
- i_chunk_size = imax - 1;
- j_chunk_size = jmax - 1;
- }
-
- /* do everything in a single pass through the data array to
- * minimize cache faulting (z, reg, and data are potentially
- * very large arrays)
- * access to the z and reg arrays is strictly sequential,
- * but we need two rows (+-imax) of the data array at a time */
- if (z[0] > zlev0)
- data[0] = (two_levels && z[0] > zlev1) ? 2 : 1;
- else
- data[0] = 0;
- jchunk = 0;
- for (j = ij = 0; j < jmax; j++)
- {
- ichunk = i_was_chunk = 0;
- for (i = 0; i < imax; i++, ij++)
- {
- /* transfer zonal existence from reg to data array
- * -- get these for next row so we can figure existence of
- * points and j-edges for this row */
- data[ij + imax + 1] = 0;
- if (reg)
- {
- if (reg[ij + imax + 1] != 0)
- data[ij + imax + 1] = ZONE_EX;
- }
- else
- {
- if (i < imax - 1 && j < jmax - 1)
- data[ij + imax + 1] = ZONE_EX;
- }
-
- /* translate z values to 0, 1, 2 flags */
- if (ij < imax)
- data[ij + 1] = 0;
- if (ij < ijmax - 1 && z[ij + 1] > zlev0)
- data[ij + 1] |= (two_levels && z[ij + 1] > zlev1) ? 2 : 1;
-
- /* apply edge boundary marks */
- ibndy = i == ichunk
- || (data[ij] & ZONE_EX) != (data[ij + 1] & ZONE_EX);
- jbndy = j == jchunk
- || (data[ij] & ZONE_EX) != (data[ij + imax] & ZONE_EX);
- if (ibndy)
- data[ij] |= I_BNDY;
- if (jbndy)
- data[ij] |= J_BNDY;
-
- /* apply i-edge start marks
- * -- i-edges are only marked when actually cut
- * -- no mark is necessary if one of the j-edges which share
- * the lower endpoint is also cut
- * -- no I0 mark necessary unless filled region below some cut,
- * no I1 mark necessary unless filled region above some cut */
- if (j)
- {
- int v0 = (data[ij] & Z_VALUE);
- int vb = (data[ij - imax] & Z_VALUE);
- if (v0 != vb)
- { /* i-edge is cut */
- if (ibndy)
- {
- if (data[ij] & ZONE_EX)
- {
- data[ij] |= I0_START;
- count++;
- }
- if (data[ij + 1] & ZONE_EX)
- {
- data[ij] |= I1_START;
- count++;
- }
- }
- else
- {
- int va = (data[ij - 1] & Z_VALUE);
- int vc = (data[ij + 1] & Z_VALUE);
- int vd = (data[ij - imax + 1] & Z_VALUE);
- if (v0 != 1 && va != v0
- && (vc != v0 || vd != v0) && (data[ij] & ZONE_EX))
- {
- data[ij] |= I0_START;
- count++;
- }
- if (vb != 1 && va == vb
- && (vc == vb || vd == vb)
- && (data[ij + 1] & ZONE_EX))
- {
- data[ij] |= I1_START;
- count++;
- }
- }
- }
- }
-
- /* apply j-edge start marks
- * -- j-edges are only marked when they are boundaries
- * -- all cut boundary edges marked
- * -- for two level case, a few uncut edges must be marked
- */
- if (i && jbndy)
- {
- int v0 = (data[ij] & Z_VALUE);
- int vb = (data[ij - 1] & Z_VALUE);
- if (v0 != vb)
- {
- if (data[ij] & ZONE_EX)
- {
- data[ij] |= J0_START;
- count++;
- }
- if (data[ij + imax] & ZONE_EX)
- {
- data[ij] |= J1_START;
- count++;
- }
- }
- else if (two_levels && v0 == 1)
- {
- if (data[ij + imax] & ZONE_EX)
- {
- if (i_was_chunk || !(data[ij + imax - 1] & ZONE_EX))
- {
- /* lower left is a drawn part of boundary */
- data[ij] |= J1_START;
- count++;
- }
- }
- else if (data[ij] & ZONE_EX)
- {
- if (data[ij + imax - 1] & ZONE_EX)
- {
- /* weird case of open hole at lower left */
- data[ij] |= J0_START;
- count++;
- }
- }
- }
- }
-
- i_was_chunk = (i == ichunk);
- if (i_was_chunk)
- ichunk += i_chunk_size;
- }
-
- if (j == jchunk)
- jchunk += j_chunk_size;
-
- /* place first START_ROW marker */
- if (count && !started)
- {
- data[ij - imax] |= START_ROW;
- started = 1;
- }
- }
-
- /* place immediate stop mark if nothing found */
- if (!count)
- data[0] |= ALL_DONE;
- else
- for (i = 0; i < ijmax; ++i) site->saddle[i] = 0;
-
- /* initialize site */
- site->edge0 = site->edge00 = site->edge = 0;
- site->left0 = site->left = 0;
- site->n = 0;
- site->count = count;
-}
-
-/* ------------------------------------------------------------------------
- Original (slightly modified) core contour generation routines are above;
- below are new routines for interfacing to mpl.
-
- ------------------------------------------------------------------------ */
-
-/* Note: index order gets switched in the Python interface;
- python Z[i,j] -> C z[j,i]
- so if the array has shape Mi, Nj in python,
- we have iMax = Nj, jMax = Mi in gcntr.c.
- On the Python side: Ny, Nx = shape(z),
- so in C, the x-dimension is the first index, the y-dimension
- the second.
-*/
-
-/* reg should have the same dimensions as data, which
- has an extra iMax + 1 points relative to Z.
- It differs from mask in being the opposite (True
- where a region exists, versus the mask, which is True
- where a data point is bad), and in that it marks
- zones, not points. All four zones sharing a bad
- point must be marked as not existing.
-*/
-static void
-mask_zones (long iMax, long jMax, const bool *mask, char *reg)
-{
- long i, j, ij;
- long nreg = iMax * jMax + iMax + 1;
-
- for (ij = iMax+1; ij < iMax*jMax; ij++)
- {
- reg[ij] = 1;
- }
-
- ij = 0;
- for (j = 0; j < jMax; j++)
- {
- for (i = 0; i < iMax; i++, ij++)
- {
- if (i == 0 || j == 0) reg[ij] = 0;
- if (mask[ij])
- {
- reg[ij] = 0;
- reg[ij + 1] = 0;
- reg[ij + iMax] = 0;
- reg[ij + iMax + 1] = 0;
- }
- }
- }
- for (; ij < nreg; ij++)
- {
- reg[ij] = 0;
- }
-}
-
-Csite *
-cntr_new()
-{
- Csite *site = new Csite;
- if (site == nullptr) return nullptr;
- site->data = nullptr;
- site->reg = nullptr;
- site->saddle = nullptr;
- site->xcp = nullptr;
- site->ycp = nullptr;
- site->kcp = nullptr;
- site->x = nullptr;
- site->y = nullptr;
- site->z = nullptr;
- return site;
-}
-
-void
-cntr_init(Csite *site, long iMax, long jMax, const double *x, const double *y,
- const double *z, const bool *mask, long i_chunk_size, long j_chunk_size)
-{
- long ijmax = iMax * jMax;
- long nreg = iMax * jMax + iMax + 1;
-
- site->imax = iMax;
- site->jmax = jMax;
- site->data = new Cdata[nreg];
- site->saddle = new Saddle[ijmax];
- if (mask != nullptr)
- {
- site->reg = new char[nreg];
- mask_zones(iMax, jMax, mask, site->reg);
- }
- /* I don't think we need to initialize site->data. */
- site->x = x;
- site->y = y;
- site->z = z;
- site->xcp = nullptr;
- site->ycp = nullptr;
- site->kcp = nullptr;
-
- /* Store correct chunk sizes for filled contours. Chunking not used for
- line contours. */
- if (i_chunk_size <= 0 || i_chunk_size > iMax - 1)
- i_chunk_size = iMax - 1;
- site->i_chunk_size = i_chunk_size;
-
- if (j_chunk_size <= 0 || j_chunk_size > jMax - 1)
- j_chunk_size = jMax - 1;
- site->j_chunk_size = j_chunk_size;
-}
-
-void cntr_del(Csite *site)
-{
- delete [] site->saddle;
- delete [] site->reg;
- delete [] site->data;
- delete site;
- site = nullptr;
-}
-
-static int
-reorder(double *xpp, double *ypp, short *kpp, double *xy, unsigned char *c, int npts, int nlevels)
-{
- std::vector<int> subp;
- int isp, nsp;
- int iseg, nsegs;
- int isegplus;
- int i;
- int k;
- int started;
- int maxnsegs = npts/2 + 1;
- /* allocate maximum possible size--gross overkill */
- std::vector<int> i0(maxnsegs);
- std::vector<int> i1(maxnsegs);
-
- /* Find the segments. */
- iseg = 0;
- started = 0;
- for (i=0; i<npts; i++)
- {
- if (started)
- {
- if ((kpp[i] >= kind_slit_up) || (i == npts-1))
- {
- i1[iseg] = i;
- started = 0;
- iseg++;
- if (iseg == maxnsegs)
- {
- k = -1;
- return k;
- }
- }
- }
- else if ((kpp[i] < kind_slit_up) && (i < npts-1))
- {
- i0[iseg] = i;
- started = 1;
- }
- }
-
- nsegs = iseg;
-
- /* Find the subpaths as sets of connected segments. */
-
- subp.resize(nsegs, false);
- for (i=0; i<nsegs; i++) subp[i] = -1;
-
- nsp = 0;
- for (iseg=0; iseg<nsegs; iseg++)
- {
- /* For each segment, if it is not closed, look ahead for
- the next connected segment.
- */
- double xend, yend;
- xend = xpp[i1[iseg]];
- yend = ypp[i1[iseg]];
- if (subp[iseg] >= 0) continue;
- subp[iseg] = nsp;
- nsp++;
- if (iseg == nsegs-1) continue;
- for (isegplus = iseg+1; isegplus < nsegs; isegplus++)
- {
- if (subp[isegplus] >= 0) continue;
-
- if (xend == xpp[i0[isegplus]] && yend == ypp[i0[isegplus]])
- {
- subp[isegplus] = subp[iseg];
- xend = xpp[i1[isegplus]];
- yend = ypp[i1[isegplus]];
- }
- }
- }
-
- /* Generate the verts and codes from the subpaths. */
- k = 0;
- for (isp=0; isp<nsp; isp++)
- {
- int first = 1;
- int kstart = k;
- for (iseg=0; iseg<nsegs; iseg++)
- {
- int istart, iend;
- if (subp[iseg] != isp) continue;
- iend = i1[iseg];
- if (first)
- {
- istart = i0[iseg];
- }
- else
- {
- istart = i0[iseg]+1; /* skip duplicate */
- }
- for (i=istart; i<=iend; i++)
- {
- xy[2*k] = xpp[i];
- xy[2*k+1] = ypp[i];
- if (first) c[k] = MOVETO;
- else c[k] = LINETO;
- first = 0;
- k++;
- if (k > npts) /* should never happen */
- {
- k = -1;
- return k;
- }
- }
- }
- if (nlevels == 2 ||
- (xy[2*kstart] == xy[2*k-2] && xy[2*kstart+1] == xy[2*k-1]))
- {
- c[k-1] = CLOSEPOLY;
- }
- }
-
- return k;
-}
-
-/* Build a list of XY 2-D arrays, shape (N,2), to which a list of path
- code arrays is concatenated.
-*/
-static py::tuple
-build_cntr_list_v2(long *np, double *xp, double *yp, short *kp,
- int nparts, long ntotal, int nlevels)
-{
- int i;
- long k;
- py::ssize_t dims[2];
- py::ssize_t kdims[1];
-
- py::list all_verts(nparts);
- py::list all_codes(nparts);
-
- for (i=0, k=0; i < nparts; k+= np[i], i++)
- {
- double *xpp = xp+k;
- double *ypp = yp+k;
- short *kpp = kp+k;
- int n;
-
- dims[0] = np[i];
- dims[1] = 2;
- kdims[0] = np[i];
- PointArray xyv(dims);
- CodeArray kv(kdims);
-
- n = reorder(xpp, ypp, kpp, xyv.mutable_data(), kv.mutable_data(), np[i], nlevels);
- if (n == -1)
- {
- throw std::runtime_error("Error reordering vertices");
- }
-
- dims[0] = n;
- xyv.resize(dims, false);
- all_verts[i] = xyv;
-
- kdims[0] = n;
- kv.resize(kdims, false);
- all_codes[i] = kv;
- }
-
- return py::make_tuple(all_verts, all_codes);
-}
-
-/* cntr_trace is called once per contour level or level pair.
- If nlevels is 1, a set of contour lines will be returned; if nlevels
- is 2, the set of polygons bounded by the levels will be returned.
- If points is True, the lines will be returned as a list of list
- of points; otherwise, as a list of tuples of vectors.
-*/
-
-py::tuple
-cntr_trace(Csite *site, double levels[], int nlevels)
-{
- int iseg;
-
- long n;
- long nparts = 0;
- long ntotal = 0;
- long ntotal2 = 0;
-
- site->zlevel[0] = levels[0];
- site->zlevel[1] = levels[0];
- if (nlevels == 2)
- {
- site->zlevel[1] = levels[1];
- }
- site->n = site->count = 0;
- data_init (site);
-
- /* make first pass to compute required sizes for second pass */
- for (;;)
- {
- n = curve_tracer (site, 0);
-
- if (!n)
- break;
- if (n > 0)
- {
- nparts++;
- ntotal += n;
- }
- else
- {
- ntotal -= n;
- }
- }
- std::vector<double> xp0(ntotal);
- std::vector<double> yp0(ntotal);
- std::vector<short> kp0(ntotal);
- std::vector<long> nseg0(nparts);
-
- /* second pass */
- site->xcp = xp0.data();
- site->ycp = yp0.data();
- site->kcp = kp0.data();
- iseg = 0;
- for (;;iseg++)
- {
- n = curve_tracer (site, 1);
- if (ntotal2 + n > ntotal)
- {
- throw std::runtime_error("curve_tracer: ntotal2, pass 2 exceeds ntotal, pass 1");
- }
- if (n == 0)
- break;
- if (n > 0)
- {
- /* could add array bounds checking */
- nseg0[iseg] = n;
- site->xcp += n;
- site->ycp += n;
- site->kcp += n;
- ntotal2 += n;
- }
- else
- {
- throw std::runtime_error("Negative n from curve_tracer in pass 2");
- }
- }
-
- site->xcp = nullptr;
- site->ycp = nullptr;
- site->kcp = nullptr;
-
- return build_cntr_list_v2(
- nseg0.data(), xp0.data(), yp0.data(), kp0.data(), nparts, ntotal, nlevels);
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/mpl2005_original.h b/contrib/python/contourpy/src/mpl2005_original.h
deleted file mode 100644
index 93dba45ff9f..00000000000
--- a/contrib/python/contourpy/src/mpl2005_original.h
+++ /dev/null
@@ -1,56 +0,0 @@
-#ifndef CONTOURPY_MPL_2005_ORIGINAL_H
-#define CONTOURPY_MPL_2005_ORIGINAL_H
-
-#include "common.h"
-
-namespace contourpy {
-
-/* the data about edges, zones, and points -- boundary or not, exists
- * or not, z value 0, 1, or 2 -- is kept in a mesh sized data array */
-typedef short Cdata;
-
-/* information to decide on correct contour direction in saddle zones
- * is stored in a mesh sized array. Only those entries corresponding
- * to saddle zones have nonzero values in this array. */
-typedef char Saddle;
-
-/* here is the minimum structure required to tell where we are in the
- * mesh sized data array */
-struct Csite
-{
- long edge; /* ij of current edge */
- long left; /* +-1 or +-imax as the zone is to right, left, below,
- * or above the edge */
- long imax; /* imax for the mesh */
- long jmax; /* jmax for the mesh */
- long n; /* number of points marked on this curve so far */
- long count; /* count of start markers visited */
- double zlevel[2]; /* contour levels, zlevel[1]<=zlevel[0]
- * signals single level case */
- Saddle *saddle; /* saddle zone information for the mesh */
- char *reg; /* region array for the mesh (was int) */
- Cdata *data; /* added by EF */
- long edge0, left0; /* starting site on this curve for closure */
- int level0; /* starting level for closure */
- long edge00; /* site needing START_ROW mark */
-
- /* making the actual marks requires a bunch of other stuff */
- const double *x, *y, *z; /* mesh coordinates and function values */
- double *xcp, *ycp; /* output contour points */
- short *kcp; /* kind of contour point */
-
- long i_chunk_size, j_chunk_size;
-};
-
-Csite *cntr_new();
-
-void cntr_init(Csite *site, long iMax, long jMax, const double *x, const double *y,
- const double *z, const bool *mask, long i_chunk_size, long j_chunk_size);
-
-void cntr_del(Csite *site);
-
-py::tuple cntr_trace(Csite *site, double levels[], int nlevels);
-
-} // namespace contourpy
-
-#endif // CONTOURPY_MPL_2005_ORIGINAL_H
diff --git a/contrib/python/contourpy/src/mpl2014.cpp b/contrib/python/contourpy/src/mpl2014.cpp
deleted file mode 100644
index e1021572a7f..00000000000
--- a/contrib/python/contourpy/src/mpl2014.cpp
+++ /dev/null
@@ -1,1710 +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 setup.py, and then rebuilding.
-
-#include "mpl2014.h"
-#include "mpl_kind_code.h"
-#include <algorithm>
-
-namespace contourpy {
-namespace mpl2014 {
-
-// Point indices from current quad index.
-#define POINT_SW (quad)
-#define POINT_SE (quad+1)
-#define POINT_NW (quad+_nx)
-#define POINT_NE (quad+_nx+1)
-
-// CacheItem masks, only accessed directly to set. To read, use accessors
-// detailed below. 1 and 2 refer to level indices (lower and upper).
-#define MASK_Z_LEVEL 0x0003 // Combines the following two.
-#define MASK_Z_LEVEL_1 0x0001 // z > lower_level.
-#define MASK_Z_LEVEL_2 0x0002 // z > upper_level.
-#define MASK_VISITED_1 0x0004 // Algorithm has visited this quad.
-#define MASK_VISITED_2 0x0008
-#define MASK_SADDLE_1 0x0010 // quad is a saddle quad.
-#define MASK_SADDLE_2 0x0020
-#define MASK_SADDLE_LEFT_1 0x0040 // Contours turn left at saddle quad.
-#define MASK_SADDLE_LEFT_2 0x0080
-#define MASK_SADDLE_START_SW_1 0x0100 // Next visit starts on S or W edge.
-#define MASK_SADDLE_START_SW_2 0x0200
-#define MASK_BOUNDARY_S 0x0400 // S edge of quad is a boundary.
-#define MASK_BOUNDARY_W 0x0800 // W edge of quad is a boundary.
-// EXISTS_QUAD bit is always used, but the 4 EXISTS_CORNER are only used if
-// _corner_mask is true. Only one of EXISTS_QUAD or EXISTS_??_CORNER is ever
-// set per quad, hence not using unique bits for each; care is needed when
-// testing for these flags as they overlap.
-#define MASK_EXISTS_QUAD 0x1000 // All of quad exists (is not masked).
-#define MASK_EXISTS_SW_CORNER 0x2000 // SW corner exists, NE corner is masked.
-#define MASK_EXISTS_SE_CORNER 0x3000
-#define MASK_EXISTS_NW_CORNER 0x4000
-#define MASK_EXISTS_NE_CORNER 0x5000
-#define MASK_EXISTS 0x7000 // Combines all 5 EXISTS masks.
-
-// The following are only needed for filled contours.
-#define MASK_VISITED_S 0x10000 // Algorithm has visited S boundary.
-#define MASK_VISITED_W 0x20000 // Algorithm has visited W boundary.
-#define MASK_VISITED_CORNER 0x40000 // Algorithm has visited corner edge.
-
-
-// Accessors for various CacheItem masks. li is shorthand for level_index.
-#define Z_LEVEL(quad) (_cache[quad] & MASK_Z_LEVEL)
-#define Z_NE Z_LEVEL(POINT_NE)
-#define Z_NW Z_LEVEL(POINT_NW)
-#define Z_SE Z_LEVEL(POINT_SE)
-#define Z_SW Z_LEVEL(POINT_SW)
-#define VISITED(quad,li) ((_cache[quad] & (li==1 ? MASK_VISITED_1 : MASK_VISITED_2)) != 0)
-#define VISITED_S(quad) ((_cache[quad] & MASK_VISITED_S) != 0)
-#define VISITED_W(quad) ((_cache[quad] & MASK_VISITED_W) != 0)
-#define VISITED_CORNER(quad) ((_cache[quad] & MASK_VISITED_CORNER) != 0)
-#define SADDLE(quad,li) ((_cache[quad] & (li==1 ? MASK_SADDLE_1 : MASK_SADDLE_2)) != 0)
-#define SADDLE_LEFT(quad,li) ((_cache[quad] & (li==1 ? MASK_SADDLE_LEFT_1 : MASK_SADDLE_LEFT_2)) != 0)
-#define SADDLE_START_SW(quad,li) ((_cache[quad] & (li==1 ? MASK_SADDLE_START_SW_1 : MASK_SADDLE_START_SW_2)) != 0)
-#define BOUNDARY_S(quad) ((_cache[quad] & MASK_BOUNDARY_S) != 0)
-#define BOUNDARY_W(quad) ((_cache[quad] & MASK_BOUNDARY_W) != 0)
-#define BOUNDARY_N(quad) BOUNDARY_S(quad+_nx)
-#define BOUNDARY_E(quad) BOUNDARY_W(quad+1)
-#define EXISTS_QUAD(quad) ((_cache[quad] & MASK_EXISTS) == MASK_EXISTS_QUAD)
-#define EXISTS_NONE(quad) ((_cache[quad] & MASK_EXISTS) == 0)
-// The following are only used if _corner_mask is true.
-#define EXISTS_SW_CORNER(quad) ((_cache[quad] & MASK_EXISTS) == MASK_EXISTS_SW_CORNER)
-#define EXISTS_SE_CORNER(quad) ((_cache[quad] & MASK_EXISTS) == MASK_EXISTS_SE_CORNER)
-#define EXISTS_NW_CORNER(quad) ((_cache[quad] & MASK_EXISTS) == MASK_EXISTS_NW_CORNER)
-#define EXISTS_NE_CORNER(quad) ((_cache[quad] & MASK_EXISTS) == MASK_EXISTS_NE_CORNER)
-#define EXISTS_ANY_CORNER(quad) (!EXISTS_NONE(quad) && !EXISTS_QUAD(quad))
-#define EXISTS_W_EDGE(quad) (EXISTS_QUAD(quad) || EXISTS_SW_CORNER(quad) || EXISTS_NW_CORNER(quad))
-#define EXISTS_E_EDGE(quad) (EXISTS_QUAD(quad) || EXISTS_SE_CORNER(quad) || EXISTS_NE_CORNER(quad))
-#define EXISTS_S_EDGE(quad) (EXISTS_QUAD(quad) || EXISTS_SW_CORNER(quad) || EXISTS_SE_CORNER(quad))
-#define EXISTS_N_EDGE(quad) (EXISTS_QUAD(quad) || EXISTS_NW_CORNER(quad) || EXISTS_NE_CORNER(quad))
-// Note that EXISTS_NE_CORNER(quad) is equivalent to BOUNDARY_SW(quad), etc.
-
-
-
-QuadEdge::QuadEdge(index_t quad_, Edge edge_)
- : quad(quad_), edge(edge_)
-{}
-
-bool QuadEdge::operator==(const QuadEdge& other) const
-{
- return quad == other.quad && edge == other.edge;
-}
-
-std::ostream& operator<<(std::ostream& os, const QuadEdge& quad_edge)
-{
- return os << quad_edge.quad << ' ' << quad_edge.edge;
-}
-
-
-
-XY::XY(const double& x_, const double& y_)
- : x(x_), y(y_)
-{}
-
-bool XY::operator==(const XY& other) const
-{
- return x == other.x && y == other.y;
-}
-
-std::ostream& operator<<(std::ostream& os, const XY& xy)
-{
- return os << '(' << xy.x << ' ' << xy.y << ')';
-}
-
-
-
-ContourLine::ContourLine(bool is_hole)
- : std::vector<XY>(),
- _is_hole(is_hole),
- _parent(0)
-{}
-
-void ContourLine::add_child(ContourLine* child)
-{
- assert(!_is_hole && "Cannot add_child to a hole");
- assert(child != 0 && "Null child ContourLine");
- _children.push_back(child);
-}
-
-void ContourLine::clear_parent()
-{
- assert(is_hole() && "Cannot clear parent of non-hole");
- assert(_parent != 0 && "Null parent ContourLine");
- _parent = 0;
-}
-
-const ContourLine::Children& ContourLine::get_children() const
-{
- assert(!_is_hole && "Cannot get_children of a hole");
- return _children;
-}
-
-const ContourLine* ContourLine::get_parent() const
-{
- assert(_is_hole && "Cannot get_parent of a non-hole");
- return _parent;
-}
-
-ContourLine* ContourLine::get_parent()
-{
- assert(_is_hole && "Cannot get_parent of a non-hole");
- return _parent;
-}
-
-bool ContourLine::is_hole() const
-{
- return _is_hole;
-}
-
-void ContourLine::set_parent(ContourLine* parent)
-{
- assert(_is_hole && "Cannot set parent of a non-hole");
- assert(parent != 0 && "Null parent ContourLine");
- _parent = parent;
-}
-
-void ContourLine::write() const
-{
- std::cout << "ContourLine " << this << " of " << size() << " points:";
- for (const_iterator it = begin(); it != end(); ++it)
- std::cout << ' ' << *it;
- if (is_hole())
- std::cout << " hole, parent=" << get_parent();
- else {
- std::cout << " not hole";
- if (!_children.empty()) {
- std::cout << ", children=";
- for (Children::const_iterator it = _children.begin();
- it != _children.end(); ++it)
- std::cout << *it << ' ';
- }
- }
- std::cout << std::endl;
-}
-
-
-
-Contour::Contour()
-{}
-
-Contour::~Contour()
-{
- delete_contour_lines();
-}
-
-void Contour::delete_contour_lines()
-{
- for (iterator line_it = begin(); line_it != end(); ++line_it) {
- delete *line_it;
- *line_it = 0;
- }
- std::vector<ContourLine*>::clear();
-}
-
-void Contour::write() const
-{
- std::cout << "Contour of " << size() << " lines." << std::endl;
- for (const_iterator it = begin(); it != end(); ++it)
- (*it)->write();
-}
-
-
-
-ParentCache::ParentCache(index_t nx, index_t x_chunk_points, index_t y_chunk_points)
- : _nx(nx),
- _x_chunk_points(x_chunk_points),
- _y_chunk_points(y_chunk_points),
- _lines(0), // Initialised when first needed.
- _istart(0),
- _jstart(0)
-{
- assert(_x_chunk_points > 0 && _y_chunk_points > 0 && "Chunk sizes must be positive");
-}
-
-ContourLine* ParentCache::get_parent(index_t quad)
-{
- index_t index = index_to_index(quad);
- ContourLine* parent = _lines[index];
- while (parent == 0) {
- index -= _x_chunk_points;
- assert(index >= 0 && "Failed to find parent in chunk ParentCache");
- parent = _lines[index];
- }
- assert(parent != 0 && "Failed to find parent in chunk ParentCache");
- return parent;
-}
-
-index_t ParentCache::index_to_index(index_t quad) const
-{
- index_t i = quad % _nx;
- index_t j = quad / _nx;
- index_t index = (i-_istart) + (j-_jstart)*_x_chunk_points;
-
- assert(i >= _istart && i < _istart + _x_chunk_points && "i-index outside chunk");
- assert(j >= _jstart && j < _jstart + _y_chunk_points && "j-index outside chunk");
- assert(index >= 0 && index < static_cast<index_t>(_lines.size()) &&
- "ParentCache index outside chunk");
-
- return index;
-}
-
-void ParentCache::set_chunk_starts(index_t istart, index_t jstart)
-{
- assert(istart >= 0 && jstart >= 0 && "Chunk start indices cannot be negative");
- _istart = istart;
- _jstart = jstart;
- if (_lines.empty())
- _lines.resize(_x_chunk_points*_y_chunk_points, 0);
- else
- std::fill(_lines.begin(), _lines.end(), (ContourLine*)0);
-}
-
-void ParentCache::set_parent(index_t quad, ContourLine& contour_line)
-{
- assert(!_lines.empty() && "Accessing ParentCache before it has been initialised");
- index_t index = index_to_index(quad);
- if (_lines[index] == 0)
- _lines[index] = contour_line.is_hole() ? contour_line.get_parent() : &contour_line;
-}
-
-
-
-Mpl2014ContourGenerator::Mpl2014ContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, index_t x_chunk_size, index_t y_chunk_size)
- : _x(x),
- _y(y),
- _z(z),
- _nx(_z.ndim() > 1 ? _z.shape(1) : 0),
- _ny(_z.ndim() > 0 ? _z.shape(0) : 0),
- _n(_nx*_ny),
- _corner_mask(corner_mask),
- _x_chunk_size(calc_chunk_size(_nx, x_chunk_size)),
- _y_chunk_size(calc_chunk_size(_ny, y_chunk_size)),
- _nxchunk(calc_chunk_count(_nx, _x_chunk_size)),
- _nychunk(calc_chunk_count(_ny, _y_chunk_size)),
- _chunk_count(_nxchunk*_nychunk),
- _cache(new CacheItem[_n]),
- _parent_cache(_nx,
- _x_chunk_size > 0 ? _x_chunk_size+1 : _nx,
- _y_chunk_size > 0 ? _y_chunk_size+1 : _ny)
-{
- if (_x.ndim() != 2 || _y.ndim() != 2 || _z.ndim() != 2)
- throw std::invalid_argument("x, y and z must all be 2D arrays");
-
- if (_x.shape(1) != _nx || _x.shape(0) != _ny ||
- _y.shape(1) != _nx || _y.shape(0) != _ny)
- throw std::invalid_argument("x, y and z arrays must have the same shape");
-
- if (_nx < 2 || _ny < 2)
- throw std::invalid_argument("x, y and z must all be at least 2x2 arrays");
-
- if (mask.ndim() != 0) { // ndim == 0 if mask is not set, which is valid.
- if (mask.ndim() != 2)
- throw std::invalid_argument("mask array must be a 2D array");
-
- if (mask.shape(1) != _nx || mask.shape(0) != _ny)
- throw std::invalid_argument(
- "If mask is set it must be a 2D array with the same shape as z");
- }
-
- if (x_chunk_size < 0 || y_chunk_size < 0)
- throw std::invalid_argument("x_chunk_size and y_chunk_size cannot be negative");
-
- init_cache_grid(mask);
-}
-
-Mpl2014ContourGenerator::~Mpl2014ContourGenerator()
-{
- delete [] _cache;
-}
-
-void Mpl2014ContourGenerator::append_contour_line_to_vertices_and_codes(
- ContourLine& contour_line, py::list& vertices_list, py::list& codes_list) const
-{
- // Convert ContourLine to Python equivalent, and clear it for reuse.
- // This function is called once for each line generated in create_contour().
- // 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::ssize_t npoints = static_cast<py::ssize_t>(contour_line.size());
-
- py::ssize_t vertices_dims[2] = {npoints, 2};
- PointArray vertices(vertices_dims);
- double* vertices_ptr = vertices.mutable_data();
-
- py::ssize_t codes_dims[1] = {npoints};
- CodeArray codes(codes_dims);
- unsigned char* codes_ptr = codes.mutable_data();
-
- for (ContourLine::const_iterator point = contour_line.begin();
- point != contour_line.end(); ++point) {
- *vertices_ptr++ = point->x;
- *vertices_ptr++ = point->y;
- *codes_ptr++ = (point == 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.append(vertices);
- codes_list.append(codes);
-
- contour_line.clear();
-}
-
-void Mpl2014ContourGenerator::append_contour_to_vertices_and_codes(
- Contour& contour, py::list& vertices_list, py::list& codes_list) const
-{
- // Convert Contour to Python equivalent, and clear it for reuse.
- // This function is called once for each polygon generated in
- // create_filled_contour(). A polygon consists of an outer line loop
- // (called the parent) and zero or more inner line loops or holes (called
- // the children). Two NumPy arrays are created for each polygon:
- // vertices is a double array of shape (npoints, 2) containing the (x, y)
- // coordinates of the points in the polygon (parent plus children)
- // 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.
-
- // Convert Contour to python equivalent, and clear it.
- for (Contour::iterator line_it = contour.begin(); line_it != contour.end();
- ++line_it) {
- ContourLine& line = **line_it;
- if (line.is_hole()) {
- // If hole has already been converted to python its parent will be
- // set to 0 and it can be deleted.
- if (line.get_parent() != 0) {
- delete *line_it;
- *line_it = 0;
- }
- }
- else {
- // Non-holes are converted to python together with their child
- // holes so that they are rendered correctly.
- ContourLine::const_iterator point;
- ContourLine::Children::const_iterator children_it;
-
- const ContourLine::Children& children = line.get_children();
- py::ssize_t npoints = static_cast<py::ssize_t>(line.size() + 1);
- for (children_it = children.begin(); children_it != children.end(); ++children_it)
- // cppcheck-suppress useStlAlgorithm
- npoints += static_cast<py::ssize_t>((*children_it)->size() + 1);
-
- py::ssize_t vertices_dims[2] = {npoints, 2};
- PointArray vertices(vertices_dims);
- double* vertices_ptr = vertices.mutable_data();
-
- py::ssize_t codes_dims[1] = {npoints};
- CodeArray codes(codes_dims);
- unsigned char* codes_ptr = codes.mutable_data();
-
- for (point = line.begin(); point != line.end(); ++point) {
- *vertices_ptr++ = point->x;
- *vertices_ptr++ = point->y;
- *codes_ptr++ = (point == line.begin() ? MOVETO : LINETO);
- }
- point = line.begin();
- *vertices_ptr++ = point->x;
- *vertices_ptr++ = point->y;
- *codes_ptr++ = CLOSEPOLY;
-
- for (children_it = children.begin(); children_it != children.end(); ++children_it) {
- ContourLine& child = **children_it;
- for (point = child.begin(); point != child.end(); ++point) {
- *vertices_ptr++ = point->x;
- *vertices_ptr++ = point->y;
- *codes_ptr++ = (point == child.begin() ? MOVETO : LINETO);
- }
- point = child.begin();
- *vertices_ptr++ = point->x;
- *vertices_ptr++ = point->y;
- *codes_ptr++ = CLOSEPOLY;
-
- child.clear_parent(); // To indicate it can be deleted.
- }
-
- vertices_list.append(vertices);
- codes_list.append(codes);
-
- delete *line_it;
- *line_it = 0;
- }
- }
-
- // Delete remaining contour lines.
- contour.delete_contour_lines();
-}
-
-index_t Mpl2014ContourGenerator::calc_chunk_count(index_t point_count, index_t chunk_size)
-{
- // Accepts any values for point_count and chunk_size.
-
- if (chunk_size > 0 && point_count > 1) {
- index_t count = (point_count-1) / chunk_size;
- if (count*chunk_size < point_count-1)
- ++count;
-
- assert(count >= 1 && "Invalid chunk count");
- return count;
- }
- else
- return 1;
-}
-
-index_t Mpl2014ContourGenerator::calc_chunk_size(index_t point_count, index_t chunk_size)
-{
- // Accepts any values for point_count and chunk_size.
- index_t ret = point_count - 1;
- if (chunk_size > 0)
- ret = std::min(chunk_size, ret);
-
- return std::max<index_t>(ret, 1);
-}
-
-void Mpl2014ContourGenerator::edge_interp(
- const QuadEdge& quad_edge, const double& level, ContourLine& contour_line)
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
- interp(get_edge_point_index(quad_edge, true),
- get_edge_point_index(quad_edge, false),
- level, contour_line);
-}
-
-py::tuple Mpl2014ContourGenerator::filled(double lower_level, double upper_level)
-{
- check_levels(lower_level, upper_level);
-
- init_cache_levels(lower_level, upper_level);
-
- Contour contour;
- py::list vertices, codes;
-
- index_t ichunk, jchunk, istart, iend, jstart, jend;
- for (index_t ijchunk = 0; ijchunk < _chunk_count; ++ijchunk) {
- get_chunk_limits(ijchunk, ichunk, jchunk, istart, iend, jstart, jend);
- _parent_cache.set_chunk_starts(istart, jstart);
-
- for (index_t j = jstart; j < jend; ++j) {
- index_t quad_end = iend + j*_nx;
- for (index_t quad = istart + j*_nx; quad < quad_end; ++quad) {
- if (!EXISTS_NONE(quad))
- single_quad_filled(contour, quad, lower_level, upper_level);
- }
- }
-
- // Clear VISITED_W and VISITED_S flags that are reused by later chunks.
- if (jchunk < _nychunk-1) {
- index_t quad_end = iend + jend*_nx;
- for (index_t quad = istart + jend*_nx; quad < quad_end; ++quad)
- _cache[quad] &= ~MASK_VISITED_S;
- }
-
- if (ichunk < _nxchunk-1) {
- index_t quad_end = iend + jend*_nx;
- for (index_t quad = iend + jstart*_nx; quad < quad_end; quad += _nx)
- _cache[quad] &= ~MASK_VISITED_W;
- }
-
- // Create python objects to return for this chunk.
- append_contour_to_vertices_and_codes(contour, vertices, codes);
- }
-
- return py::make_tuple(vertices, codes);
-}
-
-unsigned int Mpl2014ContourGenerator::follow_boundary(
- ContourLine& contour_line, QuadEdge& quad_edge, const double& lower_level,
- const double& upper_level, unsigned int level_index, const QuadEdge& start_quad_edge)
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
- assert(is_edge_a_boundary(quad_edge) && "Not a boundary edge");
- assert((level_index == 1 || level_index == 2) && "level index must be 1 or 2");
- assert(start_quad_edge.quad >= 0 && start_quad_edge.quad < _n &&
- "Start quad index out of bounds");
- assert(start_quad_edge.edge != Edge_None && "Invalid start edge");
-
- // Only called for filled contours, so always updates _parent_cache.
- unsigned int end_level = 0;
- bool first_edge = true;
- bool stop = false;
- index_t& quad = quad_edge.quad;
-
- while (true) {
- // Levels of start and end points of quad_edge.
- unsigned int start_level =
- (first_edge ? Z_LEVEL(get_edge_point_index(quad_edge, true)) : end_level);
- index_t end_point = get_edge_point_index(quad_edge, false);
- end_level = Z_LEVEL(end_point);
-
- if (level_index == 1) {
- if (start_level <= level_index && end_level == 2) {
- // Increasing z, switching levels from 1 to 2.
- level_index = 2;
- stop = true;
- }
- else if (start_level >= 1 && end_level == 0) {
- // Decreasing z, keeping same level.
- stop = true;
- }
- }
- else { // level_index == 2
- if (start_level <= level_index && end_level == 2) {
- // Increasing z, keeping same level.
- stop = true;
- }
- else if (start_level >= 1 && end_level == 0) {
- // Decreasing z, switching levels from 2 to 1.
- level_index = 1;
- stop = true;
- }
- }
-
- if (!first_edge && !stop && quad_edge == start_quad_edge)
- // Return if reached start point of contour line. Do this before
- // checking/setting VISITED flags as will already have been
- // visited.
- break;
-
- switch (quad_edge.edge) {
- case Edge_E:
- assert(!VISITED_W(quad+1) && "Already visited");
- _cache[quad+1] |= MASK_VISITED_W;
- break;
- case Edge_N:
- assert(!VISITED_S(quad+_nx) && "Already visited");
- _cache[quad+_nx] |= MASK_VISITED_S;
- break;
- case Edge_W:
- assert(!VISITED_W(quad) && "Already visited");
- _cache[quad] |= MASK_VISITED_W;
- break;
- case Edge_S:
- assert(!VISITED_S(quad) && "Already visited");
- _cache[quad] |= MASK_VISITED_S;
- break;
- case Edge_NE:
- case Edge_NW:
- case Edge_SW:
- case Edge_SE:
- assert(!VISITED_CORNER(quad) && "Already visited");
- _cache[quad] |= MASK_VISITED_CORNER;
- break;
- default:
- assert(0 && "Invalid Edge");
- break;
- }
-
- if (stop) {
- // Exiting boundary to enter interior.
- edge_interp(quad_edge, level_index == 1 ? lower_level : upper_level, contour_line);
- break;
- }
-
- move_to_next_boundary_edge(quad_edge);
-
- // Just moved to new quad edge, so label parent of start of quad edge.
- switch (quad_edge.edge) {
- case Edge_W:
- case Edge_SW:
- case Edge_S:
- case Edge_SE:
- if (!EXISTS_SE_CORNER(quad))
- _parent_cache.set_parent(quad, contour_line);
- break;
- case Edge_E:
- case Edge_NE:
- case Edge_N:
- case Edge_NW:
- if (!EXISTS_SW_CORNER(quad))
- _parent_cache.set_parent(quad + 1, contour_line);
- break;
- default:
- assert(0 && "Invalid edge");
- break;
- }
-
- // Add point to contour.
- get_point_xy(end_point, contour_line);
-
- first_edge = false;
- }
-
- return level_index;
-}
-
-void Mpl2014ContourGenerator::follow_interior(
- ContourLine& contour_line, QuadEdge& quad_edge, unsigned int level_index, const double& level,
- bool want_initial_point, const QuadEdge* start_quad_edge, unsigned int start_level_index,
- bool set_parents)
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds.");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
- assert((level_index == 1 || level_index == 2) && "level index must be 1 or 2");
- assert((start_quad_edge == 0 ||
- (start_quad_edge->quad >= 0 && start_quad_edge->quad < _n)) &&
- "Start quad index out of bounds.");
- assert((start_quad_edge == 0 || start_quad_edge->edge != Edge_None) && "Invalid start edge");
- assert((start_level_index == 1 || start_level_index == 2) &&
- "start level index must be 1 or 2");
-
- index_t& quad = quad_edge.quad;
- Edge& edge = quad_edge.edge;
-
- if (want_initial_point)
- edge_interp(quad_edge, level, contour_line);
-
- CacheItem visited_mask = (level_index == 1 ? MASK_VISITED_1 : MASK_VISITED_2);
- CacheItem saddle_mask = (level_index == 1 ? MASK_SADDLE_1 : MASK_SADDLE_2);
- Dir dir = Dir_Straight;
-
- while (true) {
- assert(!EXISTS_NONE(quad) && "Quad does not exist");
- assert(!(_cache[quad] & visited_mask) && "Quad already visited");
-
- // Determine direction to move to next quad. If the quad is already
- // labelled as a saddle quad then the direction is easily read from
- // the cache. Otherwise the direction is determined differently
- // depending on whether the quad is a corner quad or not.
-
- if (_cache[quad] & saddle_mask) {
- // Already identified as a saddle quad, so direction is easy.
- dir = (SADDLE_LEFT(quad,level_index) ? Dir_Left : Dir_Right);
- _cache[quad] |= visited_mask;
- }
- else if (EXISTS_ANY_CORNER(quad)) {
- // Need z-level of point opposite the entry edge, as that
- // determines whether contour turns left or right.
- index_t point_opposite = -1;
- switch (edge) {
- case Edge_E:
- point_opposite = (EXISTS_SE_CORNER(quad) ? POINT_SW : POINT_NW);
- break;
- case Edge_N:
- point_opposite = (EXISTS_NW_CORNER(quad) ? POINT_SW : POINT_SE);
- break;
- case Edge_W:
- point_opposite = (EXISTS_SW_CORNER(quad) ? POINT_SE : POINT_NE);
- break;
- case Edge_S:
- point_opposite = (EXISTS_SW_CORNER(quad) ? POINT_NW : POINT_NE);
- break;
- case Edge_NE: point_opposite = POINT_SW; break;
- case Edge_NW: point_opposite = POINT_SE; break;
- case Edge_SW: point_opposite = POINT_NE; break;
- case Edge_SE: point_opposite = POINT_NW; break;
- default: assert(0 && "Invalid edge"); break;
- }
- assert(point_opposite != -1 && "Failed to find opposite point");
-
- // Lower-level polygons (level_index == 1) always have higher
- // values to the left of the contour. Upper-level contours
- // (level_index == 2) are reversed, which is what the fancy XOR
- // does below.
- if ((Z_LEVEL(point_opposite) >= level_index) ^ (level_index == 2))
- dir = Dir_Right;
- else
- dir = Dir_Left;
- _cache[quad] |= visited_mask;
- }
- else {
- // Calculate configuration of this quad.
- index_t point_left = -1, point_right = -1;
- switch (edge) {
- case Edge_E: point_left = POINT_SW; point_right = POINT_NW; break;
- case Edge_N: point_left = POINT_SE; point_right = POINT_SW; break;
- case Edge_W: point_left = POINT_NE; point_right = POINT_SE; break;
- case Edge_S: point_left = POINT_NW; point_right = POINT_NE; break;
- default: assert(0 && "Invalid edge"); break;
- }
-
- unsigned int config = ((Z_LEVEL(point_left) >= level_index) << 1) |
- (Z_LEVEL(point_right) >= level_index);
-
- // Upper level (level_index == 2) polygons are reversed compared to
- // lower level ones, i.e. higher values on the right rather than
- // the left.
- if (level_index == 2)
- config = 3 - config;
-
- // Calculate turn direction to move to next quad along contour line.
- if (config == 1) {
- // New saddle quad, set up cache bits for it.
- double zmid = 0.25*(get_point_z(POINT_SW) +
- get_point_z(POINT_SE) +
- get_point_z(POINT_NW) +
- get_point_z(POINT_NE));
- _cache[quad] |= (level_index == 1 ? MASK_SADDLE_1 : MASK_SADDLE_2);
- if ((zmid > level) ^ (level_index == 2)) {
- dir = Dir_Right;
- }
- else {
- dir = Dir_Left;
- _cache[quad] |= (level_index == 1 ? MASK_SADDLE_LEFT_1 : MASK_SADDLE_LEFT_2);
- }
- if (edge == Edge_N || edge == Edge_E) {
- // Next visit to this quad must start on S or W.
- _cache[quad] |=
- (level_index == 1 ? MASK_SADDLE_START_SW_1 : MASK_SADDLE_START_SW_2);
- }
- }
- else {
- // Normal (non-saddle) quad.
- dir = (config == 0 ? Dir_Left : (config == 3 ? Dir_Right : Dir_Straight));
- _cache[quad] |= visited_mask;
- }
- }
-
- // Use dir to determine exit edge.
- edge = get_exit_edge(quad_edge, dir);
-
- if (set_parents) {
- if (edge == Edge_E)
- _parent_cache.set_parent(quad+1, contour_line);
- else if (edge == Edge_W)
- _parent_cache.set_parent(quad, contour_line);
- }
-
- // Add new point to contour line.
- edge_interp(quad_edge, level, contour_line);
-
- // Stop if reached boundary.
- if (is_edge_a_boundary(quad_edge))
- break;
-
- move_to_next_quad(quad_edge);
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
-
- // Return if reached start point of contour line.
- if (start_quad_edge != 0 &&
- quad_edge == *start_quad_edge &&
- level_index == start_level_index)
- break;
- }
-}
-
-py::tuple Mpl2014ContourGenerator::get_chunk_count() const
-{
- return py::make_tuple(_nychunk, _nxchunk);
-}
-
-void Mpl2014ContourGenerator::get_chunk_limits(
- index_t ijchunk, index_t& ichunk, index_t& jchunk, index_t& istart, index_t& iend,
- index_t& jstart, index_t& jend)
-{
- assert(ijchunk >= 0 && ijchunk < _chunk_count && "ijchunk out of bounds");
- ichunk = ijchunk % _nxchunk;
- jchunk = ijchunk / _nxchunk;
- istart = ichunk*_x_chunk_size;
- iend = (ichunk == _nxchunk-1 ? _nx : (ichunk+1)*_x_chunk_size);
- jstart = jchunk*_y_chunk_size;
- jend = (jchunk == _nychunk-1 ? _ny : (jchunk+1)*_y_chunk_size);
-}
-
-py::tuple Mpl2014ContourGenerator::get_chunk_size() const
-{
- return py::make_tuple(_y_chunk_size, _x_chunk_size);
-}
-
-bool Mpl2014ContourGenerator::get_corner_mask() const
-{
- return _corner_mask;
-}
-
-Edge Mpl2014ContourGenerator::get_corner_start_edge(
- index_t quad, unsigned int level_index) const
-{
- assert(quad >= 0 && quad < _n && "Quad index out of bounds");
- assert((level_index == 1 || level_index == 2) && "level index must be 1 or 2");
- assert(EXISTS_ANY_CORNER(quad) && "Quad is not a corner");
-
- // Diagram for NE corner. Rotate for other corners.
- //
- // edge12
- // point1 +---------+ point2
- // \ |
- // \ | edge23
- // edge31 \ |
- // \ |
- // + point3
- //
- index_t point1, point2, point3;
- Edge edge12, edge23, edge31;
- switch (_cache[quad] & MASK_EXISTS) {
- case MASK_EXISTS_SW_CORNER:
- point1 = POINT_SE; point2 = POINT_SW; point3 = POINT_NW;
- edge12 = Edge_S; edge23 = Edge_W; edge31 = Edge_NE;
- break;
- case MASK_EXISTS_SE_CORNER:
- point1 = POINT_NE; point2 = POINT_SE; point3 = POINT_SW;
- edge12 = Edge_E; edge23 = Edge_S; edge31 = Edge_NW;
- break;
- case MASK_EXISTS_NW_CORNER:
- point1 = POINT_SW; point2 = POINT_NW; point3 = POINT_NE;
- edge12 = Edge_W; edge23 = Edge_N; edge31 = Edge_SE;
- break;
- case MASK_EXISTS_NE_CORNER:
- point1 = POINT_NW; point2 = POINT_NE; point3 = POINT_SE;
- edge12 = Edge_N; edge23 = Edge_E; edge31 = Edge_SW;
- break;
- default:
- assert(0 && "Invalid EXISTS for quad");
- return Edge_None;
- }
-
- unsigned int config = ((Z_LEVEL(point1) >= level_index) << 2) |
- ((Z_LEVEL(point2) >= level_index) << 1) |
- ((Z_LEVEL(point3) >= level_index) << 0);
-
- // Upper level (level_index == 2) polygons are reversed compared to lower
- // level ones, i.e. higher values on the right rather than the left.
- if (level_index == 2)
- config = 7 - config;
-
- switch (config) {
- case 0: return Edge_None;
- case 1: return edge23;
- case 2: return edge12;
- case 3: return edge12;
- case 4: return edge31;
- case 5: return edge23;
- case 6: return edge31;
- case 7: return Edge_None;
- default: assert(0 && "Invalid config"); return Edge_None;
- }
-}
-
-index_t Mpl2014ContourGenerator::get_edge_point_index(
- const QuadEdge& quad_edge, bool start) const
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
-
- // Edges are ordered anticlockwise around their quad, as indicated by
- // directions of arrows in diagrams below.
- // Full quad NW corner (others similar)
- //
- // POINT_NW Edge_N POINT_NE POINT_NW Edge_N POINT_NE
- // +----<-----+ +----<-----+
- // | | | /
- // | | | quad /
- // Edge_W V quad ^ Edge_E Edge_W V ^
- // | | | / Edge_SE
- // | | | /
- // +---->-----+ +
- // POINT_SW Edge_S POINT_SE POINT_SW
- //
- const index_t& quad = quad_edge.quad;
- switch (quad_edge.edge) {
- case Edge_E: return (start ? POINT_SE : POINT_NE);
- case Edge_N: return (start ? POINT_NE : POINT_NW);
- case Edge_W: return (start ? POINT_NW : POINT_SW);
- case Edge_S: return (start ? POINT_SW : POINT_SE);
- case Edge_NE: return (start ? POINT_SE : POINT_NW);
- case Edge_NW: return (start ? POINT_NE : POINT_SW);
- case Edge_SW: return (start ? POINT_NW : POINT_SE);
- case Edge_SE: return (start ? POINT_SW : POINT_NE);
- default: assert(0 && "Invalid edge"); return 0;
- }
-}
-
-Edge Mpl2014ContourGenerator::get_exit_edge(const QuadEdge& quad_edge, Dir dir) const
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
-
- const index_t& quad = quad_edge.quad;
- const Edge& edge = quad_edge.edge;
- if (EXISTS_ANY_CORNER(quad)) {
- // Corner directions are always left or right. A corner is a triangle,
- // entered via one edge so the other two edges are the left and right
- // ones.
- switch (edge) {
- case Edge_E:
- return (EXISTS_SE_CORNER(quad)
- ? (dir == Dir_Left ? Edge_S : Edge_NW)
- : (dir == Dir_Right ? Edge_N : Edge_SW));
- case Edge_N:
- return (EXISTS_NW_CORNER(quad)
- ? (dir == Dir_Right ? Edge_W : Edge_SE)
- : (dir == Dir_Left ? Edge_E : Edge_SW));
- case Edge_W:
- return (EXISTS_SW_CORNER(quad)
- ? (dir == Dir_Right ? Edge_S : Edge_NE)
- : (dir == Dir_Left ? Edge_N : Edge_SE));
- case Edge_S:
- return (EXISTS_SW_CORNER(quad)
- ? (dir == Dir_Left ? Edge_W : Edge_NE)
- : (dir == Dir_Right ? Edge_E : Edge_NW));
- case Edge_NE: return (dir == Dir_Left ? Edge_S : Edge_W);
- case Edge_NW: return (dir == Dir_Left ? Edge_E : Edge_S);
- case Edge_SW: return (dir == Dir_Left ? Edge_N : Edge_E);
- case Edge_SE: return (dir == Dir_Left ? Edge_W : Edge_N);
- default: assert(0 && "Invalid edge"); return Edge_None;
- }
- }
- else {
- // A full quad has four edges, entered via one edge so that other three
- // edges correspond to left, straight and right directions.
- switch (edge) {
- case Edge_E:
- return (dir == Dir_Left ? Edge_S : (dir == Dir_Right ? Edge_N : Edge_W));
- case Edge_N:
- return (dir == Dir_Left ? Edge_E : (dir == Dir_Right ? Edge_W : Edge_S));
- case Edge_W:
- return (dir == Dir_Left ? Edge_N : (dir == Dir_Right ? Edge_S : Edge_E));
- case Edge_S:
- return (dir == Dir_Left ? Edge_W : (dir == Dir_Right ? Edge_E : Edge_N));
- default: assert(0 && "Invalid edge"); return Edge_None;
- }
- }
-}
-
-const double& Mpl2014ContourGenerator::get_point_x(index_t point) const
-{
- assert(point >= 0 && point < _n && "Point index out of bounds.");
- return _x.data()[point];
-}
-
-const double& Mpl2014ContourGenerator::get_point_y(index_t point) const
-{
- assert(point >= 0 && point < _n && "Point index out of bounds.");
- return _y.data()[point];
-}
-
-void Mpl2014ContourGenerator::get_point_xy(
- index_t point, ContourLine& contour_line) const
-{
- assert(point >= 0 && point < _n && "Point index out of bounds.");
- contour_line.emplace_back(_x.data()[point], _y.data()[point]);
-}
-
-const double& Mpl2014ContourGenerator::get_point_z(index_t point) const
-{
- assert(point >= 0 && point < _n && "Point index out of bounds.");
- return _z.data()[point];
-}
-
-Edge Mpl2014ContourGenerator::get_quad_start_edge(
- index_t quad, unsigned int level_index) const
-{
- assert(quad >= 0 && quad < _n && "Quad index out of bounds");
- assert((level_index == 1 || level_index == 2) && "level index must be 1 or 2");
- assert(EXISTS_QUAD(quad) && "Quad does not exist");
-
- unsigned int config = ((Z_NW >= level_index) << 3) |
- ((Z_NE >= level_index) << 2) |
- ((Z_SW >= level_index) << 1) |
- ((Z_SE >= level_index) << 0);
-
- // Upper level (level_index == 2) polygons are reversed compared to lower
- // level ones, i.e. higher values on the right rather than the left.
- if (level_index == 2)
- config = 15 - config;
-
- switch (config) {
- case 0: return Edge_None;
- case 1: return Edge_E;
- case 2: return Edge_S;
- case 3: return Edge_E;
- case 4: return Edge_N;
- case 5: return Edge_N;
- case 6:
- // If already identified as a saddle quad then the start edge is
- // read from the cache. Otherwise return either valid start edge
- // and the subsequent call to follow_interior() will correctly set
- // up saddle bits in cache.
- if (!SADDLE(quad,level_index) || SADDLE_START_SW(quad,level_index))
- return Edge_S;
- else
- return Edge_N;
- case 7: return Edge_N;
- case 8: return Edge_W;
- case 9:
- // See comment for 6 above.
- if (!SADDLE(quad,level_index) || SADDLE_START_SW(quad,level_index))
- return Edge_W;
- else
- return Edge_E;
- case 10: return Edge_S;
- case 11: return Edge_E;
- case 12: return Edge_W;
- case 13: return Edge_W;
- case 14: return Edge_S;
- case 15: return Edge_None;
- default: assert(0 && "Invalid config"); return Edge_None;
- }
-}
-
-Edge Mpl2014ContourGenerator::get_start_edge(index_t quad, unsigned int level_index) const
-{
- if (EXISTS_ANY_CORNER(quad))
- return get_corner_start_edge(quad, level_index);
- else
- return get_quad_start_edge(quad, level_index);
-}
-
-void Mpl2014ContourGenerator::init_cache_grid(const MaskArray& mask)
-{
- index_t i, j, quad;
-
- if (mask.ndim() == 0) {
- // No mask, easy to calculate quad existence and boundaries together.
- quad = 0;
- for (j = 0; j < _ny; ++j) {
- for (i = 0; i < _nx; ++i, ++quad) {
- _cache[quad] = 0;
-
- if (i < _nx-1 && j < _ny-1)
- _cache[quad] |= MASK_EXISTS_QUAD;
-
- if ((i % _x_chunk_size == 0 || i == _nx-1) && j < _ny-1)
- _cache[quad] |= MASK_BOUNDARY_W;
-
- if ((j % _y_chunk_size == 0 || j == _ny-1) && i < _nx-1)
- _cache[quad] |= MASK_BOUNDARY_S;
- }
- }
- }
- else {
- const bool* mask_ptr = mask.data();
-
- // Have mask so use two stages.
- // Stage 1, determine if quads/corners exist.
- quad = 0;
- for (j = 0; j < _ny; ++j) {
- for (i = 0; i < _nx; ++i, ++quad) {
- _cache[quad] = 0;
-
- if (i < _nx-1 && j < _ny-1) {
- unsigned int config = (mask_ptr[POINT_NW] << 3) |
- (mask_ptr[POINT_NE] << 2) |
- (mask_ptr[POINT_SW] << 1) |
- (mask_ptr[POINT_SE] << 0);
-
- if (_corner_mask) {
- switch (config) {
- case 0: _cache[quad] = MASK_EXISTS_QUAD; break;
- case 1: _cache[quad] = MASK_EXISTS_NW_CORNER; break;
- case 2: _cache[quad] = MASK_EXISTS_NE_CORNER; break;
- case 4: _cache[quad] = MASK_EXISTS_SW_CORNER; break;
- case 8: _cache[quad] = MASK_EXISTS_SE_CORNER; break;
- default:
- // Do nothing, quad is masked out.
- break;
- }
- }
- else if (config == 0)
- _cache[quad] = MASK_EXISTS_QUAD;
- }
- }
- }
-
- // Stage 2, calculate W and S boundaries. For each quad use boundary
- // data already calculated for quads to W and S, so must iterate
- // through quads in correct order (increasing i and j indices).
- // Cannot use boundary data for quads to E and N as have not yet
- // calculated it.
- quad = 0;
- for (j = 0; j < _ny; ++j) {
- for (i = 0; i < _nx; ++i, ++quad) {
- if (_corner_mask) {
- bool W_exists_none = (i == 0 || EXISTS_NONE(quad-1));
- bool S_exists_none = (j == 0 || EXISTS_NONE(quad-_nx));
- bool W_exists_E_edge = (i > 0 && EXISTS_E_EDGE(quad-1));
- bool S_exists_N_edge = (j > 0 && EXISTS_N_EDGE(quad-_nx));
-
- if ((EXISTS_W_EDGE(quad) && W_exists_none) ||
- (EXISTS_NONE(quad) && W_exists_E_edge) ||
- (i % _x_chunk_size == 0 && EXISTS_W_EDGE(quad) && W_exists_E_edge))
- _cache[quad] |= MASK_BOUNDARY_W;
-
- if ((EXISTS_S_EDGE(quad) && S_exists_none) ||
- (EXISTS_NONE(quad) && S_exists_N_edge) ||
- (j % _y_chunk_size == 0 && EXISTS_S_EDGE(quad) && S_exists_N_edge))
- _cache[quad] |= MASK_BOUNDARY_S;
- }
- else {
- bool W_exists_quad = (i > 0 && EXISTS_QUAD(quad-1));
- bool S_exists_quad = (j > 0 && EXISTS_QUAD(quad-_nx));
-
- if ((EXISTS_QUAD(quad) != W_exists_quad) ||
- (i % _x_chunk_size == 0 && EXISTS_QUAD(quad) && W_exists_quad))
- _cache[quad] |= MASK_BOUNDARY_W;
-
- if ((EXISTS_QUAD(quad) != S_exists_quad) ||
- (j % _y_chunk_size == 0 && EXISTS_QUAD(quad) && S_exists_quad))
- _cache[quad] |= MASK_BOUNDARY_S;
- }
- }
- }
- }
-}
-
-void Mpl2014ContourGenerator::init_cache_levels(
- const double& lower_level, const double& upper_level)
-{
- assert(!(upper_level < lower_level) && "upper and lower levels are wrong way round");
-
- bool two_levels = (lower_level != upper_level);
- CacheItem keep_mask =
- (_corner_mask ? MASK_EXISTS | MASK_BOUNDARY_S | MASK_BOUNDARY_W
- : MASK_EXISTS_QUAD | MASK_BOUNDARY_S | MASK_BOUNDARY_W);
-
- if (two_levels) {
- const double* z_ptr = _z.data();
- for (index_t quad = 0; quad < _n; ++quad, ++z_ptr) {
- _cache[quad] &= keep_mask;
- if (*z_ptr > upper_level)
- _cache[quad] |= MASK_Z_LEVEL_2;
- else if (*z_ptr > lower_level)
- _cache[quad] |= MASK_Z_LEVEL_1;
- }
- }
- else {
- const double* z_ptr = _z.data();
- for (index_t quad = 0; quad < _n; ++quad, ++z_ptr) {
- _cache[quad] &= keep_mask;
- if (*z_ptr > lower_level)
- _cache[quad] |= MASK_Z_LEVEL_1;
- }
- }
-}
-
-void Mpl2014ContourGenerator::interp(
- index_t point1, index_t point2, const double& level, ContourLine& contour_line) const
-{
- assert(point1 >= 0 && point1 < _n && "Point index 1 out of bounds.");
- assert(point2 >= 0 && point2 < _n && "Point index 2 out of bounds.");
- assert(point1 != point2 && "Identical points");
- double fraction = (get_point_z(point2) - level) / (get_point_z(point2) - get_point_z(point1));
- contour_line.emplace_back(
- get_point_x(point1)*fraction + get_point_x(point2)*(1.0 - fraction),
- get_point_y(point1)*fraction + get_point_y(point2)*(1.0 - fraction));
-}
-
-bool Mpl2014ContourGenerator::is_edge_a_boundary(
- const QuadEdge& quad_edge) const
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
-
- switch (quad_edge.edge) {
- case Edge_E: return BOUNDARY_E(quad_edge.quad);
- case Edge_N: return BOUNDARY_N(quad_edge.quad);
- case Edge_W: return BOUNDARY_W(quad_edge.quad);
- case Edge_S: return BOUNDARY_S(quad_edge.quad);
- case Edge_NE: return EXISTS_SW_CORNER(quad_edge.quad);
- case Edge_NW: return EXISTS_SE_CORNER(quad_edge.quad);
- case Edge_SW: return EXISTS_NE_CORNER(quad_edge.quad);
- case Edge_SE: return EXISTS_NW_CORNER(quad_edge.quad);
- default: assert(0 && "Invalid edge"); return true;
- }
-}
-
-py::sequence Mpl2014ContourGenerator::lines(double level)
-{
- init_cache_levels(level, level);
-
- py::list vertices_list, codes_list;
-
- // Lines that start and end on boundaries.
- index_t ichunk, jchunk, istart, iend, jstart, jend;
- for (index_t ijchunk = 0; ijchunk < _chunk_count; ++ijchunk) {
- get_chunk_limits(ijchunk, ichunk, jchunk, istart, iend, jstart, jend);
-
- for (index_t j = jstart; j < jend; ++j) {
- index_t quad_end = iend + j*_nx;
- for (index_t quad = istart + j*_nx; quad < quad_end; ++quad) {
- if (EXISTS_NONE(quad) || VISITED(quad,1)) continue;
-
- if (BOUNDARY_S(quad) && Z_SW >= 1 && Z_SE < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_S, level)) continue;
-
- if (BOUNDARY_W(quad) && Z_NW >= 1 && Z_SW < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_W, level)) continue;
-
- if (BOUNDARY_N(quad) && Z_NE >= 1 && Z_NW < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_N, level)) continue;
-
- if (BOUNDARY_E(quad) && Z_SE >= 1 && Z_NE < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_E, level)) continue;
-
- if (_corner_mask) {
- // Equates to NE boundary.
- if (EXISTS_SW_CORNER(quad) && Z_SE >= 1 && Z_NW < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_NE, level)) continue;
-
- // Equates to NW boundary.
- if (EXISTS_SE_CORNER(quad) && Z_NE >= 1 && Z_SW < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_NW, level)) continue;
-
- // Equates to SE boundary.
- if (EXISTS_NW_CORNER(quad) && Z_SW >= 1 && Z_NE < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_SE, level)) continue;
-
- // Equates to SW boundary.
- if (EXISTS_NE_CORNER(quad) && Z_NW >= 1 && Z_SE < 1 &&
- start_line(vertices_list, codes_list, quad, Edge_SW, level)) continue;
- }
- }
- }
- }
-
- // Internal loops.
- ContourLine contour_line(false); // Reused for each contour line.
- for (index_t ijchunk = 0; ijchunk < _chunk_count; ++ijchunk) {
- get_chunk_limits(ijchunk, ichunk, jchunk, istart, iend, jstart, jend);
-
- for (index_t j = jstart; j < jend; ++j) {
- index_t quad_end = iend + j*_nx;
- for (index_t quad = istart + j*_nx; quad < quad_end; ++quad) {
- if (EXISTS_NONE(quad) || VISITED(quad,1))
- continue;
-
- Edge start_edge = get_start_edge(quad, 1);
- if (start_edge == Edge_None)
- continue;
-
- QuadEdge quad_edge(quad, start_edge);
- QuadEdge start_quad_edge(quad_edge);
-
- // To obtain output identical to that produced by legacy code,
- // sometimes need to ignore the first point and add it on the
- // end instead.
- bool ignore_first = (start_edge == Edge_N);
- follow_interior(
- contour_line, quad_edge, 1, level, !ignore_first, &start_quad_edge, 1, false);
- if (ignore_first && !contour_line.empty())
- contour_line.push_back(contour_line.front());
- append_contour_line_to_vertices_and_codes(contour_line, vertices_list, codes_list);
-
- // Repeat if saddle point but not visited.
- if (SADDLE(quad,1) && !VISITED(quad,1))
- --quad;
- }
- }
- }
-
- return py::make_tuple(vertices_list, codes_list);
-}
-
-void Mpl2014ContourGenerator::move_to_next_boundary_edge(
- QuadEdge& quad_edge) const
-{
- assert(is_edge_a_boundary(quad_edge) && "QuadEdge is not a boundary");
-
- index_t& quad = quad_edge.quad;
- Edge& edge = quad_edge.edge;
-
- quad = get_edge_point_index(quad_edge, false);
-
- // quad is now such that POINT_SW is the end point of the quad_edge passed
- // to this function.
-
- // To find the next boundary edge, first attempt to turn left 135 degrees
- // and if that edge is a boundary then move to it. If not, attempt to turn
- // left 90 degrees, then left 45 degrees, then straight on, etc, until can
- // move.
- // First determine which edge to attempt first.
- int index = 0;
- switch (edge) {
- case Edge_E: index = 0; break;
- case Edge_SE: index = 1; break;
- case Edge_S: index = 2; break;
- case Edge_SW: index = 3; break;
- case Edge_W: index = 4; break;
- case Edge_NW: index = 5; break;
- case Edge_N: index = 6; break;
- case Edge_NE: index = 7; break;
- default: assert(0 && "Invalid edge"); break;
- }
-
- // If _corner_mask not set, only need to consider odd index in loop below.
- if (!_corner_mask)
- ++index;
-
- // Try each edge in turn until a boundary is found.
- int start_index = index;
- do
- {
- switch (index) {
- case 0:
- if (EXISTS_SE_CORNER(quad-_nx-1)) { // Equivalent to BOUNDARY_NW
- quad -= _nx+1;
- edge = Edge_NW;
- return;
- }
- break;
- case 1:
- if (BOUNDARY_N(quad-_nx-1)) {
- quad -= _nx+1;
- edge = Edge_N;
- return;
- }
- break;
- case 2:
- if (EXISTS_SW_CORNER(quad-1)) { // Equivalent to BOUNDARY_NE
- quad -= 1;
- edge = Edge_NE;
- return;
- }
- break;
- case 3:
- if (BOUNDARY_E(quad-1)) {
- quad -= 1;
- edge = Edge_E;
- return;
- }
- break;
- case 4:
- if (EXISTS_NW_CORNER(quad)) { // Equivalent to BOUNDARY_SE
- edge = Edge_SE;
- return;
- }
- break;
- case 5:
- if (BOUNDARY_S(quad)) {
- edge = Edge_S;
- return;
- }
- break;
- case 6:
- if (EXISTS_NE_CORNER(quad-_nx)) { // Equivalent to BOUNDARY_SW
- quad -= _nx;
- edge = Edge_SW;
- return;
- }
- break;
- case 7:
- if (BOUNDARY_W(quad-_nx)) {
- quad -= _nx;
- edge = Edge_W;
- return;
- }
- break;
- default: assert(0 && "Invalid index"); break;
- }
-
- if (_corner_mask)
- index = (index + 1) % 8;
- else
- index = (index + 2) % 8;
- } while (index != start_index);
-
- assert(0 && "Failed to find next boundary edge");
-}
-
-void Mpl2014ContourGenerator::move_to_next_quad(QuadEdge& quad_edge) const
-{
- assert(quad_edge.quad >= 0 && quad_edge.quad < _n && "Quad index out of bounds");
- assert(quad_edge.edge != Edge_None && "Invalid edge");
-
- // Move from quad_edge.quad to the neighbouring quad in the direction
- // specified by quad_edge.edge.
- switch (quad_edge.edge) {
- case Edge_E: quad_edge.quad += 1; quad_edge.edge = Edge_W; break;
- case Edge_N: quad_edge.quad += _nx; quad_edge.edge = Edge_S; break;
- case Edge_W: quad_edge.quad -= 1; quad_edge.edge = Edge_E; break;
- case Edge_S: quad_edge.quad -= _nx; quad_edge.edge = Edge_N; break;
- default: assert(0 && "Invalid edge"); break;
- }
-}
-
-void Mpl2014ContourGenerator::single_quad_filled(
- Contour& contour, index_t quad, const double& lower_level, const double& upper_level)
-{
- assert(quad >= 0 && quad < _n && "Quad index out of bounds");
-
- // Order of checking is important here as can have different ContourLines
- // from both lower and upper levels in the same quad. First check the S
- // edge, then move up the quad to the N edge checking as required.
-
- // Possible starts from S boundary.
- if (BOUNDARY_S(quad) && EXISTS_S_EDGE(quad)) {
-
- // Lower-level start from S boundary into interior.
- if (!VISITED_S(quad) && Z_SW >= 1 && Z_SE == 0)
- contour.push_back(start_filled(
- quad, Edge_S, 1, NotHole, Interior, lower_level, upper_level));
-
- // Upper-level start from S boundary into interior.
- if (!VISITED_S(quad) && Z_SW < 2 && Z_SE == 2)
- contour.push_back(start_filled(
- quad, Edge_S, 2, NotHole, Interior, lower_level, upper_level));
-
- // Lower-level start following S boundary from W to E.
- if (!VISITED_S(quad) && Z_SW <= 1 && Z_SE == 1)
- contour.push_back(start_filled(
- quad, Edge_S, 1, NotHole, Boundary, lower_level, upper_level));
-
- // Upper-level start following S boundary from W to E.
- if (!VISITED_S(quad) && Z_SW == 2 && Z_SE == 1)
- contour.push_back(start_filled(
- quad, Edge_S, 2, NotHole, Boundary, lower_level, upper_level));
- }
-
- // Possible starts from W boundary.
- if (BOUNDARY_W(quad) && EXISTS_W_EDGE(quad)) {
-
- // Lower-level start from W boundary into interior.
- if (!VISITED_W(quad) && Z_NW >= 1 && Z_SW == 0)
- contour.push_back(start_filled(
- quad, Edge_W, 1, NotHole, Interior, lower_level, upper_level));
-
- // Upper-level start from W boundary into interior.
- if (!VISITED_W(quad) && Z_NW < 2 && Z_SW == 2)
- contour.push_back(start_filled(
- quad, Edge_W, 2, NotHole, Interior, lower_level, upper_level));
-
- // Lower-level start following W boundary from N to S.
- if (!VISITED_W(quad) && Z_NW <= 1 && Z_SW == 1)
- contour.push_back(start_filled(
- quad, Edge_W, 1, NotHole, Boundary, lower_level, upper_level));
-
- // Upper-level start following W boundary from N to S.
- if (!VISITED_W(quad) && Z_NW == 2 && Z_SW == 1)
- contour.push_back(start_filled(
- quad, Edge_W, 2, NotHole, Boundary, lower_level, upper_level));
- }
-
- // Possible starts from NE boundary.
- if (EXISTS_SW_CORNER(quad)) { // i.e. BOUNDARY_NE
-
- // Lower-level start following NE boundary from SE to NW, hole.
- if (!VISITED_CORNER(quad) && Z_NW == 1 && Z_SE == 1)
- contour.push_back(start_filled(
- quad, Edge_NE, 1, Hole, Boundary, lower_level, upper_level));
- }
- // Possible starts from SE boundary.
- else if (EXISTS_NW_CORNER(quad)) { // i.e. BOUNDARY_SE
-
- // Lower-level start from N to SE.
- if (!VISITED(quad,1) && Z_NW == 0 && Z_SW == 0 && Z_NE >= 1)
- contour.push_back(start_filled(
- quad, Edge_N, 1, NotHole, Interior, lower_level, upper_level));
-
- // Upper-level start from SE to N, hole.
- if (!VISITED(quad,2) && Z_NW < 2 && Z_SW < 2 && Z_NE == 2)
- contour.push_back(start_filled(
- quad, Edge_SE, 2, Hole, Interior, lower_level, upper_level));
-
- // Upper-level start from N to SE.
- if (!VISITED(quad,2) && Z_NW == 2 && Z_SW == 2 && Z_NE < 2)
- contour.push_back(start_filled(
- quad, Edge_N, 2, NotHole, Interior, lower_level, upper_level));
-
- // Lower-level start from SE to N, hole.
- if (!VISITED(quad,1) && Z_NW >= 1 && Z_SW >= 1 && Z_NE == 0)
- contour.push_back(start_filled(
- quad, Edge_SE, 1, Hole, Interior, lower_level, upper_level));
- }
- // Possible starts from NW boundary.
- else if (EXISTS_SE_CORNER(quad)) { // i.e. BOUNDARY_NW
-
- // Lower-level start from NW to E.
- if (!VISITED(quad,1) && Z_SW == 0 && Z_SE == 0 && Z_NE >= 1)
- contour.push_back(start_filled(
- quad, Edge_NW, 1, NotHole, Interior, lower_level, upper_level));
-
- // Upper-level start from E to NW, hole.
- if (!VISITED(quad,2) && Z_SW < 2 && Z_SE < 2 && Z_NE == 2)
- contour.push_back(start_filled(
- quad, Edge_E, 2, Hole, Interior, lower_level, upper_level));
-
- // Upper-level start from NW to E.
- if (!VISITED(quad,2) && Z_SW == 2 && Z_SE == 2 && Z_NE < 2)
- contour.push_back(start_filled(
- quad, Edge_NW, 2, NotHole, Interior, lower_level, upper_level));
-
- // Lower-level start from E to NW, hole.
- if (!VISITED(quad,1) && Z_SW >= 1 && Z_SE >= 1 && Z_NE == 0)
- contour.push_back(start_filled(
- quad, Edge_E, 1, Hole, Interior, lower_level, upper_level));
- }
- // Possible starts from SW boundary.
- else if (EXISTS_NE_CORNER(quad)) { // i.e. BOUNDARY_SW
-
- // Lower-level start from SW boundary into interior.
- if (!VISITED_CORNER(quad) && Z_NW >= 1 && Z_SE == 0)
- contour.push_back(start_filled(
- quad, Edge_SW, 1, NotHole, Interior, lower_level, upper_level));
-
- // Upper-level start from SW boundary into interior.
- if (!VISITED_CORNER(quad) && Z_NW < 2 && Z_SE == 2)
- contour.push_back(start_filled(
- quad, Edge_SW, 2, NotHole, Interior, lower_level, upper_level));
-
- // Lower-level start following SW boundary from NW to SE.
- if (!VISITED_CORNER(quad) && Z_NW <= 1 && Z_SE == 1)
- contour.push_back(start_filled(
- quad, Edge_SW, 1, NotHole, Boundary, lower_level, upper_level));
-
- // Upper-level start following SW boundary from NW to SE.
- if (!VISITED_CORNER(quad) && Z_NW == 2 && Z_SE == 1)
- contour.push_back(start_filled(
- quad, Edge_SW, 2, NotHole, Boundary, lower_level, upper_level));
- }
-
- // A full (unmasked) quad can only have a start on the NE corner, i.e. from
- // N to E (lower level) or E to N (upper level). Any other start will have
- // already been created in a call to this function for a prior quad so we
- // don't need to test for it again here.
- //
- // The situation is complicated by the possibility that the quad is a
- // saddle quad, in which case a contour line starting on the N could leave
- // by either the W or the E. We only need to consider those leaving E.
- //
- // A NE corner can also have a N to E or E to N start.
- if (EXISTS_QUAD(quad) || EXISTS_NE_CORNER(quad)) {
-
- // Lower-level start from N to E.
- if (!VISITED(quad,1) && Z_NW == 0 && Z_SE == 0 && Z_NE >= 1 &&
- (!SADDLE(quad,1) || SADDLE_LEFT(quad,1)))
- contour.push_back(start_filled(
- quad, Edge_N, 1, NotHole, Interior, lower_level, upper_level));
-
- // Upper-level start from E to N, hole.
- if (!VISITED(quad,2) && Z_NW < 2 && Z_SE < 2 && Z_NE == 2 &&
- (!SADDLE(quad,2) || !SADDLE_LEFT(quad,2)))
- contour.push_back(start_filled(
- quad, Edge_E, 2, Hole, Interior, lower_level, upper_level));
-
- // Upper-level start from N to E.
- if (!VISITED(quad,2) && Z_NW == 2 && Z_SE == 2 && Z_NE < 2 &&
- (!SADDLE(quad,2) || SADDLE_LEFT(quad,2)))
- contour.push_back(start_filled(
- quad, Edge_N, 2, NotHole, Interior, lower_level, upper_level));
-
- // Lower-level start from E to N, hole.
- if (!VISITED(quad,1) && Z_NW >= 1 && Z_SE >= 1 && Z_NE == 0 &&
- (!SADDLE(quad,1) || !SADDLE_LEFT(quad,1)))
- contour.push_back(start_filled(
- quad, Edge_E, 1, Hole, Interior, lower_level, upper_level));
-
- // All possible contours passing through the interior of this quad
- // should have already been created, so assert this.
- assert((VISITED(quad,1) || get_start_edge(quad, 1) == Edge_None) &&
- "Found start of contour that should have already been created");
- assert((VISITED(quad,2) || get_start_edge(quad, 2) == Edge_None) &&
- "Found start of contour that should have already been created");
- }
-
- // Lower-level start following N boundary from E to W, hole.
- // This is required for an internal masked region which is a hole in a
- // surrounding contour line.
- if (BOUNDARY_N(quad) && EXISTS_N_EDGE(quad) &&
- !VISITED_S(quad+_nx) && Z_NW == 1 && Z_NE == 1)
- contour.push_back(start_filled(
- quad, Edge_N, 1, Hole, Boundary, lower_level, upper_level));
-}
-
-ContourLine* Mpl2014ContourGenerator::start_filled(
- index_t quad, Edge edge, unsigned int start_level_index, HoleOrNot hole_or_not,
- BoundaryOrInterior boundary_or_interior, const double& lower_level, const double& upper_level)
-{
- assert(quad >= 0 && quad < _n && "Quad index out of bounds");
- assert(edge != Edge_None && "Invalid edge");
- assert((start_level_index == 1 || start_level_index == 2) &&
- "start level index must be 1 or 2");
-
- ContourLine* contour_line = new ContourLine(hole_or_not == Hole);
- if (hole_or_not == Hole) {
- // Find and set parent ContourLine.
- ContourLine* parent = _parent_cache.get_parent(quad + 1);
- assert(parent != 0 && "Failed to find parent ContourLine");
- contour_line->set_parent(parent);
- parent->add_child(contour_line);
- }
-
- QuadEdge quad_edge(quad, edge);
- const QuadEdge start_quad_edge(quad_edge);
- unsigned int level_index = start_level_index;
-
- // If starts on interior, can only finish on interior.
- // If starts on boundary, can only finish on boundary.
-
- while (true) {
- if (boundary_or_interior == Interior) {
- double level = (level_index == 1 ? lower_level : upper_level);
- follow_interior(
- *contour_line, quad_edge, level_index, level, false, &start_quad_edge,
- start_level_index, true);
- }
- else {
- level_index = follow_boundary(
- *contour_line, quad_edge, lower_level, upper_level, level_index, start_quad_edge);
- }
-
- if (quad_edge == start_quad_edge && (boundary_or_interior == Boundary ||
- level_index == start_level_index))
- break;
-
- if (boundary_or_interior == Boundary)
- boundary_or_interior = Interior;
- else
- boundary_or_interior = Boundary;
- }
-
- return contour_line;
-}
-
-bool Mpl2014ContourGenerator::start_line(
- py::list& vertices_list, py::list& codes_list, index_t quad, Edge edge, const double& level)
-{
- assert(is_edge_a_boundary(QuadEdge(quad, edge)) && "QuadEdge is not a boundary");
-
- QuadEdge quad_edge(quad, edge);
- ContourLine contour_line(false);
- follow_interior(contour_line, quad_edge, 1, level, true, 0, 1, false);
-
- append_contour_line_to_vertices_and_codes(contour_line, vertices_list, codes_list);
-
- return VISITED(quad,1);
-}
-
-/*void Mpl2014ContourGenerator::write_cache(bool grid_only) const
-{
- std::cout << "-----------------------------------------------" << std::endl;
- for (index_t quad = 0; quad < _n; ++quad)
- write_cache_quad(quad, grid_only);
- std::cout << "-----------------------------------------------" << std::endl;
-}
-
-void Mpl2014ContourGenerator::write_cache_quad(
- index_t quad, bool grid_only) const
-{
- index_t j = quad / _nx;
- index_t i = quad - j*_nx;
- std::cout << quad << ": i=" << i << " j=" << j << " EXISTS=" << EXISTS_QUAD(quad);
- if (_corner_mask)
- std::cout << " CORNER=" << EXISTS_SW_CORNER(quad) << EXISTS_SE_CORNER(quad)
- << EXISTS_NW_CORNER(quad) << EXISTS_NE_CORNER(quad);
- std::cout << " BNDY=" << BOUNDARY_S(quad) << BOUNDARY_W(quad);
- if (!grid_only) {
- std::cout << " Z=" << Z_LEVEL(quad)
- << " SAD=" << SADDLE(quad,1) << SADDLE(quad,2)
- << " LEFT=" << SADDLE_LEFT(quad,1) << SADDLE_LEFT(quad,2)
- << " NW=" << SADDLE_START_SW(quad,1) << SADDLE_START_SW(quad,2)
- << " VIS=" << VISITED(quad,1) << VISITED(quad,2)
- << VISITED_S(quad) << VISITED_W(quad)
- << VISITED_CORNER(quad);
- }
- std::cout << std::endl;
-}*/
-
-} // namespace mpl2014
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/mpl2014.h b/contrib/python/contourpy/src/mpl2014.h
deleted file mode 100644
index 5872e8ddcff..00000000000
--- a/contrib/python/contourpy/src/mpl2014.h
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Mpl2014ContourGenerator
- * -----------------------
- * A ContourGenerator generates contours for scalar fields defined on
- * quadrilateral grids. A single QuadContourGenerator object can create both
- * line contours (at single levels) and filled contours (between pairs of
- * levels) for the same field.
- *
- * A field to be contoured has nx, ny points in the x- and y-directions
- * respectively. The quad grid is defined by x and y arrays of shape(ny, nx),
- * and the field itself is the z array also of shape(ny, nx). There is an
- * optional boolean mask; if it exists then it also has shape(ny, nx). The
- * mask applies to grid points rather than quads.
- *
- * How quads are masked based on the point mask is determined by the boolean
- * 'corner_mask' flag. If false then any quad that has one or more of its four
- * corner points masked is itself masked. If true the behaviour is the same
- * except that any quad which has exactly one of its four corner points masked
- * has only the triangular corner (half of the quad) adjacent to that point
- * masked; the opposite triangular corner has three unmasked points and is not
- * masked.
- *
- * By default the entire domain of nx*ny points is contoured together which can
- * result in some very long polygons. The alternative is to break up the
- * domain into subdomains or 'chunks' of smaller size, each of which is
- * independently contoured. The size of these chunks is controlled by the
- * 'nchunk' (or 'chunk_size') parameter. Chunking not only results in shorter
- * polygons but also requires slightly less RAM. It can result in rendering
- * artifacts though, depending on backend, antialiased flag and alpha value.
- *
- * Notation
- * --------
- * i and j are array indices in the x- and y-directions respectively. Although
- * a single element of an array z can be accessed using z[j][i] or z(j,i), it
- * is often convenient to use the single quad index z[quad], where
- * quad = i + j*nx
- * and hence
- * i = quad % nx
- * j = quad / nx
- *
- * Rather than referring to x- and y-directions, compass directions are used
- * instead such that W, E, S, N refer to the -x, +x, -y, +y directions
- * respectively. To move one quad to the E you would therefore add 1 to the
- * quad index, to move one quad to the N you would add nx to the quad index.
- *
- * Cache
- * -----
- * Lots of information that is reused during contouring is stored as single
- * bits in a mesh-sized cache, indexed by quad. Each quad's cache entry stores
- * information about the quad itself such as if it is masked, and about the
- * point at the SW corner of the quad, and about the W and S edges. Hence
- * information about each point and each edge is only stored once in the cache.
- *
- * Cache information is divided into two types: that which is constant over the
- * lifetime of the QuadContourGenerator, and that which changes for each
- * contouring operation. The former is all grid-specific information such
- * as quad and corner masks, and which edges are boundaries, either between
- * masked and non-masked regions or between adjacent chunks. The latter
- * includes whether points lie above or below the current contour levels, plus
- * some flags to indicate how the contouring is progressing.
- *
- * Line Contours
- * -------------
- * A line contour connects points with the same z-value. Each point of such a
- * contour occurs on an edge of the grid, at a point linearly interpolated to
- * the contour z-level from the z-values at the end points of the edge. The
- * direction of a line contour is such that higher values are to the left of
- * the contour, so any edge that the contour passes through will have a left-
- * hand end point with z > contour level and a right-hand end point with
- * z <= contour level.
- *
- * Line contours are of two types. Firstly there are open line strips that
- * start on a boundary, traverse the interior of the domain and end on a
- * boundary. Secondly there are closed line loops that occur completely within
- * the interior of the domain and do not touch a boundary.
- *
- * The ContourGenerator makes two sweeps through the grid to generate line
- * contours for a particular level. In the first sweep it looks only for start
- * points that occur on boundaries, and when it finds one it follows the
- * contour through the interior until it finishes on another boundary edge.
- * Each quad that is visited by the algorithm has a 'visited' flag set in the
- * cache to indicate that the quad does not need to be visited again. In the
- * second sweep all non-visited quads are checked to see if they contain part
- * of an interior closed loop, and again each time one is found it is followed
- * through the domain interior until it returns back to its start quad and is
- * therefore completed.
- *
- * The situation is complicated by saddle quads that have two opposite corners
- * with z >= contour level and the other two corners with z < contour level.
- * These therefore contain two segments of a line contour, and the visited
- * flags take account of this by only being set on the second visit. On the
- * first visit a number of saddle flags are set in the cache to indicate which
- * one of the two segments has been completed so far.
- *
- * Filled Contours
- * ---------------
- * Filled contours are produced between two contour levels and are always
- * closed polygons. They can occur completely within the interior of the
- * domain without touching a boundary, following either the lower or upper
- * contour levels. Those on the lower level are exactly like interior line
- * contours with higher values on the left. Those on the upper level are
- * reversed such that higher values are on the right.
- *
- * Filled contours can also involve a boundary in which case they consist of
- * one or more sections along a boundary and one or more sections through the
- * interior. Interior sections can be on either level, and again those on the
- * upper level have higher values on the right. Boundary sections can remain
- * on either contour level or switch between the two.
- *
- * Once the start of a filled contour is found, the algorithm is similar to
- * that for line contours in that it follows the contour to its end, which
- * because filled contours are always closed polygons will be by returning
- * back to the start. However, because two levels must be considered, each
- * level has its own set of saddle and visited flags and indeed some extra
- * visited flags for boundary edges.
- *
- * The major complication for filled contours is that some polygons can be
- * holes (with points ordered clockwise) within other polygons (with points
- * ordered anticlockwise). When it comes to rendering filled contours each
- * non-hole polygon must be rendered along with its zero or more contained
- * holes or the rendering will not be correct. The filled contour finding
- * algorithm could progress pretty much as the line contour algorithm does,
- * taking each polygon as it is found, but then at the end there would have to
- * be an extra step to identify the parent non-hole polygon for each hole.
- * This is not a particularly onerous task but it does not scale well and can
- * easily dominate the execution time of the contour finding for even modest
- * problems. It is much better to identity each hole's parent non-hole during
- * the sweep algorithm.
- *
- * This requirement dictates the order that filled contours are identified. As
- * the algorithm sweeps up through the grid, every time a polygon passes
- * through a quad a ParentCache object is updated with the new possible parent.
- * When a new hole polygon is started, the ParentCache is used to find the
- * first possible parent in the same quad or to the S of it. Great care is
- * needed each time a new quad is checked to see if a new polygon should be
- * started, as a single quad can have multiple polygon starts, e.g. a quad
- * could be a saddle quad for both lower and upper contour levels, meaning it
- * has four contour line segments passing through it which could all be from
- * different polygons. The S-most polygon must be started first, then the next
- * S-most and so on until the N-most polygon is started in that quad.
- */
-#ifndef CONTOURPY_MPL_2014_H
-#define CONTOURPY_MPL_2014_H
-
-#include "contour_generator.h"
-#include <list>
-#include <iostream>
-#include <vector>
-
-namespace contourpy {
-namespace mpl2014 {
-
-// Edge of a quad including diagonal edges of masked quads if _corner_mask true.
-typedef enum
-{
- // Listing values here so easier to check for debug purposes.
- Edge_None = -1,
- Edge_E = 0,
- Edge_N = 1,
- Edge_W = 2,
- Edge_S = 3,
- // The following are only used if _corner_mask is true.
- Edge_NE = 4,
- Edge_NW = 5,
- Edge_SW = 6,
- Edge_SE = 7
-} Edge;
-
-// Combination of a quad and an edge of that quad.
-// An invalid quad edge has quad of -1.
-struct QuadEdge
-{
- QuadEdge() = delete;
- QuadEdge(index_t quad_, Edge edge_);
- bool operator==(const QuadEdge& other) const;
- friend std::ostream& operator<<(std::ostream& os, const QuadEdge& quad_edge);
-
- index_t quad;
- Edge edge;
-};
-
-// 2D point with x,y coordinates.
-struct XY
-{
- XY() = delete;
- XY(const double& x_, const double& y_);
- bool operator==(const XY& other) const;
- friend std::ostream& operator<<(std::ostream& os, const XY& xy);
-
- double x, y;
-};
-
-// 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().
-// A ContourLine is either a hole (points ordered clockwise) or it is not
-// (points ordered anticlockwise). Each hole has a parent ContourLine that is
-// not a hole; each non-hole contains zero or more child holes. A non-hole and
-// its child holes must be rendered together to obtain the correct results.
-class ContourLine : public std::vector<XY>
-{
-public:
- typedef std::list<ContourLine*> Children;
-
- explicit ContourLine(bool is_hole);
- void add_child(ContourLine* child);
- void clear_parent();
- const Children& get_children() const;
- const ContourLine* get_parent() const;
- ContourLine* get_parent();
- bool is_hole() const;
- void set_parent(ContourLine* parent);
- void write() const;
-
-private:
- bool _is_hole;
- ContourLine* _parent; // Only set if is_hole, not owned.
- Children _children; // Only set if !is_hole, not owned.
-};
-
-
-// A Contour is a collection of zero or more ContourLines.
-class Contour : public std::vector<ContourLine*>
-{
-public:
- Contour();
- virtual ~Contour();
- void delete_contour_lines();
- void write() const;
-};
-
-
-// Single chunk of ContourLine parents, indexed by quad. As a chunk's filled
-// contours are created, the ParentCache is updated each time a ContourLine
-// passes through each quad. When a new ContourLine is created, if it is a
-// hole its parent ContourLine is read from the ParentCache by looking at the
-// start quad, then each quad to the S in turn until a non-zero ContourLine is
-// found.
-class ParentCache
-{
-public:
- ParentCache(index_t nx, index_t x_chunk_points, index_t y_chunk_points);
- ContourLine* get_parent(index_t quad);
- void set_chunk_starts(index_t istart, index_t jstart);
- void set_parent(index_t quad, ContourLine& contour_line);
-
-private:
- index_t index_to_index(index_t quad) const;
-
- index_t _nx;
- index_t _x_chunk_points, _y_chunk_points; // Number of points not quads.
- std::vector<ContourLine*> _lines; // Not owned.
- index_t _istart, _jstart;
-};
-
-
-// See overview of algorithm at top of file.
-class Mpl2014ContourGenerator : public ContourGenerator
-{
-public:
- // Constructor with optional mask.
- // x, y, z: double arrays of shape (ny,nx).
- // mask: boolean array, ether empty (if no mask), or of shape (ny,nx).
- // corner_mask: flag for different masking behaviour.
- // x_chunk_size: 0 for no chunking, or +ve integer for size of chunks that
- // the x-direction is subdivided into.
- // y_chunk_size: 0 for no chunking, or +ve integer for size of chunks that
- // the y-direction is subdivided into.
- Mpl2014ContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, index_t x_chunk_size, index_t y_chunk_size);
-
- // Destructor.
- virtual ~Mpl2014ContourGenerator();
-
- // Create and return polygons for a filled contour between the two
- // specified levels.
- py::tuple filled(double lower_level, double upper_level) override;
-
- py::tuple get_chunk_count() const; // Return (y_chunk_count, x_chunk_count)
- py::tuple get_chunk_size() const; // Return (y_chunk_size, x_chunk_size)
-
- bool get_corner_mask() const;
-
- // Create and return polygons for a line (i.e. non-filled) contour at the
- // specified level.
- py::sequence lines(double level) override;
-
-private:
- // Typedef for following either a boundary of the domain or the interior;
- // clearer than using a boolean.
- typedef enum
- {
- Boundary,
- Interior
- } BoundaryOrInterior;
-
- // Typedef for direction of movement from one quad to the next.
- typedef enum
- {
- Dir_Right = -1,
- Dir_Straight = 0,
- Dir_Left = +1
- } Dir;
-
- // Typedef for a polygon being a hole or not; clearer than using a boolean.
- typedef enum
- {
- NotHole,
- Hole
- } HoleOrNot;
-
- // Append a C++ ContourLine to the end of two python lists. Used for line
- // contours where each ContourLine is converted to a separate numpy array
- // of (x,y) points and a second numpy array of 'kinds' or 'codes'.
- // Clears the ContourLine too.
- void append_contour_line_to_vertices_and_codes(
- ContourLine& contour_line, py::list& vertices_list, py::list& codes_list) const;
-
- // Append a C++ Contour to the end of two python lists. Used for filled
- // contours where each non-hole ContourLine and its child holes are
- // represented by a numpy array of (x,y) points and a second numpy array of
- // 'kinds' or 'codes' that indicates where the points array is split into
- // individual polygons.
- // Clears the Contour too, freeing each ContourLine as soon as possible
- // for minimum RAM usage.
- void append_contour_to_vertices_and_codes(
- Contour& contour, py::list& vertices_list, py::list& codes_list) const;
-
- // Return number of chunks that fit in the specified point_count.
- static index_t calc_chunk_count(index_t point_count, index_t chunk_size);
-
- // Return actual chunk_size from specified point_count and requested chunk_size.
- static index_t calc_chunk_size(index_t point_count, index_t chunk_size);
-
- // Append the point on the specified QuadEdge that intersects the specified
- // level to the specified ContourLine.
- void edge_interp(const QuadEdge& quad_edge, const double& level, ContourLine& contour_line);
-
- // Follow a contour along a boundary, appending points to the ContourLine
- // as it progresses. Only called for filled contours. Stops when the
- // contour leaves the boundary to move into the interior of the domain, or
- // when the start_quad_edge is reached in which case the ContourLine is a
- // completed closed loop. Always adds the end point of each boundary edge
- // to the ContourLine, regardless of whether moving to another boundary
- // edge or leaving the boundary into the interior. Never adds the start
- // point of the first boundary edge to the ContourLine.
- // contour_line: ContourLine to append points to.
- // quad_edge: on entry the QuadEdge to start from, on exit the QuadEdge
- // that is stopped on.
- // lower_level: lower contour z-value.
- // upper_level: upper contour z-value.
- // level_index: level index started on (1 = lower, 2 = upper level).
- // start_quad_edge: QuadEdge that the ContourLine started from, which is
- // used to check if the ContourLine is finished.
- // Returns the end level_index.
- unsigned int follow_boundary(
- ContourLine& contour_line, QuadEdge& quad_edge, const double& lower_level,
- const double& upper_level, unsigned int level_index, const QuadEdge& start_quad_edge);
-
- // Follow a contour across the interior of the domain, appending points to
- // the ContourLine as it progresses. Called for both line and filled
- // contours. Stops when the contour reaches a boundary or, if the
- // start_quad_edge is specified, when quad_edge == start_quad_edge and
- // level_index == start_level_index. Always adds the end point of each
- // quad traversed to the ContourLine; only adds the start point of the
- // first quad if want_initial_point flag is true.
- // contour_line: ContourLine to append points to.
- // quad_edge: on entry the QuadEdge to start from, on exit the QuadEdge
- // that is stopped on.
- // level_index: level index started on (1 = lower, 2 = upper level).
- // level: contour z-value.
- // want_initial_point: whether want to append the initial point to the
- // ContourLine or not.
- // start_quad_edge: the QuadEdge that the ContourLine started from to
- // check if the ContourLine is finished, or 0 if no check should occur.
- // start_level_index: the level_index that the ContourLine started from.
- // set_parents: whether should set ParentCache as it progresses or not.
- // This is true for filled contours, false for line contours.
- void follow_interior(
- ContourLine& contour_line, QuadEdge& quad_edge, unsigned int level_index,
- const double& level, bool want_initial_point, const QuadEdge* start_quad_edge,
- unsigned int start_level_index, bool set_parents);
-
- // Return the index limits of a particular chunk.
- void get_chunk_limits(
- index_t ijchunk, index_t& ichunk, index_t& jchunk, index_t& istart, index_t& iend,
- index_t& jstart, index_t& jend);
-
- // Check if a contour starts within the specified corner quad on the
- // specified level_index, and if so return the start edge. Otherwise
- // return Edge_None.
- Edge get_corner_start_edge(index_t quad, unsigned int level_index) const;
-
- // Return index of point at start or end of specified QuadEdge, assuming
- // anticlockwise ordering around non-masked quads.
- index_t get_edge_point_index(const QuadEdge& quad_edge, bool start) const;
-
- // Return the edge to exit a quad from, given the specified entry quad_edge
- // and direction to move in.
- Edge get_exit_edge(const QuadEdge& quad_edge, Dir dir) const;
-
- const double& get_point_x(index_t point) const;
- const double& get_point_y(index_t point) const;
-
- // Append the (x,y) coordinates of the specified point index to the
- // specified ContourLine.
- void get_point_xy(index_t point, ContourLine& contour_line) const;
-
- // Return the z-value of the specified point index.
- const double& get_point_z(index_t point) const;
-
- // Check if a contour starts within the specified non-corner quad on the
- // specified level_index, and if so return the start edge. Otherwise
- // return Edge_None.
- Edge get_quad_start_edge(index_t quad, unsigned int level_index) const;
-
- // Check if a contour starts within the specified quad, whether it is a
- // corner or a full quad, and if so return the start edge. Otherwise
- // return Edge_None.
- Edge get_start_edge(index_t quad, unsigned int level_index) const;
-
- // Initialise the cache to contain grid information that is constant
- // across the lifetime of this object, i.e. does not vary between calls to
- // create_contour() and create_filled_contour().
- void init_cache_grid(const MaskArray& mask);
-
- // Initialise the cache with information that is specific to contouring the
- // specified two levels. The levels are the same for contour lines,
- // different for filled contours.
- void init_cache_levels(const double& lower_level, const double& upper_level);
-
- // Append the (x,y) point at which the level intersects the line connecting
- // the two specified point indices to the specified ContourLine.
- void interp(
- index_t point1, index_t point2, const double& level, ContourLine& contour_line) const;
-
- // Return true if the specified QuadEdge is a boundary, i.e. is either an
- // edge between a masked and non-masked quad/corner or is a chunk boundary.
- bool is_edge_a_boundary(const QuadEdge& quad_edge) const;
-
- // Follow a boundary from one QuadEdge to the next in an anticlockwise
- // manner around the non-masked region.
- void move_to_next_boundary_edge(QuadEdge& quad_edge) const;
-
- // Move from the quad specified by quad_edge.quad to the neighbouring quad
- // by crossing the edge specified by quad_edge.edge.
- void move_to_next_quad(QuadEdge& quad_edge) const;
-
- // Check for filled contours starting within the specified quad and
- // complete any that are found, appending them to the specified Contour.
- void single_quad_filled(
- Contour& contour, index_t quad, const double& lower_level, const double& upper_level);
-
- // Start and complete a filled contour line.
- // quad: index of quad to start ContourLine in.
- // edge: edge of quad to start ContourLine from.
- // start_level_index: the level_index that the ContourLine starts from.
- // hole_or_not: whether the ContourLine is a hole or not.
- // boundary_or_interior: whether the ContourLine starts on a boundary or
- // the interior.
- // lower_level: lower contour z-value.
- // upper_level: upper contour z-value.
- // Returns newly created ContourLine.
- ContourLine* start_filled(
- index_t quad, Edge edge, unsigned int start_level_index, HoleOrNot hole_or_not,
- BoundaryOrInterior boundary_or_interior, const double& lower_level,
- const double& upper_level);
-
- // Start and complete a line contour that both starts and end on a
- // boundary, traversing the interior of the domain.
- // vertices_list: Python list that the ContourLine should be appended to.
- // codes_list: Python list that the kind codes should be appended to.
- // quad: index of quad to start ContourLine in.
- // edge: boundary edge to start ContourLine from.
- // level: contour z-value.
- // Returns true if the start quad does not need to be visited again, i.e.
- // VISITED(quad,1).
- bool start_line(
- py::list& vertices_list, py::list& codes_list, index_t quad, Edge edge,
- const double& level);
-
- // Debug function that writes the cache status to stdout.
- //void write_cache(bool grid_only = false) const;
-
- // Debug function that writes that cache status for a single quad to
- // stdout.
- //void write_cache_quad(index_t quad, bool grid_only) const;
-
-
-
- // Note that mask is not stored as once it has been used to initialise the
- // cache it is no longer needed.
- CoordinateArray _x, _y, _z;
- index_t _nx, _ny; // Number of points in each direction.
- index_t _n; // Total number of points (and hence quads).
-
- bool _corner_mask;
- index_t _x_chunk_size; // Number of quads per chunk (not points).
- index_t _y_chunk_size; // Always > 0.
-
- index_t _nxchunk, _nychunk; // Number of chunks in each direction.
- index_t _chunk_count; // Total number of chunks.
-
- typedef uint32_t CacheItem;
- CacheItem* _cache;
-
- ParentCache _parent_cache; // On W quad sides.
-};
-
-} // namespace mpl2014
-} // namespace contourpy
-
-#endif // #define CONTOURPY_MPL_2014_H
diff --git a/contrib/python/contourpy/src/mpl_kind_code.h b/contrib/python/contourpy/src/mpl_kind_code.h
deleted file mode 100644
index 11209f8be86..00000000000
--- a/contrib/python/contourpy/src/mpl_kind_code.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Enum for kind codes used in Matplotlib Paths.
-
-#ifndef MPL_KIND_CODE
-#define MPL_KIND_CODE
-
-namespace contourpy {
-
-typedef enum
-{
- MOVETO = 1,
- LINETO = 2,
- CLOSEPOLY = 79
-} MplKindCode;
-
-} // namespace contourpy
-
-#endif // MPL_KIND_CODE
diff --git a/contrib/python/contourpy/src/outer_or_hole.cpp b/contrib/python/contourpy/src/outer_or_hole.cpp
deleted file mode 100644
index 47a91aeb7c1..00000000000
--- a/contrib/python/contourpy/src/outer_or_hole.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "outer_or_hole.h"
-#include <iostream>
-
-namespace contourpy {
-
-std::ostream &operator<<(std::ostream &os, const OuterOrHole& outer_or_hole)
-{
- switch (outer_or_hole) {
- case Outer:
- os << "Outer";
- break;
- case Hole:
- os << "Hole";
- break;
- }
- return os;
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/outer_or_hole.h b/contrib/python/contourpy/src/outer_or_hole.h
deleted file mode 100644
index 0089a0bf753..00000000000
--- a/contrib/python/contourpy/src/outer_or_hole.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#ifndef CONTOURPY_OUTER_OR_HOLE_H
-#define CONTOURPY_OUTER_OR_HOLE_H
-
-#include <iosfwd>
-
-namespace contourpy {
-
-typedef enum
-{
- Outer,
- Hole
-} OuterOrHole;
-
-std::ostream &operator<<(std::ostream &os, const OuterOrHole& outer_or_hole);
-
-} // namespace contourpy
-
-#endif // CONTOURPY_OUTER_OR_HOLE_H
diff --git a/contrib/python/contourpy/src/output_array.h b/contrib/python/contourpy/src/output_array.h
deleted file mode 100644
index ce6be00d37c..00000000000
--- a/contrib/python/contourpy/src/output_array.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef CONTOURPY_OUTPUT_ARRAY_H
-#define CONTOURPY_OUTPUT_ARRAY_H
-
-#include "common.h"
-#include <vector>
-
-namespace contourpy {
-
-// A reusable array that is output from C++ to Python. Depending on the chosen line or fill type,
-// it can either be created as a NumPy array that will be directly returned to the Python caller,
-// or as a C++ vector that will be further manipulated (such as split up) before being converted to
-// NumPy array(s) for returning. BaseContourGenerator's marching does not care which form it is as
-// it just writes values to either array using an incrementing pointer.
-template <typename T>
-class OutputArray
-{
-public:
- OutputArray()
- : size(0), start(nullptr), current(nullptr)
- {}
-
- void clear()
- {
- vector.clear();
- size = 0;
- start = current = nullptr;
- }
-
- void create_cpp(count_t new_size)
- {
- assert(new_size > 0);
- size = new_size;
- vector.resize(size);
- start = current = vector.data();
- }
-
- py::array_t<T> create_python(count_t new_size)
- {
- assert(new_size > 0);
- size = new_size;
- py::array_t<T> py_array(size);
- start = current = py_array.mutable_data();
- return py_array;
- }
-
- py::array_t<T> create_python(count_t shape0, count_t shape1)
- {
- assert(shape0 > 0 && shape1 > 0);
- size = shape0*shape1;
- py::array_t<T> py_array({shape0, shape1});
- start = current = py_array.mutable_data();
- return py_array;
- }
-
- // Non-copyable and non-moveable.
- OutputArray(const OutputArray& other) = delete;
- OutputArray(const OutputArray&& other) = delete;
- OutputArray& operator=(const OutputArray& other) = delete;
- OutputArray& operator=(const OutputArray&& other) = delete;
-
-
- std::vector<T> vector;
- count_t size;
- T* start; // Start of array, whether C++ or Python.
- T* current; // Where to write next value to before incrementing.
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_OUTPUT_ARRAY_H
diff --git a/contrib/python/contourpy/src/serial.cpp b/contrib/python/contourpy/src/serial.cpp
deleted file mode 100644
index dd69471ef64..00000000000
--- a/contrib/python/contourpy/src/serial.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-#include "base_impl.h"
-#include "converter.h"
-#include "serial.h"
-
-namespace contourpy {
-
-SerialContourGenerator::SerialContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, LineType line_type, FillType fill_type,
- bool quad_as_tri, ZInterp z_interp, index_t x_chunk_size, index_t y_chunk_size)
- : BaseContourGenerator(x, y, z, mask, corner_mask, line_type, fill_type, quad_as_tri, z_interp,
- x_chunk_size, y_chunk_size)
-{}
-
-void SerialContourGenerator::export_filled(
- const ChunkLocal& local, std::vector<py::list>& return_lists)
-{
- assert(local.total_point_count > 0);
-
- switch (get_fill_type())
- {
- case FillType::OuterCode:
- case FillType::OuterOffset: {
- assert(!has_direct_points() && !has_direct_line_offsets());
- auto outer_count = local.line_count - local.hole_count;
-
- for (decltype(outer_count) i = 0; i < outer_count; ++i) {
- auto outer_start = local.outer_offsets.start[i];
- auto outer_end = local.outer_offsets.start[i+1];
- auto point_start = local.line_offsets.start[outer_start];
- auto point_end = local.line_offsets.start[outer_end];
- auto point_count = point_end - point_start;
- assert(point_count > 2);
-
- return_lists[0].append(Converter::convert_points(
- point_count, local.points.start + 2*point_start));
-
- if (get_fill_type() == FillType::OuterCode)
- return_lists[1].append(Converter::convert_codes(
- point_count, outer_end - outer_start + 1,
- local.line_offsets.start + outer_start, point_start));
- else
- return_lists[1].append(Converter::convert_offsets(
- outer_end - outer_start + 1, local.line_offsets.start + outer_start,
- point_start));
- }
- break;
- }
- case FillType::ChunkCombinedCode:
- case FillType::ChunkCombinedCodeOffset: {
- assert(has_direct_points() && !has_direct_line_offsets());
-
- // return_lists[0][local_chunk] already contains combined points.
- // If ChunkCombinedCodeOffset. return_lists[2][local.chunk] already contains outer
- // offsets.
- return_lists[1][local.chunk] = Converter::convert_codes(
- local.total_point_count, local.line_count + 1, local.line_offsets.start, 0);
- break;
- }
- case FillType::ChunkCombinedOffset:
- case FillType::ChunkCombinedOffsetOffset:
- assert(has_direct_points() && has_direct_line_offsets());
- if (get_fill_type() == FillType::ChunkCombinedOffsetOffset) {
- assert(has_direct_outer_offsets());
- }
- // return_lists[0][local_chunk] already contains combined points.
- // return_lists[1][local.chunk] already contains line offsets.
- // If ChunkCombinedOffsetOffset, return_lists[2][local.chunk] already contains
- // outer offsets.
- break;
- }
-}
-
-void SerialContourGenerator::export_lines(
- const ChunkLocal& local, std::vector<py::list>& return_lists)
-{
- assert(local.total_point_count > 0);
-
- switch (get_line_type())
- {
- case LineType::Separate:
- case LineType::SeparateCode: {
- assert(!has_direct_points() && !has_direct_line_offsets());
-
- bool separate_code = (get_line_type() == LineType::SeparateCode);
-
- for (decltype(local.line_count) i = 0; i < local.line_count; ++i) {
- auto point_start = local.line_offsets.start[i];
- auto point_end = local.line_offsets.start[i+1];
- auto point_count = point_end - point_start;
- assert(point_count > 1);
-
- return_lists[0].append(Converter::convert_points(
- point_count, local.points.start + 2*point_start));
-
- if (separate_code) {
- return_lists[1].append(
- Converter::convert_codes_check_closed_single(
- point_count, local.points.start + 2*point_start));
- }
- }
- break;
- }
- case LineType::ChunkCombinedCode: {
- assert(has_direct_points() && !has_direct_line_offsets());
-
- // return_lists[0][local.chunk] already contains points.
- return_lists[1][local.chunk] = Converter::convert_codes_check_closed(
- local.total_point_count, local.line_count + 1, local.line_offsets.start,
- local.points.start);
- break;
- }
- case LineType::ChunkCombinedOffset:
- assert(has_direct_points() && has_direct_line_offsets());
- // return_lists[0][local.chunk] already contains points.
- // return_lists[1][local.chunk] already contains line offsets.
- break;
- case LineType::ChunkCombinedNan:
- assert(has_direct_points());
- // return_lists[0][local.chunk] already contains points.
- break;
- }
-}
-
-void SerialContourGenerator::march(std::vector<py::list>& return_lists)
-{
- auto n_chunks = get_n_chunks();
- bool single_chunk = (n_chunks == 1);
-
- if (single_chunk) {
- // Stage 1: If single chunk, initialise cache z-levels and starting locations for whole
- // domain.
- init_cache_levels_and_starts();
- }
-
- // Stage 2: Trace contours.
- ChunkLocal local;
- for (index_t chunk = 0; chunk < n_chunks; ++chunk) {
- get_chunk_limits(chunk, local);
- if (!single_chunk)
- init_cache_levels_and_starts(&local);
- march_chunk(local, return_lists);
- local.clear();
- }
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/serial.h b/contrib/python/contourpy/src/serial.h
deleted file mode 100644
index 0ee288f22e6..00000000000
--- a/contrib/python/contourpy/src/serial.h
+++ /dev/null
@@ -1,40 +0,0 @@
-#ifndef CONTOURPY_SERIAL_H
-#define CONTOURPY_SERIAL_H
-
-#include "base.h"
-
-namespace contourpy {
-
-class SerialContourGenerator : public BaseContourGenerator<SerialContourGenerator>
-{
-public:
- SerialContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, LineType line_type, FillType fill_type,
- bool quad_as_tri, ZInterp z_interp, index_t x_chunk_size, index_t y_chunk_size);
-
-private:
- friend class BaseContourGenerator<SerialContourGenerator>;
-
- // Dummy Lock class which is the single-threaded version of ThreadedContourGenerator::Lock and
- // does not do anything, allowing base class code to use Lock objects for both serial and
- // multithreaded code.
- class Lock
- {
- public:
- explicit Lock(SerialContourGenerator& contour_generator)
- {}
- };
-
- // Write points and offsets/codes to output numpy arrays.
- void export_filled(const ChunkLocal& local, std::vector<py::list>& return_lists);
-
- // Write points and offsets/codes to output numpy arrays.
- void export_lines(const ChunkLocal& local, std::vector<py::list>& return_lists);
-
- void march(std::vector<py::list>& return_lists);
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_SERIAL_H
diff --git a/contrib/python/contourpy/src/threaded.cpp b/contrib/python/contourpy/src/threaded.cpp
deleted file mode 100644
index 9f6eec1beda..00000000000
--- a/contrib/python/contourpy/src/threaded.cpp
+++ /dev/null
@@ -1,316 +0,0 @@
-#include "base_impl.h"
-#include "converter.h"
-#include "threaded.h"
-#include "util.h"
-#include <thread>
-
-namespace contourpy {
-
-ThreadedContourGenerator::ThreadedContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, LineType line_type, FillType fill_type,
- bool quad_as_tri, ZInterp z_interp, index_t x_chunk_size, index_t y_chunk_size,
- index_t n_threads)
- : BaseContourGenerator(x, y, z, mask, corner_mask, line_type, fill_type, quad_as_tri, z_interp,
- x_chunk_size, y_chunk_size),
- _n_threads(limit_n_threads(n_threads, get_n_chunks())),
- _next_chunk(0)
-{}
-
-void ThreadedContourGenerator::export_filled(
- const ChunkLocal& local, std::vector<py::list>& return_lists)
-{
- // Reimplementation of SerialContourGenerator::export_filled() to separate out the creation of
- // numpy arrays (which requires a thread lock) from the population of those arrays (which does
- // not). This minimises the time that the lock is used for.
-
- assert(local.total_point_count > 0);
-
- switch (get_fill_type())
- {
- case FillType::OuterCode:
- case FillType::OuterOffset: {
- assert(!has_direct_points() && !has_direct_line_offsets());
-
- auto outer_count = local.line_count - local.hole_count;
- bool outer_code = (get_fill_type() == FillType::OuterCode);
- std::vector<PointArray::value_type*> points_ptrs(outer_count);
- std::vector<CodeArray::value_type*> codes_ptrs(outer_code ? outer_count: 0);
- std::vector<OffsetArray::value_type*> offsets_ptrs(outer_code ? 0 : outer_count);
-
- {
- Lock lock(*this); // cppcheck-suppress unreadVariable
- for (decltype(outer_count) i = 0; i < outer_count; ++i) {
- auto outer_start = local.outer_offsets.start[i];
- auto outer_end = local.outer_offsets.start[i+1];
- auto point_start = local.line_offsets.start[outer_start];
- auto point_end = local.line_offsets.start[outer_end];
- auto point_count = point_end - point_start;
- assert(point_count > 2);
-
- index_t points_shape[2] = {static_cast<index_t>(point_count), 2};
- PointArray point_array(points_shape);
- return_lists[0].append(point_array);
- points_ptrs[i] = point_array.mutable_data();
-
- if (outer_code) {
- index_t codes_shape = static_cast<index_t>(point_count);
- CodeArray code_array(codes_shape);
- return_lists[1].append(code_array);
- codes_ptrs[i] = code_array.mutable_data();
- }
- else {
- index_t offsets_shape = static_cast<index_t>(outer_end - outer_start + 1);
- OffsetArray offset_array(offsets_shape);
- return_lists[1].append(offset_array);
- offsets_ptrs[i] = offset_array.mutable_data();
- }
- }
- }
-
- for (decltype(outer_count) i = 0; i < outer_count; ++i) {
- auto outer_start = local.outer_offsets.start[i];
- auto outer_end = local.outer_offsets.start[i+1];
- auto point_start = local.line_offsets.start[outer_start];
- auto point_end = local.line_offsets.start[outer_end];
- auto point_count = point_end - point_start;
- assert(point_count > 2);
-
- Converter::convert_points(
- point_count, local.points.start + 2*point_start, points_ptrs[i]);
-
- if (outer_code)
- Converter::convert_codes(
- point_count, outer_end - outer_start + 1,
- local.line_offsets.start + outer_start, point_start, codes_ptrs[i]);
- else
- Converter::convert_offsets(
- outer_end - outer_start + 1, local.line_offsets.start + outer_start,
- point_start, offsets_ptrs[i]);
- }
- break;
- }
- case FillType::ChunkCombinedCode:
- case FillType::ChunkCombinedCodeOffset: {
- assert(has_direct_points() && !has_direct_line_offsets());
- // return_lists[0][local_chunk] already contains combined points.
- // If ChunkCombinedCodeOffset. return_lists[2][local.chunk] already contains outer
- // offsets.
-
- index_t codes_shape = static_cast<index_t>(local.total_point_count);
- CodeArray::value_type* codes_ptr = nullptr;
-
- {
- Lock lock(*this); // cppcheck-suppress unreadVariable
- CodeArray code_array(codes_shape);
- return_lists[1][local.chunk] = code_array;
- codes_ptr = code_array.mutable_data();
- }
-
- Converter::convert_codes(
- local.total_point_count, local.line_count + 1, local.line_offsets.start, 0,
- codes_ptr);
- break;
- }
- case FillType::ChunkCombinedOffset:
- case FillType::ChunkCombinedOffsetOffset:
- assert(has_direct_points() && has_direct_line_offsets());
- if (get_fill_type() == FillType::ChunkCombinedOffsetOffset) {
- assert(has_direct_outer_offsets());
- }
- // return_lists[0][local_chunk] already contains combined points.
- // return_lists[1][local.chunk] already contains line offsets.
- // If ChunkCombinedOffsetOffset, return_lists[2][local.chunk] already contains
- // outer offsets.
- break;
- }
-}
-
-void ThreadedContourGenerator::export_lines(
- const ChunkLocal& local, std::vector<py::list>& return_lists)
-{
- // Reimplementation of SerialContourGenerator::export_lines() to separate out the creation of
- // numpy arrays (which requires a thread lock) from the population of those arrays (which does
- // not). This minimises the time that the lock is used for.
-
- assert(local.total_point_count > 0);
-
- switch (get_line_type())
- {
- case LineType::Separate:
- case LineType::SeparateCode: {
- assert(!has_direct_points() && !has_direct_line_offsets());
-
- bool separate_code = (get_line_type() == LineType::SeparateCode);
- std::vector<PointArray::value_type*> points_ptrs(local.line_count);
- std::vector<CodeArray::value_type*> codes_ptrs(separate_code ? local.line_count: 0);
-
- {
- Lock lock(*this); // cppcheck-suppress unreadVariable
- for (decltype(local.line_count) i = 0; i < local.line_count; ++i) {
- auto point_start = local.line_offsets.start[i];
- auto point_end = local.line_offsets.start[i+1];
- auto point_count = point_end - point_start;
- assert(point_count > 1);
-
- index_t points_shape[2] = {static_cast<index_t>(point_count), 2};
- PointArray point_array(points_shape);
-
- return_lists[0].append(point_array);
- points_ptrs[i] = point_array.mutable_data();
-
- if (separate_code) {
- index_t codes_shape = static_cast<index_t>(point_count);
- CodeArray code_array(codes_shape);
- return_lists[1].append(code_array);
- codes_ptrs[i] = code_array.mutable_data();
- }
- }
- }
-
- for (decltype(local.line_count) i = 0; i < local.line_count; ++i) {
- auto point_start = local.line_offsets.start[i];
- auto point_end = local.line_offsets.start[i+1];
- auto point_count = point_end - point_start;
- assert(point_count > 1);
-
- Converter::convert_points(
- point_count, local.points.start + 2*point_start, points_ptrs[i]);
-
- if (separate_code) {
- Converter::convert_codes_check_closed_single(
- point_count, local.points.start + 2*point_start, codes_ptrs[i]);
- }
- }
- break;
- }
- case LineType::ChunkCombinedCode: {
- assert(has_direct_points() && !has_direct_line_offsets());
- // return_lists[0][local.chunk] already contains points.
-
- index_t codes_shape = static_cast<index_t>(local.total_point_count);
- CodeArray::value_type* codes_ptr = nullptr;
-
- {
- Lock lock(*this); // cppcheck-suppress unreadVariable
- CodeArray code_array(codes_shape);
- return_lists[1][local.chunk] = code_array;
- codes_ptr = code_array.mutable_data();
- }
-
- Converter::convert_codes_check_closed(
- local.total_point_count, local.line_count + 1, local.line_offsets.start,
- local.points.start, codes_ptr);
- break;
- }
- case LineType::ChunkCombinedOffset:
- assert(has_direct_points() && has_direct_line_offsets());
- // return_lists[0][local.chunk] already contains points.
- // return_lists[1][local.chunk] already contains line offsets.
- break;
- case LineType::ChunkCombinedNan:
- assert(has_direct_points());
- // return_lists[0][local.chunk] already contains points.
- break;
- }
-}
-
-index_t ThreadedContourGenerator::get_thread_count() const
-{
- return _n_threads;
-}
-
-index_t ThreadedContourGenerator::limit_n_threads(index_t n_threads, index_t n_chunks)
-{
- index_t max_threads = std::max<index_t>(Util::get_max_threads(), 1);
- if (n_threads == 0)
- return std::min(max_threads, n_chunks);
- else
- return std::min({max_threads, n_chunks, n_threads});
-}
-
-void ThreadedContourGenerator::march(std::vector<py::list>& return_lists)
-{
- // Each thread executes thread_function() which has two stages:
- // 1) Initialise cache z-levels and starting locations
- // 2) Trace contours
- // Each stage is performed on a chunk by chunk basis. There is a barrier between the two stages
- // to synchronise the threads so the cache setup is complete before being used by the trace.
- _next_chunk = 0; // Next available chunk index.
- _finished_count = 0; // Count of threads that have finished the cache init.
-
- // Main thread releases GIL for remainder of this function.
- // It is temporarily reacquired as necessary within the scope of threaded Lock objects.
- py::gil_scoped_release release;
-
- // Create (_n_threads-1) new worker threads.
- std::vector<std::thread> threads;
- threads.reserve(_n_threads-1);
- for (index_t i = 0; i < _n_threads-1; ++i)
- threads.emplace_back(
- &ThreadedContourGenerator::thread_function, this, std::ref(return_lists));
-
- thread_function(std::ref(return_lists)); // Main thread work.
-
- for (auto& thread : threads)
- thread.join();
- assert(_next_chunk == 2*get_n_chunks());
- threads.clear();
-}
-
-void ThreadedContourGenerator::thread_function(std::vector<py::list>& return_lists)
-{
- // Function that is executed by each of the threads.
- // _next_chunk starts at zero and increases up to 2*_n_chunks. A thread in need of work reads
- // _next_chunk and incremements it, then processes that chunk. For _next_chunk < _n_chunks this
- // is stage 1 (init cache levels and starting locations) and for _next_chunk >= _n_chunks this
- // is stage 2 (trace contours). There is a synchronisation barrier between the two stages so
- // that the cache initialisation is complete before being used by the contour trace.
-
- auto n_chunks = get_n_chunks();
- index_t chunk;
- ChunkLocal local;
-
- // Stage 1: Initialise cache z-levels and starting locations.
- while (true) {
- {
- std::lock_guard<std::mutex> guard(_chunk_mutex);
- if (_next_chunk < n_chunks)
- chunk = _next_chunk++;
- else
- break; // No more work to do.
- }
-
- get_chunk_limits(chunk, local);
- init_cache_levels_and_starts(&local);
- local.clear();
- }
-
- {
- // Implementation of multithreaded barrier. Each thread increments the shared counter.
- // Last thread to finish notifies the other threads that they can all continue.
- std::unique_lock<std::mutex> lock(_chunk_mutex);
- _finished_count++;
- if (_finished_count == _n_threads)
- _condition_variable.notify_all();
- else
- _condition_variable.wait(lock);
- }
-
- // Stage 2: Trace contours.
- while (true) {
- {
- std::lock_guard<std::mutex> guard(_chunk_mutex);
- if (_next_chunk < 2*n_chunks)
- chunk = _next_chunk++ - n_chunks;
- else
- break; // No more work to do.
- }
-
- get_chunk_limits(chunk, local);
- march_chunk(local, return_lists);
- local.clear();
- }
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/threaded.h b/contrib/python/contourpy/src/threaded.h
deleted file mode 100644
index ff45c1c5446..00000000000
--- a/contrib/python/contourpy/src/threaded.h
+++ /dev/null
@@ -1,69 +0,0 @@
-#ifndef CONTOURPY_THREADED_H
-#define CONTOURPY_THREADED_H
-
-#include "base.h"
-#include <condition_variable>
-#include <mutex>
-
-namespace contourpy {
-
-class ThreadedContourGenerator : public BaseContourGenerator<ThreadedContourGenerator>
-{
-public:
- ThreadedContourGenerator(
- const CoordinateArray& x, const CoordinateArray& y, const CoordinateArray& z,
- const MaskArray& mask, bool corner_mask, LineType line_type, FillType fill_type,
- bool quad_as_tri, ZInterp z_interp, index_t x_chunk_size, index_t y_chunk_size,
- index_t n_threads);
-
- index_t get_thread_count() const;
-
-private:
- friend class BaseContourGenerator<ThreadedContourGenerator>;
-
- // Lock class is used to lock access to a single thread when creating/modifying Python objects.
- // Also acquires the GIL for the duration of the lock.
- class Lock
- {
- public:
- explicit Lock(ThreadedContourGenerator& contour_generator)
- : _lock(contour_generator._python_mutex)
- {}
-
- // Non-copyable and non-moveable.
- Lock(const Lock& other) = delete;
- Lock(const Lock&& other) = delete;
- Lock& operator=(const Lock& other) = delete;
- Lock& operator=(const Lock&& other) = delete;
-
- private:
- std::unique_lock<std::mutex> _lock;
- py::gil_scoped_acquire _gil;
- };
-
- // Write points and offsets/codes to output numpy arrays.
- void export_filled(const ChunkLocal& local, std::vector<py::list>& return_lists);
-
- // Write points and offsets/codes to output numpy arrays.
- void export_lines(const ChunkLocal& local, std::vector<py::list>& return_lists);
-
- static index_t limit_n_threads(index_t n_threads, index_t n_chunks);
-
- void march(std::vector<py::list>& return_lists);
-
- void thread_function(std::vector<py::list>& return_lists);
-
-
-
- // Multithreading member variables.
- index_t _n_threads; // Number of threads used.
- index_t _next_chunk; // Next available chunk for thread to process.
- index_t _finished_count; // Count of threads that have finished the cache init.
- std::mutex _chunk_mutex; // Locks access to _next_chunk/_finished_count.
- std::mutex _python_mutex; // Locks access to Python objects.
- std::condition_variable _condition_variable; // Implements multithreaded barrier.
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_THREADED_H
diff --git a/contrib/python/contourpy/src/util.cpp b/contrib/python/contourpy/src/util.cpp
deleted file mode 100644
index 82c1cc21d93..00000000000
--- a/contrib/python/contourpy/src/util.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-#include "util.h"
-#include <cmath>
-#include <thread>
-
-namespace contourpy {
-
-bool Util::_nan_loaded = false;
-
-double Util::nan = 0.0;
-
-void Util::ensure_nan_loaded()
-{
- if (!_nan_loaded) {
- auto numpy = py::module_::import("numpy");
- nan = numpy.attr("nan").cast<double>();
- _nan_loaded = true;
- }
-}
-
-index_t Util::get_max_threads()
-{
- return static_cast<index_t>(std::thread::hardware_concurrency());
-}
-
-bool Util::is_nan(double value)
-{
- return std::isnan(value);
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/util.h b/contrib/python/contourpy/src/util.h
deleted file mode 100644
index dcafd131c15..00000000000
--- a/contrib/python/contourpy/src/util.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef CONTOURPY_UTIL_H
-#define CONTOURPY_UTIL_H
-
-#include "common.h"
-
-namespace contourpy {
-
-class Util
-{
-public:
- static void ensure_nan_loaded();
-
- static index_t get_max_threads();
-
- static bool is_nan(double value);
-
- // This is the NaN used internally and returned to calling functions. The value is taken from
- // numpy rather than the standard C++ approach so that it is guaranteed to work with
- // numpy.isnan(). The value is actually the same for many platforms, but this approach
- // guarantees it works for all platforms that numpy supports.
- //
- // ensure_nan_loaded() must be called before this value is read.
- static double nan;
-
-private:
- static bool _nan_loaded;
-};
-
-} // namespace contourpy
-
-#endif // CONTOURPY_UTIL_H
diff --git a/contrib/python/contourpy/src/wrap.cpp b/contrib/python/contourpy/src/wrap.cpp
deleted file mode 100644
index fede2280956..00000000000
--- a/contrib/python/contourpy/src/wrap.cpp
+++ /dev/null
@@ -1,419 +0,0 @@
-#include "base_impl.h"
-#include "contour_generator.h"
-#include "fill_type.h"
-#include "line_type.h"
-#include "mpl2005.h"
-#include "mpl2014.h"
-#include "serial.h"
-#include "threaded.h"
-#include "util.h"
-#include "z_interp.h"
-
-#define STRINGIFY(x) #x
-#define MACRO_STRINGIFY(x) STRINGIFY(x)
-
-namespace py = pybind11;
-using namespace pybind11::literals;
-
-static contourpy::LineType mpl20xx_line_type = contourpy::LineType::SeparateCode;
-static contourpy::FillType mpl20xx_fill_type = contourpy::FillType::OuterCode;
-
-PYBIND11_MODULE(_contourpy, m, py::mod_gil_not_used()) {
- m.doc() =
- "C++11 extension module wrapped using `pybind11`_.\n\n"
- "It should not be necessary to access classes and functions in this extension module "
- "directly. Instead, :func:`~contourpy.contour_generator` should be used to create "
- ":class:`~.ContourGenerator` objects, and the enums "
- "(:class:`~.FillType`, :class:`~.LineType` and "
- ":class:`~.ZInterp`) and :func:`.max_threads` function are all available "
- "in the :mod:`contourpy` module.";
-
- m.attr("__version__") = MACRO_STRINGIFY(CONTOURPY_VERSION);
-
- // asserts are enabled if NDEBUG is 0.
- m.attr("NDEBUG") =
-#ifdef NDEBUG
- 1;
-#else
- 0;
-#endif
-
- py::enum_<contourpy::FillType>(m, "FillType",
- "Enum used for ``fill_type`` keyword argument in :func:`~.contour_generator`.\n\n"
- "This controls the format of filled contour data returned from "
- ":meth:`~.ContourGenerator.filled`.")
- .value("OuterCode", contourpy::FillType::OuterCode)
- .value("OuterOffset", contourpy::FillType::OuterOffset)
- .value("ChunkCombinedCode", contourpy::FillType::ChunkCombinedCode)
- .value("ChunkCombinedOffset", contourpy::FillType::ChunkCombinedOffset)
- .value("ChunkCombinedCodeOffset", contourpy::FillType::ChunkCombinedCodeOffset)
- .value("ChunkCombinedOffsetOffset", contourpy::FillType::ChunkCombinedOffsetOffset)
- .export_values();
-
- py::enum_<contourpy::LineType>(m, "LineType",
- "Enum used for ``line_type`` keyword argument in :func:`~.contour_generator`.\n\n"
- "This controls the format of contour line data returned from "
- ":meth:`~.ContourGenerator.lines`.\n\n"
- "``LineType.ChunkCombinedNan`` added in version 1.2.0")
- .value("Separate", contourpy::LineType::Separate)
- .value("SeparateCode", contourpy::LineType::SeparateCode)
- .value("ChunkCombinedCode", contourpy::LineType::ChunkCombinedCode)
- .value("ChunkCombinedOffset", contourpy::LineType::ChunkCombinedOffset)
- .value("ChunkCombinedNan", contourpy::LineType::ChunkCombinedNan)
- .export_values();
-
- py::enum_<contourpy::ZInterp>(m, "ZInterp",
- "Enum used for ``z_interp`` keyword argument in :func:`~.contour_generator`\n\n"
- "This controls the interpolation used on ``z`` values to determine where contour lines "
- "intersect the edges of grid quads, and ``z`` values at quad centres.")
- .value("Linear", contourpy::ZInterp::Linear)
- .value("Log", contourpy::ZInterp::Log)
- .export_values();
-
- m.def("max_threads", &contourpy::Util::get_max_threads,
- "Return the maximum number of threads, obtained from "
- "``std::thread::hardware_concurrency()``.\n\n"
- "This is the number of threads used by a multithreaded ContourGenerator if the kwarg "
- "``threads=0`` is passed to :func:`~.contour_generator`.");
-
- const char* chunk_count_doc = "Return tuple of (y, x) chunk counts.";
- const char* chunk_size_doc = "Return tuple of (y, x) chunk sizes.";
- const char* corner_mask_doc = "Return whether ``corner_mask`` is set or not.";
- const char* create_contour_doc =
- "Synonym for :meth:`~.ContourGenerator.lines` to provide backward compatibility "
- "with Matplotlib.";
- const char* create_filled_contour_doc =
- "Synonym for :meth:`~.ContourGenerator.filled` to provide backward compatibility "
- "with Matplotlib.";
- const char* default_fill_type_doc =
- "Return the default :class:`~.FillType` used by this algorithm.";
- const char* default_line_type_doc =
- "Return the default :class:`~.LineType` used by this algorithm.";
- const char* fill_type_doc = "Return the :class:`~.FillType`.";
- const char* filled_doc =
- "Calculate and return filled contours between two levels.\n\n"
- "Args:\n"
- " lower_level (float): Lower z-level of the filled contours, cannot be ``np.nan``.\n"
- " upper_level (float): Upper z-level of the filled contours, cannot be ``np.nan``.\n"
- "Return:\n"
- " Filled contour polygons as nested sequences of numpy arrays. The exact format is "
- "determined by the :attr:`~.ContourGenerator.fill_type` used by the ``ContourGenerator`` "
- "and the options are explained at :ref:`fill_type`.\n\n"
- "Raises a ``ValueError`` if ``lower_level >= upper_level`` or if\n"
- "``lower_level`` or ``upper_level`` are ``np.nan``.\n\n"
- "To return filled contours below a ``level`` use ``filled(-np.inf, level)``.\n"
- "To return filled contours above a ``level`` use ``filled(level, np.inf)``";
- const char* line_type_doc = "Return the :class:`~.LineType`.";
- const char* lines_doc =
- "Calculate and return contour lines at a particular level.\n\n"
- "Args:\n"
- " level (float): z-level to calculate contours at.\n\n"
- "Return:\n"
- " Contour lines (open line strips and closed line loops) as nested sequences of "
- "numpy arrays. The exact format is determined by the :attr:`~.ContourGenerator.line_type` "
- "used by the ``ContourGenerator`` and the options are explained at :ref:`line_type`.\n\n"
- "``level`` may be ``np.nan``, ``np.inf`` or ``-np.inf``; they all return the same result "
- "which is an empty line set.";
- const char* multi_filled_doc =
- "Calculate and return filled contours between multiple pairs of adjacent levels.\n\n"
- "Args:\n"
- " levels (array-like of floats): z-levels to calculate filled contours between. "
- "There must be at least 2 levels, they cannot be NaN, and each level must be larger than "
- "the previous level.\n\n"
- "Return:\n"
- " List of filled contours, one per pair of levels. The length of the returned list is "
- "one less than ``len(levels)``. The format of returned contours is determined by the "
- "``fill_type`` used by the ``ContourGenerator`` and the options are explained at "
- ":ref:`fill_type`.\n\n"
- "Raises a ``ValueError`` if ``levels`` are not increasing, or contain a NaN, or there are "
- "fewer than 2 ``levels``.\n\n"
- "Calling:\n\n"
- ".. code-block:: python\n\n"
- " ret = cont_gen.multi_filled(levels)\n\n"
- "is equivalent to:\n\n"
- ".. code-block:: python\n\n"
- " ret = [cont_gen.filled(lower, upper) for lower, upper in zip(levels[:-1], levels[1:])]\n\n"
- ".. versionadded:: 1.3.0";
- const char* multi_lines_doc =
- "Calculate and return contour lines at multiple levels.\n\n"
- "Args:\n"
- " levels (array-like of floats): z-levels to calculate contours at.\n\n"
- "Return:\n"
- " List of contour lines, one per level. The format of returned lines is determined by "
- "the ``line_type`` used by the ``ContourGenerator`` and the options are explained at "
- ":ref:`line_type`.\n\n"
- "Calling:\n\n"
- ".. code-block:: python\n\n"
- " ret = cont_gen.multi_lines(levels)\n\n"
- "is equivalent to:\n\n"
- ".. code-block:: python\n\n"
- " ret = [cont_gen.lines(level) for level in levels]\n\n"
- ".. versionadded:: 1.3.0";
- const char* quad_as_tri_doc = "Return whether ``quad_as_tri`` is set or not.";
- const char* supports_corner_mask_doc =
- "Return whether this algorithm supports ``corner_mask``.";
- const char* supports_fill_type_doc =
- "Return whether this algorithm supports a particular :class:`~.FillType`.";
- const char* supports_line_type_doc =
- "Return whether this algorithm supports a particular :class:`~.LineType`.";
- const char* supports_quad_as_tri_doc =
- "Return whether this algorithm supports ``quad_as_tri``.";
- const char* supports_threads_doc =
- "Return whether this algorithm supports the use of threads.";
- const char* supports_z_interp_doc =
- "Return whether this algorithm supports ``z_interp`` values other than ``ZInterp.Linear`` "
- "which all support.";
- const char* thread_count_doc = "Return the number of threads used.";
- const char* z_interp_doc = "Return the ``ZInterp``.";
-
- py::class_<contourpy::ContourGenerator>(m, "ContourGenerator",
- "Abstract base class for contour generator classes, defining the interface that they all "
- "implement.")
- .def("create_contour", &contourpy::ContourGenerator::lines, create_contour_doc, "level"_a)
- .def("create_filled_contour", &contourpy::ContourGenerator::filled,
- create_filled_contour_doc, "lower_level"_a, "upper_level"_a)
- .def("filled", &contourpy::ContourGenerator::filled, filled_doc,
- "lower_level"_a, "upper_level"_a)
- .def("lines", &contourpy::ContourGenerator::lines, lines_doc, "level"_a)
- .def("multi_filled", &contourpy::ContourGenerator::multi_filled, multi_filled_doc,
- "levels"_a)
- .def("multi_lines", &contourpy::ContourGenerator::multi_lines, multi_lines_doc, "levels"_a)
- .def_property_readonly(
- "chunk_count", [](py::object /* self */) {return py::make_tuple(1, 1);},
- chunk_count_doc)
- .def_property_readonly(
- "chunk_size", [](py::object /* self */) {return py::make_tuple(1, 1);}, chunk_size_doc)
- .def_property_readonly(
- "corner_mask", [](py::object /* self */) {return false;}, corner_mask_doc)
- .def_property_readonly(
- "fill_type", [](py::object /* self */) {return contourpy::FillType::OuterOffset;},
- fill_type_doc)
- .def_property_readonly(
- "line_type", [](py::object /* self */) {return contourpy::LineType::Separate;},
- line_type_doc)
- .def_property_readonly(
- "quad_as_tri", [](py::object /* self */) {return false;}, quad_as_tri_doc)
- .def_property_readonly(
- "thread_count", [](py::object /* self */) {return 1;}, thread_count_doc)
- .def_property_readonly(
- "z_interp", [](py::object /* self */) {return contourpy::ZInterp::Linear;},
- z_interp_doc)
- .def_property_readonly_static(
- "default_fill_type",
- [](py::object /* self */) {return contourpy::FillType::OuterOffset;},
- default_fill_type_doc)
- .def_property_readonly_static(
- "default_line_type", [](py::object /* self */) {return contourpy::LineType::Separate;},
- default_line_type_doc)
- .def_static("supports_corner_mask", []() {return false;}, supports_corner_mask_doc)
- .def_static(
- "supports_fill_type", [](contourpy::FillType /* fill_type */) {return false;},
- "fill_type"_a, supports_fill_type_doc)
- .def_static(
- "supports_line_type", [](contourpy::LineType /* line_type */) {return false;},
- "line_type"_a, supports_line_type_doc)
- .def_static("supports_quad_as_tri", []() {return false;}, supports_quad_as_tri_doc)
- .def_static("supports_threads", []() {return false;}, supports_threads_doc)
- .def_static("supports_z_interp", []() {return false;}, supports_z_interp_doc);
-
- py::class_<contourpy::Mpl2005ContourGenerator, contourpy::ContourGenerator>(
- m, "Mpl2005ContourGenerator",
- "ContourGenerator corresponding to ``name=\"mpl2005\"``.\n\n"
- "This is the original 2005 Matplotlib algorithm. "
- "Does not support any of ``corner_mask``, ``quad_as_tri``, ``threads`` or ``z_interp``. "
- "Only supports ``line_type=LineType.SeparateCode`` and ``fill_type=FillType.OuterCode``. "
- "Only supports chunking for filled contours, not contour lines.\n\n"
- ".. warning::\n"
- " This algorithm is in ``contourpy`` for historic comparison. No new features or bug "
- "fixes will be added to it, except for security-related bug fixes.")
- .def(py::init<const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::MaskArray&,
- contourpy::index_t,
- contourpy::index_t>(),
- "x"_a, "y"_a, "z"_a, "mask"_a, py::kw_only(), "x_chunk_size"_a = 0,
- "y_chunk_size"_a = 0)
- .def_property_readonly(
- "chunk_count", &contourpy::Mpl2005ContourGenerator::get_chunk_count, chunk_count_doc)
- .def_property_readonly(
- "chunk_size", &contourpy::Mpl2005ContourGenerator::get_chunk_size, chunk_size_doc)
- .def_property_readonly(
- "fill_type", [](py::object /* self */) {return mpl20xx_fill_type;}, fill_type_doc)
- .def_property_readonly(
- "line_type", [](py::object /* self */) {return mpl20xx_line_type;}, line_type_doc)
- .def_property_readonly_static(
- "default_fill_type", [](py::object /* self */) {return mpl20xx_fill_type;},
- default_fill_type_doc)
- .def_property_readonly_static(
- "default_line_type", [](py::object /* self */) {return mpl20xx_line_type;},
- default_line_type_doc)
- .def_static(
- "supports_fill_type",
- [](contourpy::FillType fill_type) {return fill_type == mpl20xx_fill_type;},
- supports_fill_type_doc)
- .def_static(
- "supports_line_type",
- [](contourpy::LineType line_type) {return line_type == mpl20xx_line_type;},
- supports_line_type_doc);
-
- py::class_<contourpy::mpl2014::Mpl2014ContourGenerator, contourpy::ContourGenerator>(
- m, "Mpl2014ContourGenerator",
- "ContourGenerator corresponding to ``name=\"mpl2014\"``.\n\n"
- "This is the 2014 Matplotlib algorithm, a replacement of the original 2005 algorithm that "
- "added ``corner_mask`` and made the code more maintainable. "
- "Only supports ``corner_mask``, does not support ``quad_as_tri``, ``threads`` or "
- "``z_interp``. \n"
- "Only supports ``line_type=LineType.SeparateCode`` and "
- "``fill_type=FillType.OuterCode``.\n\n"
- ".. warning::\n"
- " This algorithm is in ``contourpy`` for historic comparison. No new features or bug "
- "fixes will be added to it, except for security-related bug fixes.")
- .def(py::init<const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::MaskArray&,
- bool,
- contourpy::index_t,
- contourpy::index_t>(),
- "x"_a, "y"_a, "z"_a, "mask"_a, py::kw_only(), "corner_mask"_a, "x_chunk_size"_a = 0,
- "y_chunk_size"_a = 0)
- .def_property_readonly(
- "chunk_count", &contourpy::mpl2014::Mpl2014ContourGenerator::get_chunk_count,
- chunk_count_doc)
- .def_property_readonly(
- "chunk_size", &contourpy::mpl2014::Mpl2014ContourGenerator::get_chunk_size,
- chunk_size_doc)
- .def_property_readonly(
- "corner_mask", &contourpy::mpl2014::Mpl2014ContourGenerator::get_corner_mask,
- corner_mask_doc)
- .def_property_readonly(
- "fill_type", [](py::object /* self */) {return mpl20xx_fill_type;}, fill_type_doc)
- .def_property_readonly(
- "line_type", [](py::object /* self */) {return mpl20xx_line_type;}, line_type_doc)
- .def_property_readonly_static(
- "default_fill_type", [](py::object /* self */) {return mpl20xx_fill_type;},
- default_fill_type_doc)
- .def_property_readonly_static(
- "default_line_type", [](py::object /* self */) {return mpl20xx_line_type;},
- default_line_type_doc)
- .def_static("supports_corner_mask", []() {return true;}, supports_corner_mask_doc)
- .def_static(
- "supports_fill_type",
- [](contourpy::FillType fill_type) {return fill_type == mpl20xx_fill_type;},
- supports_fill_type_doc)
- .def_static(
- "supports_line_type",
- [](contourpy::LineType line_type) {return line_type == mpl20xx_line_type;},
- supports_line_type_doc);
-
- py::class_<contourpy::SerialContourGenerator, contourpy::ContourGenerator>(
- m, "SerialContourGenerator",
- "ContourGenerator corresponding to ``name=\"serial\"``, the default algorithm for "
- "``contourpy``.\n\n"
- "Supports ``corner_mask``, ``quad_as_tri`` and ``z_interp`` but not ``threads``. "
- "Supports all options for ``line_type`` and ``fill_type``.")
- .def(py::init<const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::MaskArray&,
- bool,
- contourpy::LineType,
- contourpy::FillType,
- bool,
- contourpy::ZInterp,
- contourpy::index_t,
- contourpy::index_t>(),
- "x"_a, "y"_a, "z"_a, "mask"_a, py::kw_only(), "corner_mask"_a, "line_type"_a,
- "fill_type"_a, "quad_as_tri"_a, "z_interp"_a, "x_chunk_size"_a = 0,
- "y_chunk_size"_a = 0)
- .def("_write_cache", &contourpy::SerialContourGenerator::write_cache)
- .def_property_readonly(
- "chunk_count", &contourpy::SerialContourGenerator::get_chunk_count, chunk_count_doc)
- .def_property_readonly(
- "chunk_size", &contourpy::SerialContourGenerator::get_chunk_size, chunk_size_doc)
- .def_property_readonly(
- "corner_mask", &contourpy::SerialContourGenerator::get_corner_mask, corner_mask_doc)
- .def_property_readonly(
- "fill_type", &contourpy::SerialContourGenerator::get_fill_type, fill_type_doc)
- .def_property_readonly(
- "line_type", &contourpy::SerialContourGenerator::get_line_type, line_type_doc)
- .def_property_readonly(
- "quad_as_tri", &contourpy::SerialContourGenerator::get_quad_as_tri, quad_as_tri_doc)
- .def_property_readonly(
- "z_interp", &contourpy::SerialContourGenerator::get_z_interp, z_interp_doc)
- .def_property_readonly_static(
- "default_fill_type",
- [](py::object /* self */) {return contourpy::SerialContourGenerator::default_fill_type();},
- default_fill_type_doc)
- .def_property_readonly_static("default_line_type",
- [](py::object /* self */) {return contourpy::SerialContourGenerator::default_line_type();},
- default_line_type_doc)
- .def_static("supports_corner_mask", []() {return true;}, supports_corner_mask_doc)
- .def_static(
- "supports_fill_type", &contourpy::SerialContourGenerator::supports_fill_type,
- supports_fill_type_doc)
- .def_static(
- "supports_line_type", &contourpy::SerialContourGenerator::supports_line_type,
- supports_line_type_doc)
- .def_static("supports_quad_as_tri", []() {return true;}, supports_quad_as_tri_doc)
- .def_static("supports_z_interp", []() {return true;}, supports_z_interp_doc);
-
- py::class_<contourpy::ThreadedContourGenerator, contourpy::ContourGenerator>(
- m, "ThreadedContourGenerator",
- "ContourGenerator corresponding to ``name=\"threaded\"``, the multithreaded version of "
- ":class:`~.SerialContourGenerator`.\n\n"
- "Supports ``corner_mask``, ``quad_as_tri`` and ``z_interp`` and ``threads``. "
- "Supports all options for ``line_type`` and ``fill_type``.")
- .def(py::init<const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::CoordinateArray&,
- const contourpy::MaskArray&,
- bool,
- contourpy::LineType,
- contourpy::FillType,
- bool,
- contourpy::ZInterp,
- contourpy::index_t,
- contourpy::index_t,
- contourpy::index_t>(),
- "x"_a, "y"_a, "z"_a, "mask"_a, py::kw_only(), "corner_mask"_a, "line_type"_a,
- "fill_type"_a, "quad_as_tri"_a, "z_interp"_a, "x_chunk_size"_a = 0,
- "y_chunk_size"_a = 0, "thread_count"_a = 0)
- .def("_write_cache", &contourpy::ThreadedContourGenerator::write_cache)
- .def_property_readonly(
- "chunk_count", &contourpy::ThreadedContourGenerator::get_chunk_count, chunk_count_doc)
- .def_property_readonly(
- "chunk_size", &contourpy::ThreadedContourGenerator::get_chunk_size, chunk_size_doc)
- .def_property_readonly(
- "corner_mask", &contourpy::ThreadedContourGenerator::get_corner_mask, corner_mask_doc)
- .def_property_readonly(
- "fill_type", &contourpy::ThreadedContourGenerator::get_fill_type, fill_type_doc)
- .def_property_readonly(
- "line_type", &contourpy::ThreadedContourGenerator::get_line_type, line_type_doc)
- .def_property_readonly(
- "quad_as_tri", &contourpy::ThreadedContourGenerator::get_quad_as_tri, quad_as_tri_doc)
- .def_property_readonly(
- "thread_count", &contourpy::ThreadedContourGenerator::get_thread_count,
- thread_count_doc)
- .def_property_readonly(
- "z_interp", &contourpy::ThreadedContourGenerator::get_z_interp, z_interp_doc)
- .def_property_readonly_static(
- "default_fill_type",
- [](py::object /* self */) {return contourpy::ThreadedContourGenerator::default_fill_type();},
- default_fill_type_doc)
- .def_property_readonly_static(
- "default_line_type",
- [](py::object /* self */) {return contourpy::ThreadedContourGenerator::default_line_type();},
- default_line_type_doc)
- .def_static("supports_corner_mask", []() {return true;}, supports_corner_mask_doc)
- .def_static(
- "supports_fill_type", &contourpy::ThreadedContourGenerator::supports_fill_type,
- supports_fill_type_doc)
- .def_static(
- "supports_line_type", &contourpy::ThreadedContourGenerator::supports_line_type,
- supports_line_type_doc)
- .def_static("supports_quad_as_tri", []() {return true;}, supports_quad_as_tri_doc)
- .def_static("supports_threads", []() {return true;}, supports_threads_doc)
- .def_static("supports_z_interp", []() {return true;}, supports_z_interp_doc);
-}
diff --git a/contrib/python/contourpy/src/z_interp.cpp b/contrib/python/contourpy/src/z_interp.cpp
deleted file mode 100644
index 92811ad0d02..00000000000
--- a/contrib/python/contourpy/src/z_interp.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include "z_interp.h"
-#include <iostream>
-
-namespace contourpy {
-
-std::ostream &operator<<(std::ostream &os, const ZInterp& z_interp)
-{
- switch (z_interp) {
- case ZInterp::Linear:
- os << "Linear";
- break;
- case ZInterp::Log:
- os << "Log";
- break;
- }
- return os;
-}
-
-} // namespace contourpy
diff --git a/contrib/python/contourpy/src/z_interp.h b/contrib/python/contourpy/src/z_interp.h
deleted file mode 100644
index 76a7550be62..00000000000
--- a/contrib/python/contourpy/src/z_interp.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef CONTOURPY_Z_INTERP_H
-#define CONTOURPY_Z_INTERP_H
-
-#include <iosfwd>
-#include <string>
-
-namespace contourpy {
-
-// Enum for type of interpolation used to find intersection of contour lines
-// with grid cell edges.
-
-// C++11 scoped enum, must be fully qualified to use.
-enum class ZInterp
-{
- Linear = 1,
- Log = 2
-};
-
-std::ostream &operator<<(std::ostream &os, const ZInterp& z_interp);
-
-} // namespace contourpy
-
-#endif // CONTOURPY_ZINTERP_H