mpl2005_original.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526
  1. /*
  2. cntr.c
  3. General purpose contour tracer for quadrilateral meshes.
  4. Handles single level contours, or region between a pair of levels.
  5. The routines that do all the work, as well as the explanatory
  6. comments, came from gcntr.c, part of the GIST package. The
  7. original mpl interface was also based on GIST. The present
  8. interface uses parts of the original, but places them in
  9. the entirely different framework of a Python type. It
  10. was written by following the Python "Extending and Embedding"
  11. tutorial.
  12. */
  13. #include "mpl2005_original.h"
  14. #include "mpl_kind_code.h"
  15. /* Note that all arrays in these routines are Fortran-style,
  16. in the sense that the "i" index varies fastest; the dimensions
  17. of the corresponding C array are z[jmax][imax] in the notation
  18. used here. We can identify i and j with the x and y dimensions,
  19. respectively.
  20. */
  21. /* What is a contour?
  22. *
  23. * Given a quadrilateral mesh (x,y), and values of a z at the points
  24. * of that mesh, we seek a set of polylines connecting points at a
  25. * particular value of z. Each point on such a contour curve lies
  26. * on an edge of the mesh, at a point linearly interpolated to the
  27. * contour level z0 between the given values of z at the endpoints
  28. * of the edge.
  29. *
  30. * Identifying these points is easy. Figuring out how to connect them
  31. * into a curve -- or possibly a set of disjoint curves -- is difficult.
  32. * Each disjoint curve may be either a closed circuit, or it may begin
  33. * and end on a mesh boundary.
  34. *
  35. * One of the problems with a quadrilateral mesh is that when the z
  36. * values at one pair of diagonally opposite points lie below z0, and
  37. * the values at the other diagonal pair of the same zone lie above z0,
  38. * all four edges of the zone are cut, and there is an ambiguity in
  39. * how we should connect the points. I call this a saddle zone.
  40. * The problem is that two disjoint curves cut through a saddle zone
  41. * (I reject the alternative of connecting the opposite points to make
  42. * a single self-intersecting curve, since those make ugly contour plots
  43. * -- I've tried it). The solution is to determine the z value of the
  44. * centre of the zone, which is the mean of the z values of the four
  45. * corner points. If the centre z is higher than the contour level of
  46. * interest and you are moving along the line with higher values on the
  47. * left, turn right to leave the saddle zone. If the centre z is lower
  48. * than the contour level turn left. Whether the centre z is higher
  49. * than the 1 or 2 contour levels is stored in the saddle array so that
  50. * it does not need to be recalculated in subsequent passes.
  51. *
  52. * Another complicating factor is that there may be logical holes in
  53. * the mesh -- zones which do not exist. We want our contours to stop
  54. * if they hit the edge of such a zone, just as if they'd hit the edge
  55. * of the whole mesh. The input region array addresses this issue.
  56. *
  57. * Yet another complication: We may want a list of closed polygons which
  58. * outline the region between two contour levels z0 and z1. These may
  59. * include sections of the mesh boundary (including edges of logical
  60. * holes defined by the region array), in addition to sections of the
  61. * contour curves at one or both levels. This introduces a huge
  62. * topological problem -- if one of the closed contours (possibly
  63. * including an interior logical hole in the mesh, but not any part of
  64. * the boundary of the whole mesh) encloses a region which is not
  65. * between z0 and z1, that curve must be connected by a slit (or "branch
  66. * cut") to the enclosing curve, so that the list of disjoint polygons
  67. * we return is each simply connected.
  68. *
  69. * Okay, one final stunning difficulty: For the two level case, no
  70. * individual polygon should have more than a few thousand sides, since
  71. * huge filled polygons place an inordinate load on rendering software,
  72. * which needs an amount of scratch space proportional to the number
  73. * of sides it needs to fill. So in the two level case, we want to
  74. * chunk the mesh into rectangular pieces of no more than, say, 30x30
  75. * zones, which keeps each returned polygon to less than a few thousand
  76. * sides (the worst case is very very bad -- you can easily write down
  77. * a function and two level values which produce a polygon that cuts
  78. * every edge of the mesh twice).
  79. */
  80. /*
  81. * Here is the numbering scheme for points, edges, and zones in
  82. * the mesh -- note that each ij corresponds to one point, one zone,
  83. * one i-edge (i=constant edge) and one j-edge (j=constant edge):
  84. *
  85. * (ij-1)-------(ij)-------(ij)
  86. * | |
  87. * | |
  88. * | |
  89. * (ij-1) (ij) (ij)
  90. * | |
  91. * | |
  92. * | |
  93. * (ij-iX-1)----(ij-iX)----(ij-iX)
  94. *
  95. * At each point, the function value is either 0, 1, or 2, depending
  96. * on whether it is below z0, between z0 and z1, or above z1.
  97. * Each zone either exists (1) or not (0).
  98. * From these three bits of data, all of the curve connectivity follows.
  99. *
  100. * The tracing algorithm is naturally edge-based: Either you are at a
  101. * point where a level cuts an edge, ready to step across a zone to
  102. * another edge, or you are drawing the edge itself, if it happens to
  103. * be a boundary with at least one section between z0 and z1.
  104. *
  105. * In either case, the edge is a directed edge -- either the zone
  106. * you are advancing into is to its left or right, or you are actually
  107. * drawing it. I always trace curves keeping the region between z0 and
  108. * z1 to the left of the curve. If I'm tracing a boundary, I'm always
  109. * moving CCW (counter clockwise) around the zone that exists. And if
  110. * I'm about to cross a zone, I'll make the direction of the edge I'm
  111. * sitting on be such that the zone I'm crossing is to its left.
  112. *
  113. * I start tracing each curve near its lower left corner (mesh oriented
  114. * as above), which is the first point I encounter scanning through the
  115. * mesh in order. When I figure the 012 z values and zonal existence,
  116. * I also mark the potential starting points: Each edge may harbor a
  117. * potential starting point corresponding to either direction, so there
  118. * are four start possibilities at each ij point. Only the following
  119. * possibilities need to be marked as potential starting edges:
  120. *
  121. * +-+-+-+
  122. * | | | |
  123. * A-0-C-+ One or both levels cut E and have z=1 above them, and
  124. * | EZ| | 0A is cut and either 0C is cut or CD is cut.
  125. * +-B-D-+ Or, one or both levels cut E and E is a boundary edge.
  126. * | | | | (and Z exists)
  127. * +-+-+-+
  128. *
  129. * +-+-+-+
  130. * | | | |
  131. * +-A-0-C One or both levels cut E and have z=1 below them, and
  132. * | |ZE | 0A is cut and either 0C is cut or CD is cut.
  133. * +-+-B-D Or, one or both levels cut E and E is a boundary edge.
  134. * | | | | (and Z exists)
  135. * +-+-+-+
  136. *
  137. * +-+-+-+
  138. * | | | |
  139. * +-+-+-+ E is a boundary edge, Z exists, at some point on E
  140. * | |Z| | lies between the levels.
  141. * +-+E+-+
  142. * | | | |
  143. * +-+-+-+
  144. *
  145. * +-+-+-+
  146. * | | | |
  147. * +-+E+-+ E is a boundary edge, Z exists, at some point on E
  148. * | |Z| | lies between the levels.
  149. * +-+-+-+
  150. * | | | |
  151. * +-+-+-+
  152. *
  153. * During the first tracing pass, the start mark is erased whenever
  154. * any non-starting edge is encountered, reducing the number of points
  155. * that need to be considered for the second pass. The first pass
  156. * makes the basic connectivity decisions. It figures out how many
  157. * disjoint curves there will be, and identifies slits for the two level
  158. * case or open contours for the single level case, and removes all but
  159. * the actual start markers. A second tracing pass can perform the
  160. * actual final trace.
  161. */
  162. /* ------------------------------------------------------------------------ */
  163. namespace contourpy {
  164. void print_Csite(Csite *Csite)
  165. {
  166. Cdata *data = Csite->data;
  167. int i, j, ij;
  168. int nd = Csite->imax * (Csite->jmax + 1) + 1;
  169. printf("zlevels: %8.2lg %8.2lg\n", Csite->zlevel[0], Csite->zlevel[1]);
  170. printf("edge %ld, left %ld, n %ld, count %ld, edge0 %ld, left0 %ld\n",
  171. Csite->edge, Csite->left, Csite->n, Csite->count,
  172. Csite->edge0, Csite->left0);
  173. printf(" level0 %d, edge00 %ld\n", Csite->level0, Csite->edge00);
  174. printf("%04x\n", data[nd-1]);
  175. for (j = Csite->jmax; j >= 0; j--)
  176. {
  177. for (i=0; i < Csite->imax; i++)
  178. {
  179. ij = i + j * Csite->imax;
  180. printf("%04x ", data[ij]);
  181. }
  182. printf("\n");
  183. }
  184. printf("\n");
  185. }
  186. /* the Cdata array consists of the following bits:
  187. * Z_VALUE (2 bits) 0, 1, or 2 function value at point
  188. * ZONE_EX 1 zone exists, 0 zone doesn't exist
  189. * I_BNDY this i-edge (i=constant edge) is a mesh boundary
  190. * J_BNDY this j-edge (i=constant edge) is a mesh boundary
  191. * I0_START this i-edge is a start point into zone to left
  192. * I1_START this i-edge is a start point into zone to right
  193. * J0_START this j-edge is a start point into zone below
  194. * J1_START this j-edge is a start point into zone above
  195. * START_ROW next start point is in current row (accelerates 2nd pass)
  196. * SLIT_UP marks this i-edge as the beginning of a slit upstroke
  197. * SLIT_DN marks this i-edge as the beginning of a slit downstroke
  198. * OPEN_END marks an i-edge start point whose other endpoint is
  199. * on a boundary for the single level case
  200. * ALL_DONE marks final start point
  201. * SLIT_DN_VISITED this slit downstroke hasn't/has been visited in pass 2
  202. */
  203. #define Z_VALUE 0x0003
  204. #define ZONE_EX 0x0004
  205. #define I_BNDY 0x0008
  206. #define J_BNDY 0x0010
  207. #define I0_START 0x0020
  208. #define I1_START 0x0040
  209. #define J0_START 0x0080
  210. #define J1_START 0x0100
  211. #define START_ROW 0x0200
  212. #define SLIT_UP 0x0400
  213. #define SLIT_DN 0x0800
  214. #define OPEN_END 0x1000
  215. #define ALL_DONE 0x2000
  216. #define SLIT_DN_VISITED 0x4000
  217. /* some helpful macros to find points relative to a given directed
  218. * edge -- points are designated 0, 1, 2, 3 CCW around zone with 0 and
  219. * 1 the endpoints of the current edge */
  220. #define FORWARD(left,ix) ((left)>0?((left)>1?1:-(ix)):((left)<-1?-1:(ix)))
  221. #define POINT0(edge,fwd) ((edge)-((fwd)>0?fwd:0))
  222. #define POINT1(edge,fwd) ((edge)+((fwd)<0?fwd:0))
  223. #define IS_JEDGE(edge,left) ((left)>0?((left)>1?1:0):((left)<-1?1:0))
  224. #define ANY_START (I0_START|I1_START|J0_START|J1_START)
  225. #define START_MARK(left) \
  226. ((left)>0?((left)>1?J1_START:I1_START):((left)<-1?J0_START:I0_START))
  227. enum {kind_zone, kind_edge1, kind_edge2,
  228. kind_slit_up, kind_slit_down, kind_start_slit=16};
  229. /* Saddle zone array consists of the following bits:
  230. * SADDLE_SET whether zone's saddle data has been set.
  231. * SADDLE_GT0 whether z of centre of zone is higher than site->level[0].
  232. * SADDLE_GT1 whether z of centre of zone is higher than site->level[1].
  233. */
  234. #define SADDLE_SET 0x01
  235. #define SADDLE_GT0 0x02
  236. #define SADDLE_GT1 0x04
  237. /* ------------------------------------------------------------------------ */
  238. /* these actually mark points */
  239. static int zone_crosser (Csite * site, int level, int pass2);
  240. static int edge_walker (Csite * site, int pass2);
  241. static int slit_cutter (Csite * site, int up, int pass2);
  242. /* this calls the first three to trace the next disjoint curve
  243. * -- return value is number of points on this curve, or
  244. * 0 if there are no more curves this pass
  245. * -(number of points) on first pass if:
  246. * this is two level case, and the curve closed on a hole
  247. * this is single level case, curve is open, and will start from
  248. * a different point on the second pass
  249. * -- in both cases, this curve will be combined with another
  250. * on the second pass */
  251. static long curve_tracer (Csite * site, int pass2);
  252. /* this initializes the data array for curve_tracer */
  253. static void data_init (Csite * site);
  254. /* ------------------------------------------------------------------------ */
  255. /* zone_crosser assumes you are sitting at a cut edge about to cross
  256. * the current zone. It always marks the initial point, crosses at
  257. * least one zone, and marks the final point. On non-boundary i-edges,
  258. * it is responsible for removing start markers on the first pass. */
  259. static int
  260. zone_crosser (Csite * site, int level, int pass2)
  261. {
  262. Cdata * data = site->data;
  263. long edge = site->edge;
  264. long left = site->left;
  265. long n = site->n;
  266. long fwd = FORWARD (left, site->imax);
  267. long p0, p1;
  268. int jedge = IS_JEDGE (edge, left);
  269. long edge0 = site->edge0;
  270. long left0 = site->left0;
  271. int level0 = site->level0 == level;
  272. int two_levels = site->zlevel[1] > site->zlevel[0];
  273. Saddle* saddle = site->saddle;
  274. const double *x = pass2 ? site->x : 0;
  275. const double *y = pass2 ? site->y : 0;
  276. const double *z = site->z;
  277. double zlevel = site->zlevel[level];
  278. double *xcp = pass2 ? site->xcp : 0;
  279. double *ycp = pass2 ? site->ycp : 0;
  280. short *kcp = pass2 ? site->kcp : 0;
  281. int z0, z1, z2, z3;
  282. int done = 0;
  283. int n_kind;
  284. if (level)
  285. level = 2;
  286. for (;;)
  287. {
  288. n_kind = 0;
  289. /* set edge endpoints */
  290. p0 = POINT0 (edge, fwd);
  291. p1 = POINT1 (edge, fwd);
  292. /* always mark cut on current edge */
  293. if (pass2)
  294. {
  295. /* second pass actually computes and stores the point */
  296. double zcp = (zlevel - z[p0]) / (z[p1] - z[p0]);
  297. xcp[n] = zcp * (x[p1] - x[p0]) + x[p0];
  298. ycp[n] = zcp * (y[p1] - y[p0]) + y[p0];
  299. kcp[n] = kind_zone;
  300. n_kind = n;
  301. }
  302. if (!done && !jedge)
  303. {
  304. if (n)
  305. {
  306. /* if this is not the first point on the curve, and we're
  307. * not done, and this is an i-edge, check several things */
  308. if (!two_levels && !pass2 && (data[edge] & OPEN_END))
  309. {
  310. /* reached an OPEN_END mark, skip the n++ */
  311. done = 4; /* same return value 4 used below */
  312. break;
  313. }
  314. /* check for curve closure -- if not, erase any start mark */
  315. if (edge == edge0 && left == left0)
  316. {
  317. /* may signal closure on a downstroke */
  318. if (level0)
  319. done = (!pass2 && two_levels && left < 0) ? 5 : 3;
  320. }
  321. else if (!pass2)
  322. {
  323. Cdata start =
  324. data[edge] & (fwd > 0 ? I0_START : I1_START);
  325. if (start)
  326. {
  327. data[edge] &= ~start;
  328. site->count--;
  329. }
  330. if (!two_levels)
  331. {
  332. start = data[edge] & (fwd > 0 ? I1_START : I0_START);
  333. if (start)
  334. {
  335. data[edge] &= ~start;
  336. site->count--;
  337. }
  338. }
  339. }
  340. }
  341. }
  342. n++;
  343. if (done)
  344. break;
  345. /* cross current zone to another cut edge */
  346. z0 = (data[p0] & Z_VALUE) != level; /* 1 if fill toward p0 */
  347. z1 = !z0; /* know level cuts edge */
  348. z2 = (data[p1 + left] & Z_VALUE) != level;
  349. z3 = (data[p0 + left] & Z_VALUE) != level;
  350. if (z0 == z2)
  351. {
  352. if (z1 == z3)
  353. {
  354. /* this is a saddle zone, determine whether to turn left or
  355. * right depending on height of centre of zone relative to
  356. * contour level. Set saddle[zone] if not already decided. */
  357. int turnRight;
  358. long zone = edge + (left > 0 ? left : 0);
  359. if (!(saddle[zone] & SADDLE_SET))
  360. {
  361. double zcentre;
  362. saddle[zone] = SADDLE_SET;
  363. zcentre = (z[p0] + z[p0+left] + z[p1] + z[p1+left])/4.0;
  364. if (zcentre > site->zlevel[0])
  365. saddle[zone] |=
  366. (two_levels && zcentre > site->zlevel[1])
  367. ? SADDLE_GT0 | SADDLE_GT1 : SADDLE_GT0;
  368. }
  369. turnRight = level == 2 ? (saddle[zone] & SADDLE_GT1)
  370. : (saddle[zone] & SADDLE_GT0);
  371. if (z1 ^ (level == 2))
  372. turnRight = !turnRight;
  373. if (!turnRight)
  374. goto bkwd;
  375. }
  376. /* bend forward (right along curve) */
  377. jedge = !jedge;
  378. edge = p1 + (left > 0 ? left : 0);
  379. {
  380. long tmp = fwd;
  381. fwd = -left;
  382. left = tmp;
  383. }
  384. }
  385. else if (z1 == z3)
  386. {
  387. bkwd:
  388. /* bend backward (left along curve) */
  389. jedge = !jedge;
  390. edge = p0 + (left > 0 ? left : 0);
  391. {
  392. long tmp = fwd;
  393. fwd = left;
  394. left = -tmp;
  395. }
  396. }
  397. else
  398. {
  399. /* straight across to opposite edge */
  400. edge += left;
  401. }
  402. /* after crossing zone, edge/left/fwd is oriented CCW relative to
  403. * the next zone, assuming we will step there */
  404. /* now that we've taken a step, check for the downstroke
  405. * of a slit on the second pass (upstroke checked above)
  406. * -- taking step first avoids a race condition */
  407. if (pass2 && two_levels && !jedge)
  408. {
  409. if (left > 0)
  410. {
  411. if (data[edge] & SLIT_UP)
  412. done = 6;
  413. }
  414. else
  415. {
  416. if (data[edge] & SLIT_DN)
  417. done = 5;
  418. }
  419. }
  420. if (!done)
  421. {
  422. /* finally, check if we are on a boundary */
  423. if (data[edge] & (jedge ? J_BNDY : I_BNDY))
  424. {
  425. done = two_levels ? 2 : 4;
  426. /* flip back into the zone that exists */
  427. left = -left;
  428. fwd = -fwd;
  429. if (!pass2 && (edge != edge0 || left != left0))
  430. {
  431. Cdata start = data[edge] & START_MARK (left);
  432. if (start)
  433. {
  434. data[edge] &= ~start;
  435. site->count--;
  436. }
  437. }
  438. }
  439. }
  440. }
  441. site->edge = edge;
  442. site->n = n;
  443. site->left = left;
  444. if (done <= 4)
  445. {
  446. return done;
  447. }
  448. if (pass2 && n_kind)
  449. {
  450. kcp[n_kind] += kind_start_slit;
  451. }
  452. return slit_cutter (site, done - 5, pass2);
  453. }
  454. /* edge_walker assumes that the current edge is being drawn CCW
  455. * around the current zone. Since only boundary edges are drawn
  456. * and we always walk around with the filled region to the left,
  457. * no edge is ever drawn CW. We attempt to advance to the next
  458. * edge on this boundary, but if current second endpoint is not
  459. * between the two contour levels, we exit back to zone_crosser.
  460. * Note that we may wind up marking no points.
  461. * -- edge_walker is never called for single level case */
  462. static int
  463. edge_walker (Csite * site, int pass2)
  464. {
  465. Cdata * data = site->data;
  466. long edge = site->edge;
  467. long left = site->left;
  468. long n = site->n;
  469. long fwd = FORWARD (left, site->imax);
  470. long p0 = POINT0 (edge, fwd);
  471. long p1 = POINT1 (edge, fwd);
  472. int jedge = IS_JEDGE (edge, left);
  473. long edge0 = site->edge0;
  474. long left0 = site->left0;
  475. int level0 = site->level0 == 2;
  476. int marked;
  477. int n_kind = 0;
  478. const double *x = pass2 ? site->x : 0;
  479. const double *y = pass2 ? site->y : 0;
  480. double *xcp = pass2 ? site->xcp : 0;
  481. double *ycp = pass2 ? site->ycp : 0;
  482. short *kcp = pass2 ? site->kcp : 0;
  483. int z0, z1, heads_up = 0;
  484. for (;;)
  485. {
  486. /* mark endpoint 0 only if value is 1 there, and this is a
  487. * two level task */
  488. z0 = data[p0] & Z_VALUE;
  489. z1 = data[p1] & Z_VALUE;
  490. marked = 0;
  491. n_kind = 0;
  492. if (z0 == 1)
  493. {
  494. /* mark current boundary point */
  495. if (pass2)
  496. {
  497. xcp[n] = x[p0];
  498. ycp[n] = y[p0];
  499. kcp[n] = kind_edge1;
  500. n_kind = n;
  501. }
  502. marked = 1;
  503. }
  504. else if (!n)
  505. {
  506. /* if this is the first point is not between the levels
  507. * must do the job of the zone_crosser and mark the first cut here,
  508. * so that it will be marked again by zone_crosser as it closes */
  509. if (pass2)
  510. {
  511. double zcp = site->zlevel[(z0 != 0)];
  512. zcp = (zcp - site->z[p0]) / (site->z[p1] - site->z[p0]);
  513. xcp[n] = zcp * (x[p1] - x[p0]) + x[p0];
  514. ycp[n] = zcp * (y[p1] - y[p0]) + y[p0];
  515. kcp[n] = kind_edge2;
  516. n_kind = n;
  517. }
  518. marked = 1;
  519. }
  520. if (n)
  521. {
  522. /* check for closure */
  523. if (level0 && edge == edge0 && left == left0)
  524. {
  525. site->edge = edge;
  526. site->left = left;
  527. site->n = n + marked;
  528. /* if the curve is closing on a hole, need to make a downslit */
  529. if (fwd < 0 && !(data[edge] & (jedge ? J_BNDY : I_BNDY)))
  530. {
  531. if (n_kind) kcp[n_kind] += kind_start_slit;
  532. return slit_cutter (site, 0, pass2);
  533. }
  534. if (fwd < 0 && level0 && left < 0)
  535. {
  536. /* remove J0_START from this boundary edge as boundary is
  537. * included by the upwards slit from contour line below. */
  538. data[edge] &= ~J0_START;
  539. if (n_kind) kcp[n_kind] += kind_start_slit;
  540. return slit_cutter (site, 0, pass2);
  541. }
  542. return 3;
  543. }
  544. else if (pass2)
  545. {
  546. if (heads_up || (fwd < 0 && (data[edge] & SLIT_DN)))
  547. {
  548. if (!heads_up && !(data[edge] & SLIT_DN_VISITED))
  549. data[edge] |= SLIT_DN_VISITED;
  550. else
  551. {
  552. site->edge = edge;
  553. site->left = left;
  554. site->n = n + marked;
  555. if (n_kind) kcp[n_kind] += kind_start_slit;
  556. return slit_cutter (site, heads_up, pass2);
  557. }
  558. }
  559. }
  560. else
  561. {
  562. /* if this is not first point, clear start mark for this edge */
  563. Cdata start = data[edge] & START_MARK (left);
  564. if (start)
  565. {
  566. data[edge] &= ~start;
  567. site->count--;
  568. }
  569. }
  570. }
  571. if (marked)
  572. n++;
  573. /* if next endpoint not between levels, need to exit to zone_crosser */
  574. if (z1 != 1)
  575. {
  576. site->edge = edge;
  577. site->left = left;
  578. site->n = n;
  579. return (z1 != 0); /* return level closest to p1 */
  580. }
  581. /* step to p1 and find next edge
  582. * -- turn left if possible, else straight, else right
  583. * -- check for upward slit beginning at same time */
  584. edge = p1 + (left > 0 ? left : 0);
  585. if (pass2 && jedge && fwd > 0 && (data[edge] & SLIT_UP))
  586. {
  587. jedge = !jedge;
  588. heads_up = 1;
  589. }
  590. else if (data[edge] & (jedge ? I_BNDY : J_BNDY))
  591. {
  592. long tmp = fwd;
  593. fwd = left;
  594. left = -tmp;
  595. jedge = !jedge;
  596. }
  597. else
  598. {
  599. edge = p1 + (fwd > 0 ? fwd : 0);
  600. if (pass2 && !jedge && fwd > 0 && (data[edge] & SLIT_UP))
  601. {
  602. heads_up = 1;
  603. }
  604. else if (!(data[edge] & (jedge ? J_BNDY : I_BNDY)))
  605. {
  606. edge = p1 - (left < 0 ? left : 0);
  607. jedge = !jedge;
  608. {
  609. long tmp = fwd;
  610. fwd = -left;
  611. left = tmp;
  612. }
  613. }
  614. }
  615. p0 = p1;
  616. p1 = POINT1 (edge, fwd);
  617. }
  618. }
  619. /* -- slit_cutter is never called for single level case */
  620. static int
  621. slit_cutter (Csite * site, int up, int pass2)
  622. {
  623. Cdata * data = site->data;
  624. long imax = site->imax;
  625. long n = site->n;
  626. const double *x = pass2 ? site->x : 0;
  627. const double *y = pass2 ? site->y : 0;
  628. double *xcp = pass2 ? site->xcp : 0;
  629. double *ycp = pass2 ? site->ycp : 0;
  630. short *kcp = pass2 ? site->kcp : 0;
  631. if (up && pass2)
  632. {
  633. /* upward stroke of slit proceeds up left side of slit until
  634. * it hits a boundary or a point not between the contour levels
  635. * -- this never happens on the first pass */
  636. long p1 = site->edge;
  637. int z1;
  638. for (;;)
  639. {
  640. z1 = data[p1] & Z_VALUE;
  641. if (z1 != 1)
  642. {
  643. site->edge = p1;
  644. site->left = -1;
  645. site->n = n;
  646. return (z1 != 0);
  647. }
  648. else if (data[p1] & J_BNDY)
  649. {
  650. /* this is very unusual case of closing on a mesh hole */
  651. site->edge = p1;
  652. site->left = -imax;
  653. site->n = n;
  654. return 2;
  655. }
  656. xcp[n] = x[p1];
  657. ycp[n] = y[p1];
  658. kcp[n] = kind_slit_up;
  659. n++;
  660. p1 += imax;
  661. }
  662. }
  663. else
  664. {
  665. /* downward stroke proceeds down right side of slit until it
  666. * hits a boundary or point not between the contour levels */
  667. long p0 = site->edge;
  668. int z0;
  669. /* at beginning of first pass, mark first i-edge with SLIT_DN */
  670. data[p0] |= SLIT_DN;
  671. p0 -= imax;
  672. for (;;)
  673. {
  674. z0 = data[p0] & Z_VALUE;
  675. if (!pass2)
  676. {
  677. if (z0 != 1 || (data[p0] & I_BNDY) || (data[p0 + 1] & J_BNDY))
  678. {
  679. /* at end of first pass, mark final i-edge with SLIT_UP */
  680. data[p0 + imax] |= SLIT_UP;
  681. /* one extra count for splicing at outer curve */
  682. site->n = n + 1;
  683. return 4; /* return same special value as for OPEN_END */
  684. }
  685. }
  686. else
  687. {
  688. if (z0 != 1)
  689. {
  690. site->edge = p0 + imax;
  691. site->left = 1;
  692. site->n = n;
  693. return (z0 != 0);
  694. }
  695. else if (data[p0 + 1] & J_BNDY)
  696. {
  697. site->edge = p0 + 1;
  698. site->left = imax;
  699. site->n = n;
  700. return 2;
  701. }
  702. else if (data[p0] & I_BNDY)
  703. {
  704. site->edge = p0;
  705. site->left = 1;
  706. site->n = n;
  707. return 2;
  708. }
  709. }
  710. if (pass2)
  711. {
  712. xcp[n] = x[p0];
  713. ycp[n] = y[p0];
  714. kcp[n] = kind_slit_down;
  715. n++;
  716. }
  717. else
  718. {
  719. /* on first pass need to count for upstroke as well */
  720. n += 2;
  721. }
  722. p0 -= imax;
  723. }
  724. }
  725. }
  726. /* ------------------------------------------------------------------------ */
  727. /* curve_tracer finds the next starting point, then traces the curve,
  728. * returning the number of points on this curve
  729. * -- in a two level trace, the return value is negative on the
  730. * first pass if the curve closed on a hole
  731. * -- in a single level trace, the return value is negative on the
  732. * first pass if the curve is an incomplete open curve
  733. * -- a return value of 0 indicates no more curves */
  734. static long
  735. curve_tracer (Csite * site, int pass2)
  736. {
  737. Cdata * data = site->data;
  738. long imax = site->imax;
  739. long edge0 = site->edge0;
  740. long left0 = site->left0;
  741. long edge00 = site->edge00;
  742. int two_levels = site->zlevel[1] > site->zlevel[0];
  743. int level, level0, mark_row;
  744. long n;
  745. /* it is possible for a single i-edge to serve as two actual start
  746. * points, one to the right and one to the left
  747. * -- for the two level case, this happens on the first pass for
  748. * a doubly cut edge, or on a chunking boundary
  749. * -- for single level case, this is impossible, but a similar
  750. * situation involving open curves is handled below
  751. * a second two start possibility is when the edge0 zone does not
  752. * exist and both the i-edge and j-edge boundaries are cut
  753. * yet another possibility is three start points at a junction
  754. * of chunk cuts
  755. * -- sigh, several other rare possibilities,
  756. * allow for general case, just go in order i1, i0, j1, j0 */
  757. int two_starts;
  758. /* printf("curve_tracer pass %d\n", pass2); */
  759. /* print_Csite(site); */
  760. if (left0 == 1)
  761. two_starts = data[edge0] & (I0_START | J1_START | J0_START);
  762. else if (left0 == -1)
  763. two_starts = data[edge0] & (J1_START | J0_START);
  764. else if (left0 == imax)
  765. two_starts = data[edge0] & J0_START;
  766. else
  767. two_starts = 0;
  768. if (pass2 || edge0 == 0)
  769. {
  770. /* zip up to row marked on first pass (or by data_init if edge0==0)
  771. * -- but not for double start case */
  772. if (!two_starts)
  773. {
  774. /* final start point marked by ALL_DONE marker */
  775. int first = (edge0 == 0 && !pass2);
  776. long e0 = edge0;
  777. if (data[edge0] & ALL_DONE)
  778. return 0;
  779. while (!(data[edge0] & START_ROW))
  780. edge0 += imax;
  781. if (e0 == edge0)
  782. edge0++; /* two starts handled specially */
  783. if (first)
  784. /* if this is the very first start point, we want to remove
  785. * the START_ROW marker placed by data_init */
  786. data[edge0 - edge0 % imax] &= ~START_ROW;
  787. }
  788. }
  789. else
  790. {
  791. /* first pass ends when all potential start points visited */
  792. if (site->count <= 0)
  793. {
  794. /* place ALL_DONE marker for second pass */
  795. data[edge00] |= ALL_DONE;
  796. /* reset initial site for second pass */
  797. site->edge0 = site->edge00 = site->left0 = 0;
  798. return 0;
  799. }
  800. if (!two_starts)
  801. edge0++;
  802. }
  803. if (two_starts)
  804. {
  805. /* trace second curve with this start immediately */
  806. if (left0 == 1 && (data[edge0] & I0_START))
  807. {
  808. left0 = -1;
  809. level = (data[edge0] & I_BNDY) ? 2 : 0;
  810. }
  811. else if ((left0 == 1 || left0 == -1) && (data[edge0] & J1_START))
  812. {
  813. left0 = imax;
  814. level = 2;
  815. }
  816. else
  817. {
  818. left0 = -imax;
  819. level = 2;
  820. }
  821. }
  822. else
  823. {
  824. /* usual case is to scan for next start marker
  825. * -- on second pass, this is at most one row of mesh, but first
  826. * pass hits nearly every point of the mesh, since it can't
  827. * know in advance which potential start marks removed */
  828. while (!(data[edge0] & ANY_START))
  829. edge0++;
  830. if (data[edge0] & I1_START)
  831. left0 = 1;
  832. else if (data[edge0] & I0_START)
  833. left0 = -1;
  834. else if (data[edge0] & J1_START)
  835. left0 = imax;
  836. else /*data[edge0]&J0_START */
  837. left0 = -imax;
  838. if (data[edge0] & (I1_START | I0_START))
  839. level = (data[edge0] & I_BNDY) ? 2 : 0;
  840. else
  841. level = 2;
  842. }
  843. /* this start marker will not be unmarked, but it has been visited */
  844. if (!pass2)
  845. site->count--;
  846. /* if this curve starts on a non-boundary i-edge, we need to
  847. * determine the level */
  848. if (!level && two_levels)
  849. level = left0 > 0 ?
  850. ((data[edge0 - imax] & Z_VALUE) !=
  851. 0) : ((data[edge0] & Z_VALUE) != 0);
  852. /* initialize site for this curve */
  853. site->edge = site->edge0 = edge0;
  854. site->left = site->left0 = left0;
  855. site->level0 = level0 = level; /* for open curve detection only */
  856. /* single level case just uses zone_crosser */
  857. if (!two_levels)
  858. level = 0;
  859. /* to generate the curve, alternate between zone_crosser and
  860. * edge_walker until closure or first call to edge_walker in
  861. * single level case */
  862. site->n = 0;
  863. for (;;)
  864. {
  865. if (level < 2)
  866. level = zone_crosser (site, level, pass2);
  867. else if (level < 3)
  868. level = edge_walker (site, pass2);
  869. else
  870. break;
  871. }
  872. n = site->n;
  873. /* single level case may have ended at a boundary rather than closing
  874. * -- need to recognize this case here in order to place the
  875. * OPEN_END mark for zone_crosser, remove this start marker,
  876. * and be sure not to make a START_ROW mark for this case
  877. * two level case may close with slit_cutter, in which case start
  878. * must also be removed and no START_ROW mark made
  879. * -- change sign of return n to inform caller */
  880. if (!pass2 && level > 3 && (two_levels || level0 == 0))
  881. {
  882. if (!two_levels)
  883. data[edge0] |= OPEN_END;
  884. data[edge0] &= ~(left0 > 0 ? I1_START : I0_START);
  885. mark_row = 0; /* do not mark START_ROW */
  886. n = -n;
  887. }
  888. else
  889. {
  890. if (two_levels)
  891. mark_row = !two_starts;
  892. else
  893. mark_row = 1;
  894. }
  895. /* on first pass, must apply START_ROW mark in column above previous
  896. * start marker
  897. * -- but skip if we just did second of two start case */
  898. if (!pass2 && mark_row)
  899. {
  900. data[edge0 - (edge0 - edge00) % imax] |= START_ROW;
  901. site->edge00 = edge0;
  902. }
  903. return n;
  904. }
  905. /* ------------------------------------------------------------------------ */
  906. static void
  907. data_init (Csite * site)
  908. {
  909. Cdata * data = site->data;
  910. long imax = site->imax;
  911. long jmax = site->jmax;
  912. long ijmax = imax * jmax;
  913. const double *z = site->z;
  914. double zlev0 = site->zlevel[0];
  915. double zlev1 = site->zlevel[1];
  916. int two_levels = zlev1 > zlev0;
  917. char *reg = site->reg;
  918. long count = 0;
  919. int started = 0;
  920. int ibndy, jbndy, i_was_chunk;
  921. long ichunk, jchunk, i, j, ij;
  922. long i_chunk_size = site->i_chunk_size;
  923. long j_chunk_size = site->j_chunk_size;
  924. if (!two_levels)
  925. {
  926. /* Chunking not used for lines as start points are not correct. */
  927. i_chunk_size = imax - 1;
  928. j_chunk_size = jmax - 1;
  929. }
  930. /* do everything in a single pass through the data array to
  931. * minimize cache faulting (z, reg, and data are potentially
  932. * very large arrays)
  933. * access to the z and reg arrays is strictly sequential,
  934. * but we need two rows (+-imax) of the data array at a time */
  935. if (z[0] > zlev0)
  936. data[0] = (two_levels && z[0] > zlev1) ? 2 : 1;
  937. else
  938. data[0] = 0;
  939. jchunk = 0;
  940. for (j = ij = 0; j < jmax; j++)
  941. {
  942. ichunk = i_was_chunk = 0;
  943. for (i = 0; i < imax; i++, ij++)
  944. {
  945. /* transfer zonal existence from reg to data array
  946. * -- get these for next row so we can figure existence of
  947. * points and j-edges for this row */
  948. data[ij + imax + 1] = 0;
  949. if (reg)
  950. {
  951. if (reg[ij + imax + 1] != 0)
  952. data[ij + imax + 1] = ZONE_EX;
  953. }
  954. else
  955. {
  956. if (i < imax - 1 && j < jmax - 1)
  957. data[ij + imax + 1] = ZONE_EX;
  958. }
  959. /* translate z values to 0, 1, 2 flags */
  960. if (ij < imax)
  961. data[ij + 1] = 0;
  962. if (ij < ijmax - 1 && z[ij + 1] > zlev0)
  963. data[ij + 1] |= (two_levels && z[ij + 1] > zlev1) ? 2 : 1;
  964. /* apply edge boundary marks */
  965. ibndy = i == ichunk
  966. || (data[ij] & ZONE_EX) != (data[ij + 1] & ZONE_EX);
  967. jbndy = j == jchunk
  968. || (data[ij] & ZONE_EX) != (data[ij + imax] & ZONE_EX);
  969. if (ibndy)
  970. data[ij] |= I_BNDY;
  971. if (jbndy)
  972. data[ij] |= J_BNDY;
  973. /* apply i-edge start marks
  974. * -- i-edges are only marked when actually cut
  975. * -- no mark is necessary if one of the j-edges which share
  976. * the lower endpoint is also cut
  977. * -- no I0 mark necessary unless filled region below some cut,
  978. * no I1 mark necessary unless filled region above some cut */
  979. if (j)
  980. {
  981. int v0 = (data[ij] & Z_VALUE);
  982. int vb = (data[ij - imax] & Z_VALUE);
  983. if (v0 != vb)
  984. { /* i-edge is cut */
  985. if (ibndy)
  986. {
  987. if (data[ij] & ZONE_EX)
  988. {
  989. data[ij] |= I0_START;
  990. count++;
  991. }
  992. if (data[ij + 1] & ZONE_EX)
  993. {
  994. data[ij] |= I1_START;
  995. count++;
  996. }
  997. }
  998. else
  999. {
  1000. int va = (data[ij - 1] & Z_VALUE);
  1001. int vc = (data[ij + 1] & Z_VALUE);
  1002. int vd = (data[ij - imax + 1] & Z_VALUE);
  1003. if (v0 != 1 && va != v0
  1004. && (vc != v0 || vd != v0) && (data[ij] & ZONE_EX))
  1005. {
  1006. data[ij] |= I0_START;
  1007. count++;
  1008. }
  1009. if (vb != 1 && va == vb
  1010. && (vc == vb || vd == vb)
  1011. && (data[ij + 1] & ZONE_EX))
  1012. {
  1013. data[ij] |= I1_START;
  1014. count++;
  1015. }
  1016. }
  1017. }
  1018. }
  1019. /* apply j-edge start marks
  1020. * -- j-edges are only marked when they are boundaries
  1021. * -- all cut boundary edges marked
  1022. * -- for two level case, a few uncut edges must be marked
  1023. */
  1024. if (i && jbndy)
  1025. {
  1026. int v0 = (data[ij] & Z_VALUE);
  1027. int vb = (data[ij - 1] & Z_VALUE);
  1028. if (v0 != vb)
  1029. {
  1030. if (data[ij] & ZONE_EX)
  1031. {
  1032. data[ij] |= J0_START;
  1033. count++;
  1034. }
  1035. if (data[ij + imax] & ZONE_EX)
  1036. {
  1037. data[ij] |= J1_START;
  1038. count++;
  1039. }
  1040. }
  1041. else if (two_levels && v0 == 1)
  1042. {
  1043. if (data[ij + imax] & ZONE_EX)
  1044. {
  1045. if (i_was_chunk || !(data[ij + imax - 1] & ZONE_EX))
  1046. {
  1047. /* lower left is a drawn part of boundary */
  1048. data[ij] |= J1_START;
  1049. count++;
  1050. }
  1051. }
  1052. else if (data[ij] & ZONE_EX)
  1053. {
  1054. if (data[ij + imax - 1] & ZONE_EX)
  1055. {
  1056. /* weird case of open hole at lower left */
  1057. data[ij] |= J0_START;
  1058. count++;
  1059. }
  1060. }
  1061. }
  1062. }
  1063. i_was_chunk = (i == ichunk);
  1064. if (i_was_chunk)
  1065. ichunk += i_chunk_size;
  1066. }
  1067. if (j == jchunk)
  1068. jchunk += j_chunk_size;
  1069. /* place first START_ROW marker */
  1070. if (count && !started)
  1071. {
  1072. data[ij - imax] |= START_ROW;
  1073. started = 1;
  1074. }
  1075. }
  1076. /* place immediate stop mark if nothing found */
  1077. if (!count)
  1078. data[0] |= ALL_DONE;
  1079. else
  1080. for (i = 0; i < ijmax; ++i) site->saddle[i] = 0;
  1081. /* initialize site */
  1082. site->edge0 = site->edge00 = site->edge = 0;
  1083. site->left0 = site->left = 0;
  1084. site->n = 0;
  1085. site->count = count;
  1086. }
  1087. /* ------------------------------------------------------------------------
  1088. Original (slightly modified) core contour generation routines are above;
  1089. below are new routines for interfacing to mpl.
  1090. ------------------------------------------------------------------------ */
  1091. /* Note: index order gets switched in the Python interface;
  1092. python Z[i,j] -> C z[j,i]
  1093. so if the array has shape Mi, Nj in python,
  1094. we have iMax = Nj, jMax = Mi in gcntr.c.
  1095. On the Python side: Ny, Nx = shape(z),
  1096. so in C, the x-dimension is the first index, the y-dimension
  1097. the second.
  1098. */
  1099. /* reg should have the same dimensions as data, which
  1100. has an extra iMax + 1 points relative to Z.
  1101. It differs from mask in being the opposite (True
  1102. where a region exists, versus the mask, which is True
  1103. where a data point is bad), and in that it marks
  1104. zones, not points. All four zones sharing a bad
  1105. point must be marked as not existing.
  1106. */
  1107. static void
  1108. mask_zones (long iMax, long jMax, const bool *mask, char *reg)
  1109. {
  1110. long i, j, ij;
  1111. long nreg = iMax * jMax + iMax + 1;
  1112. for (ij = iMax+1; ij < iMax*jMax; ij++)
  1113. {
  1114. reg[ij] = 1;
  1115. }
  1116. ij = 0;
  1117. for (j = 0; j < jMax; j++)
  1118. {
  1119. for (i = 0; i < iMax; i++, ij++)
  1120. {
  1121. if (i == 0 || j == 0) reg[ij] = 0;
  1122. if (mask[ij])
  1123. {
  1124. reg[ij] = 0;
  1125. reg[ij + 1] = 0;
  1126. reg[ij + iMax] = 0;
  1127. reg[ij + iMax + 1] = 0;
  1128. }
  1129. }
  1130. }
  1131. for (; ij < nreg; ij++)
  1132. {
  1133. reg[ij] = 0;
  1134. }
  1135. }
  1136. Csite *
  1137. cntr_new()
  1138. {
  1139. Csite *site = new Csite;
  1140. if (site == nullptr) return nullptr;
  1141. site->data = nullptr;
  1142. site->reg = nullptr;
  1143. site->saddle = nullptr;
  1144. site->xcp = nullptr;
  1145. site->ycp = nullptr;
  1146. site->kcp = nullptr;
  1147. site->x = nullptr;
  1148. site->y = nullptr;
  1149. site->z = nullptr;
  1150. return site;
  1151. }
  1152. void
  1153. cntr_init(Csite *site, long iMax, long jMax, const double *x, const double *y,
  1154. const double *z, const bool *mask, long i_chunk_size, long j_chunk_size)
  1155. {
  1156. long ijmax = iMax * jMax;
  1157. long nreg = iMax * jMax + iMax + 1;
  1158. site->imax = iMax;
  1159. site->jmax = jMax;
  1160. site->data = new Cdata[nreg];
  1161. site->saddle = new Saddle[ijmax];
  1162. if (mask != nullptr)
  1163. {
  1164. site->reg = new char[nreg];
  1165. mask_zones(iMax, jMax, mask, site->reg);
  1166. }
  1167. /* I don't think we need to initialize site->data. */
  1168. site->x = x;
  1169. site->y = y;
  1170. site->z = z;
  1171. site->xcp = nullptr;
  1172. site->ycp = nullptr;
  1173. site->kcp = nullptr;
  1174. /* Store correct chunk sizes for filled contours. Chunking not used for
  1175. line contours. */
  1176. if (i_chunk_size <= 0 || i_chunk_size > iMax - 1)
  1177. i_chunk_size = iMax - 1;
  1178. site->i_chunk_size = i_chunk_size;
  1179. if (j_chunk_size <= 0 || j_chunk_size > jMax - 1)
  1180. j_chunk_size = jMax - 1;
  1181. site->j_chunk_size = j_chunk_size;
  1182. }
  1183. void cntr_del(Csite *site)
  1184. {
  1185. delete [] site->saddle;
  1186. delete [] site->reg;
  1187. delete [] site->data;
  1188. delete site;
  1189. site = nullptr;
  1190. }
  1191. static int
  1192. reorder(double *xpp, double *ypp, short *kpp, double *xy, unsigned char *c, int npts, int nlevels)
  1193. {
  1194. std::vector<int> subp;
  1195. int isp, nsp;
  1196. int iseg, nsegs;
  1197. int isegplus;
  1198. int i;
  1199. int k;
  1200. int started;
  1201. int maxnsegs = npts/2 + 1;
  1202. /* allocate maximum possible size--gross overkill */
  1203. std::vector<int> i0(maxnsegs);
  1204. std::vector<int> i1(maxnsegs);
  1205. /* Find the segments. */
  1206. iseg = 0;
  1207. started = 0;
  1208. for (i=0; i<npts; i++)
  1209. {
  1210. if (started)
  1211. {
  1212. if ((kpp[i] >= kind_slit_up) || (i == npts-1))
  1213. {
  1214. i1[iseg] = i;
  1215. started = 0;
  1216. iseg++;
  1217. if (iseg == maxnsegs)
  1218. {
  1219. k = -1;
  1220. return k;
  1221. }
  1222. }
  1223. }
  1224. else if ((kpp[i] < kind_slit_up) && (i < npts-1))
  1225. {
  1226. i0[iseg] = i;
  1227. started = 1;
  1228. }
  1229. }
  1230. nsegs = iseg;
  1231. /* Find the subpaths as sets of connected segments. */
  1232. subp.resize(nsegs, false);
  1233. for (i=0; i<nsegs; i++) subp[i] = -1;
  1234. nsp = 0;
  1235. for (iseg=0; iseg<nsegs; iseg++)
  1236. {
  1237. /* For each segment, if it is not closed, look ahead for
  1238. the next connected segment.
  1239. */
  1240. double xend, yend;
  1241. xend = xpp[i1[iseg]];
  1242. yend = ypp[i1[iseg]];
  1243. if (subp[iseg] >= 0) continue;
  1244. subp[iseg] = nsp;
  1245. nsp++;
  1246. if (iseg == nsegs-1) continue;
  1247. for (isegplus = iseg+1; isegplus < nsegs; isegplus++)
  1248. {
  1249. if (subp[isegplus] >= 0) continue;
  1250. if (xend == xpp[i0[isegplus]] && yend == ypp[i0[isegplus]])
  1251. {
  1252. subp[isegplus] = subp[iseg];
  1253. xend = xpp[i1[isegplus]];
  1254. yend = ypp[i1[isegplus]];
  1255. }
  1256. }
  1257. }
  1258. /* Generate the verts and codes from the subpaths. */
  1259. k = 0;
  1260. for (isp=0; isp<nsp; isp++)
  1261. {
  1262. int first = 1;
  1263. int kstart = k;
  1264. for (iseg=0; iseg<nsegs; iseg++)
  1265. {
  1266. int istart, iend;
  1267. if (subp[iseg] != isp) continue;
  1268. iend = i1[iseg];
  1269. if (first)
  1270. {
  1271. istart = i0[iseg];
  1272. }
  1273. else
  1274. {
  1275. istart = i0[iseg]+1; /* skip duplicate */
  1276. }
  1277. for (i=istart; i<=iend; i++)
  1278. {
  1279. xy[2*k] = xpp[i];
  1280. xy[2*k+1] = ypp[i];
  1281. if (first) c[k] = MOVETO;
  1282. else c[k] = LINETO;
  1283. first = 0;
  1284. k++;
  1285. if (k > npts) /* should never happen */
  1286. {
  1287. k = -1;
  1288. return k;
  1289. }
  1290. }
  1291. }
  1292. if (nlevels == 2 ||
  1293. (xy[2*kstart] == xy[2*k-2] && xy[2*kstart+1] == xy[2*k-1]))
  1294. {
  1295. c[k-1] = CLOSEPOLY;
  1296. }
  1297. }
  1298. return k;
  1299. }
  1300. /* Build a list of XY 2-D arrays, shape (N,2), to which a list of path
  1301. code arrays is concatenated.
  1302. */
  1303. static py::tuple
  1304. build_cntr_list_v2(long *np, double *xp, double *yp, short *kp,
  1305. int nparts, long ntotal, int nlevels)
  1306. {
  1307. int i;
  1308. long k;
  1309. py::ssize_t dims[2];
  1310. py::ssize_t kdims[1];
  1311. py::list all_verts(nparts);
  1312. py::list all_codes(nparts);
  1313. for (i=0, k=0; i < nparts; k+= np[i], i++)
  1314. {
  1315. double *xpp = xp+k;
  1316. double *ypp = yp+k;
  1317. short *kpp = kp+k;
  1318. int n;
  1319. dims[0] = np[i];
  1320. dims[1] = 2;
  1321. kdims[0] = np[i];
  1322. PointArray xyv(dims);
  1323. CodeArray kv(kdims);
  1324. n = reorder(xpp, ypp, kpp, xyv.mutable_data(), kv.mutable_data(), np[i], nlevels);
  1325. if (n == -1)
  1326. {
  1327. throw std::runtime_error("Error reordering vertices");
  1328. }
  1329. dims[0] = n;
  1330. xyv.resize(dims, false);
  1331. all_verts[i] = xyv;
  1332. kdims[0] = n;
  1333. kv.resize(kdims, false);
  1334. all_codes[i] = kv;
  1335. }
  1336. return py::make_tuple(all_verts, all_codes);
  1337. }
  1338. /* cntr_trace is called once per contour level or level pair.
  1339. If nlevels is 1, a set of contour lines will be returned; if nlevels
  1340. is 2, the set of polygons bounded by the levels will be returned.
  1341. If points is True, the lines will be returned as a list of list
  1342. of points; otherwise, as a list of tuples of vectors.
  1343. */
  1344. py::tuple
  1345. cntr_trace(Csite *site, double levels[], int nlevels)
  1346. {
  1347. int iseg;
  1348. long n;
  1349. long nparts = 0;
  1350. long ntotal = 0;
  1351. long ntotal2 = 0;
  1352. site->zlevel[0] = levels[0];
  1353. site->zlevel[1] = levels[0];
  1354. if (nlevels == 2)
  1355. {
  1356. site->zlevel[1] = levels[1];
  1357. }
  1358. site->n = site->count = 0;
  1359. data_init (site);
  1360. /* make first pass to compute required sizes for second pass */
  1361. for (;;)
  1362. {
  1363. n = curve_tracer (site, 0);
  1364. if (!n)
  1365. break;
  1366. if (n > 0)
  1367. {
  1368. nparts++;
  1369. ntotal += n;
  1370. }
  1371. else
  1372. {
  1373. ntotal -= n;
  1374. }
  1375. }
  1376. std::vector<double> xp0(ntotal);
  1377. std::vector<double> yp0(ntotal);
  1378. std::vector<short> kp0(ntotal);
  1379. std::vector<long> nseg0(nparts);
  1380. /* second pass */
  1381. site->xcp = xp0.data();
  1382. site->ycp = yp0.data();
  1383. site->kcp = kp0.data();
  1384. iseg = 0;
  1385. for (;;iseg++)
  1386. {
  1387. n = curve_tracer (site, 1);
  1388. if (ntotal2 + n > ntotal)
  1389. {
  1390. throw std::runtime_error("curve_tracer: ntotal2, pass 2 exceeds ntotal, pass 1");
  1391. }
  1392. if (n == 0)
  1393. break;
  1394. if (n > 0)
  1395. {
  1396. /* could add array bounds checking */
  1397. nseg0[iseg] = n;
  1398. site->xcp += n;
  1399. site->ycp += n;
  1400. site->kcp += n;
  1401. ntotal2 += n;
  1402. }
  1403. else
  1404. {
  1405. throw std::runtime_error("Negative n from curve_tracer in pass 2");
  1406. }
  1407. }
  1408. site->xcp = nullptr;
  1409. site->ycp = nullptr;
  1410. site->kcp = nullptr;
  1411. return build_cntr_list_v2(
  1412. nseg0.data(), xp0.data(), yp0.data(), kp0.data(), nparts, ntotal, nlevels);
  1413. }
  1414. } // namespace contourpy