aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/contourpy/src/mpl2005_original.cpp
diff options
context:
space:
mode:
authorshumkovnd <shumkovnd@yandex-team.com>2023-11-10 14:39:34 +0300
committershumkovnd <shumkovnd@yandex-team.com>2023-11-10 16:42:24 +0300
commit77eb2d3fdcec5c978c64e025ced2764c57c00285 (patch)
treec51edb0748ca8d4a08d7c7323312c27ba1a8b79a /contrib/python/contourpy/src/mpl2005_original.cpp
parentdd6d20cadb65582270ac23f4b3b14ae189704b9d (diff)
downloadydb-77eb2d3fdcec5c978c64e025ced2764c57c00285.tar.gz
KIKIMR-19287: add task_stats_drawing script
Diffstat (limited to 'contrib/python/contourpy/src/mpl2005_original.cpp')
-rw-r--r--contrib/python/contourpy/src/mpl2005_original.cpp1526
1 files changed, 1526 insertions, 0 deletions
diff --git a/contrib/python/contourpy/src/mpl2005_original.cpp b/contrib/python/contourpy/src/mpl2005_original.cpp
new file mode 100644
index 0000000000..f2f0e8567c
--- /dev/null
+++ b/contrib/python/contourpy/src/mpl2005_original.cpp
@@ -0,0 +1,1526 @@
+/*
+ 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