5050 * The problem is that two disjoint curves cut through a saddle zone
5151 * (I reject the alternative of connecting the opposite points to make
5252 * a single self-intersecting curve, since those make ugly contour plots
53- * -- I've tried it). The real problem with saddle zones is that you
54- * need to communicate the connectivity decision you make back to the
55- * calling routine, since for the next contour level, we need to tell
56- * the contour tracer to make the same decision as on the previous
57- * level. The input/output triangulation array is the solution to this
58- * nasty problem.
53+ * -- I've tried it). The solution is to determine the z value of the
54+ * centre of the zone, which is the mean of the z values of the four
55+ * corner points. If the centre z is higher than the contour level of
56+ * interest and you are moving along the line with higher values on the
57+ * left, turn right to leave the saddle zone. If the centre z is lower
58+ * than the contour level turn left. Whether the centre z is higher
59+ * than the 1 or 2 contour levels is stored in the saddle array so that
60+ * it does not need to be recalculated in subsequent passes.
5961 *
6062 * Another complicating factor is that there may be logical holes in
6163 * the mesh -- zones which do not exist. We want our contours to stop
175177 * or not, z value 0, 1, or 2 -- is kept in a mesh sized data array */
176178typedef short Cdata ;
177179
180+ /* information to decide on correct contour direction in saddle zones
181+ * is stored in a mesh sized array. Only those entries corresponding
182+ * to saddle zones have nonzero values in this array. */
183+ typedef char Saddle ;
184+
178185/* here is the minimum structure required to tell where we are in the
179186 * mesh sized data array */
180187typedef struct Csite Csite ;
@@ -189,8 +196,8 @@ struct Csite
189196 long count ; /* count of start markers visited */
190197 double zlevel [2 ]; /* contour levels, zlevel[1]<=zlevel[0]
191198 * signals single level case */
192- short * triangle ; /* triangulation array for the mesh */
193- char * reg ; /* region array for the mesh (was int) */
199+ Saddle * saddle ; /* saddle zone information for the mesh */
200+ char * reg ; /* region array for the mesh (was int) */
194201 Cdata * data ; /* added by EF */
195202 long edge0 , left0 ; /* starting site on this curve for closure */
196203 int level0 ; /* starting level for closure */
@@ -225,8 +232,6 @@ void print_Csite(Csite *Csite)
225232 printf ("\n" );
226233}
227234
228- /* triangle only takes values of -1, 0, 1, so it could be a signed char. */
229- /* most or all of the longs probably could be converted to ints with no loss */
230235
231236/* the Cdata array consists of the following bits:
232237 * Z_VALUE (2 bits) 0, 1, or 2 function value at point
@@ -243,6 +248,7 @@ void print_Csite(Csite *Csite)
243248 * OPEN_END marks an i-edge start point whose other endpoint is
244249 * on a boundary for the single level case
245250 * ALL_DONE marks final start point
251+ * SLIT_DN_VISITED this slit downstroke hasn't/has been visited in pass 2
246252 */
247253#define Z_VALUE 0x0003
248254#define ZONE_EX 0x0004
@@ -257,6 +263,7 @@ void print_Csite(Csite *Csite)
257263#define SLIT_DN 0x0800
258264#define OPEN_END 0x1000
259265#define ALL_DONE 0x2000
266+ #define SLIT_DN_VISITED 0x4000
260267
261268/* some helpful macros to find points relative to a given directed
262269 * edge -- points are designated 0, 1, 2, 3 CCW around zone with 0 and
@@ -272,6 +279,15 @@ void print_Csite(Csite *Csite)
272279enum {kind_zone , kind_edge1 , kind_edge2 ,
273280 kind_slit_up , kind_slit_down , kind_start_slit = 16 } point_kinds ;
274281
282+ /* Saddle zone array consists of the following bits:
283+ * SADDLE_SET whether zone's saddle data has been set.
284+ * SADDLE_GT0 whether z of centre of zone is higher than site->level[0].
285+ * SADDLE_GT1 whether z of centre of zone is higher than site->level[1].
286+ */
287+ #define SADDLE_SET 0x01
288+ #define SADDLE_GT0 0x02
289+ #define SADDLE_GT1 0x04
290+
275291/* ------------------------------------------------------------------------ */
276292
277293/* these actually mark points */
@@ -313,18 +329,17 @@ zone_crosser (Csite * site, int level, int pass2)
313329 long left0 = site -> left0 ;
314330 int level0 = site -> level0 == level ;
315331 int two_levels = site -> zlevel [1 ] > site -> zlevel [0 ];
316- short * triangle = site -> triangle ;
332+ Saddle * saddle = site -> saddle ;
317333
318334 const double * x = pass2 ? site -> x : 0 ;
319335 const double * y = pass2 ? site -> y : 0 ;
320- const double * z = pass2 ? site -> z : 0 ;
321- double zlevel = pass2 ? site -> zlevel [level ] : 0.0 ;
336+ const double * z = site -> z ;
337+ double zlevel = site -> zlevel [level ];
322338 double * xcp = pass2 ? site -> xcp : 0 ;
323339 double * ycp = pass2 ? site -> ycp : 0 ;
324340 short * kcp = pass2 ? site -> kcp : 0 ;
325341
326342 int z0 , z1 , z2 , z3 ;
327- int keep_left = 0 ; /* flag to try to minimize curvature in saddles */
328343 int done = 0 ;
329344 int n_kind ;
330345
@@ -402,29 +417,28 @@ zone_crosser (Csite * site, int level, int pass2)
402417 {
403418 if (z1 == z3 )
404419 {
405- /* this is a saddle zone, need triangle to decide
406- * -- set triangle if not already decided for this zone */
420+ /* this is a saddle zone, determine whether to turn left or
421+ * right depending on height of centre of zone relative to
422+ * contour level. Set saddle[zone] if not already decided. */
407423 long zone = edge + (left > 0 ? left : 0 );
408- if (triangle )
424+ if (!( saddle [ zone ] & SADDLE_SET ) )
409425 {
410- if (!triangle [zone ])
411- {
412- if (keep_left )
413- triangle [zone ] = jedge ? -1 : 1 ;
414- else
415- triangle [zone ] = jedge ? 1 : -1 ;
416- }
417- if (triangle [zone ] > 0 ? !jedge : jedge )
418- goto bkwd ;
419- }
420- else
421- {
422- if (keep_left )
423- goto bkwd ;
426+ saddle [zone ] = SADDLE_SET ;
427+ double zcentre = (z [p0 ] + z [p0 + left ] + z [p1 ] + z [p1 + left ])/4.0 ;
428+ if (zcentre > site -> zlevel [0 ])
429+ saddle [zone ] |=
430+ (two_levels && zcentre > site -> zlevel [1 ])
431+ ? SADDLE_GT0 | SADDLE_GT1 : SADDLE_GT0 ;
424432 }
433+
434+ int turnRight = level == 2 ? (saddle [zone ] & SADDLE_GT1 )
435+ : (saddle [zone ] & SADDLE_GT0 );
436+ if (z1 ^ (level == 2 ))
437+ turnRight = !turnRight ;
438+ if (!turnRight )
439+ goto bkwd ;
425440 }
426441 /* bend forward (right along curve) */
427- keep_left = 1 ;
428442 jedge = !jedge ;
429443 edge = p1 + (left > 0 ? left : 0 );
430444 {
@@ -437,7 +451,6 @@ zone_crosser (Csite * site, int level, int pass2)
437451 {
438452 bkwd :
439453 /* bend backward (left along curve) */
440- keep_left = 0 ;
441454 jedge = !jedge ;
442455 edge = p0 + (left > 0 ? left : 0 );
443456 {
@@ -590,17 +603,27 @@ edge_walker (Csite * site, int pass2)
590603 if (n_kind ) kcp [n_kind ] += kind_start_slit ;
591604 return slit_cutter (site , 0 , pass2 );
592605 }
606+ if (fwd < 0 && level0 && left < 0 )
607+ {
608+ if (n_kind ) kcp [n_kind ] += kind_start_slit ;
609+ return slit_cutter (site , 0 , pass2 );
610+ }
593611 return 3 ;
594612 }
595613 else if (pass2 )
596614 {
597615 if (heads_up || (fwd < 0 && (data [edge ] & SLIT_DN )))
598616 {
599- site -> edge = edge ;
600- site -> left = left ;
601- site -> n = n + marked ;
602- if (n_kind ) kcp [n_kind ] += kind_start_slit ;
603- return slit_cutter (site , heads_up , pass2 );
617+ if (!heads_up && !(data [edge ] & SLIT_DN_VISITED ))
618+ data [edge ] |= SLIT_DN_VISITED ;
619+ else
620+ {
621+ site -> edge = edge ;
622+ site -> left = left ;
623+ site -> n = n + marked ;
624+ if (n_kind ) kcp [n_kind ] += kind_start_slit ;
625+ return slit_cutter (site , heads_up , pass2 );
626+ }
604627 }
605628 }
606629 else
@@ -1181,6 +1204,8 @@ data_init (Csite * site, long nchunk)
11811204 /* place immediate stop mark if nothing found */
11821205 if (!count )
11831206 data [0 ] |= ALL_DONE ;
1207+ else
1208+ for (i = 0 ; i < ijmax ; ++ i ) site -> saddle [i ] = 0 ;
11841209
11851210 /* initialize site */
11861211 site -> edge0 = site -> edge00 = site -> edge = 0 ;
@@ -1252,7 +1277,7 @@ cntr_new(void)
12521277 if (site == NULL ) return NULL ;
12531278 site -> data = NULL ;
12541279 site -> reg = NULL ;
1255- site -> triangle = NULL ;
1280+ site -> saddle = NULL ;
12561281 site -> xcp = NULL ;
12571282 site -> ycp = NULL ;
12581283 site -> kcp = NULL ;
@@ -1268,7 +1293,6 @@ cntr_init(Csite *site, long iMax, long jMax, double *x, double *y,
12681293{
12691294 long ijmax = iMax * jMax ;
12701295 long nreg = iMax * jMax + iMax + 1 ;
1271- long i ;
12721296
12731297 site -> imax = iMax ;
12741298 site -> jmax = jMax ;
@@ -1278,21 +1302,20 @@ cntr_init(Csite *site, long iMax, long jMax, double *x, double *y,
12781302 PyMem_Free (site );
12791303 return -1 ;
12801304 }
1281- site -> triangle = (short * ) PyMem_Malloc (sizeof (short ) * ijmax );
1282- if (site -> triangle == NULL )
1305+ site -> saddle = (Saddle * ) PyMem_Malloc (sizeof (Saddle ) * ijmax );
1306+ if (site -> saddle == NULL )
12831307 {
12841308 PyMem_Free (site -> data );
12851309 PyMem_Free (site );
12861310 return -1 ;
12871311 }
1288- for (i = 0 ; i < ijmax ; i ++ ) site -> triangle [i ] = 0 ;
12891312 site -> reg = NULL ;
12901313 if (mask != NULL )
12911314 {
12921315 site -> reg = (char * ) PyMem_Malloc (sizeof (char ) * nreg );
12931316 if (site -> reg == NULL )
12941317 {
1295- PyMem_Free (site -> triangle );
1318+ PyMem_Free (site -> saddle );
12961319 PyMem_Free (site -> data );
12971320 PyMem_Free (site );
12981321 return -1 ;
@@ -1311,7 +1334,7 @@ cntr_init(Csite *site, long iMax, long jMax, double *x, double *y,
13111334
13121335void cntr_del (Csite * site )
13131336{
1314- PyMem_Free (site -> triangle );
1337+ PyMem_Free (site -> saddle );
13151338 PyMem_Free (site -> reg );
13161339 PyMem_Free (site -> data );
13171340 PyMem_Free (site );
0 commit comments