marchingsquares.js 95 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424
  1. /*!
  2. * MarchingSquaresJS
  3. * version 1.3.3
  4. * https://github.com/RaumZeit/MarchingSquares.js
  5. *
  6. * @license GNU Affero General Public License.
  7. * Copyright (c) 2015-2019 Ronny Lorenz <ronny@tbi.univie.ac.at>
  8. */
  9. (function (global, factory) {
  10. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  11. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  12. (factory((global.MarchingSquaresJS = global.MarchingSquaresJS || {})));
  13. }(this, (function (exports) { 'use strict';
  14. /*
  15. * Compute the distance of a value 'v' from 'a' through linear interpolation
  16. * between the values of 'a' and 'b'
  17. *
  18. * Note, that we assume that 'a' and 'b' have unit distance (i.e. 1)
  19. */
  20. function linear(a, b, v) {
  21. if (a < b)
  22. return (v - a) / (b - a);
  23. return (a - v) / (a - b);
  24. }
  25. /*
  26. * Compute the distance of a pair of values ('v0', 'v1') from 'a' through linear interpolation
  27. * between the values of 'a' and 'b'
  28. *
  29. * This function assumes that exactly one value, 'v0' or 'v1', is actually located
  30. * between 'a' and 'b', and choses the right one automagically
  31. *
  32. * Note, that we assume that 'a' and 'b' have unit distance (i.e. 1)
  33. */
  34. function linear_ab(a, b, v0, v1) {
  35. var tmp;
  36. if (v0 > v1) {
  37. tmp = v0;
  38. v0 = v1;
  39. v1 = tmp;
  40. }
  41. if (a < b) {
  42. if (a < v0)
  43. return (v0 - a) / (b - a);
  44. else
  45. return (v1 - a) / (b - a);
  46. } else if (a > v1) {
  47. return (a - v1) / (a - b);
  48. }
  49. return (a - v0) / (a - b);
  50. }
  51. /*
  52. * Compute the distance of a pair of values ('v0', 'v1') from 'a' through linear interpolation
  53. * between the values of 'a' and 'b'
  54. *
  55. * This function automagically choses the value 'vN' that is closer to 'a'
  56. *
  57. * Note, that we assume that 'a' and 'b' have unit distance (i.e. 1)
  58. */
  59. function linear_a(a, b, minV, maxV) {
  60. if (a < b)
  61. return (minV - a) / (b - a);
  62. return (a - maxV) / (a - b);
  63. }
  64. /*
  65. * Compute the distance of a pair of values ('v0', 'v1') from 'a' through linear interpolation
  66. * between the values of 'a' and 'b'
  67. *
  68. * This function automagically choses the value 'vN' that is closer to 'b'
  69. *
  70. * Note, that we assume that 'a' and 'b' have unit distance (i.e. 1)
  71. */
  72. function linear_b(a, b, minV, maxV) {
  73. if (a < b)
  74. return (maxV - a) / (b - a);
  75. return (a - minV) / (a - b);
  76. }
  77. function Options() {
  78. /* Settings common to all implemented algorithms */
  79. this.successCallback = null;
  80. this.verbose = false;
  81. this.polygons = false;
  82. this.polygons_full = false;
  83. this.linearRing = true;
  84. this.noQuadTree = false;
  85. this.noFrame = false;
  86. }
  87. /* Compose settings specific to IsoBands algorithm */
  88. function isoBandOptions(userSettings) {
  89. var i,
  90. key,
  91. val,
  92. bandOptions,
  93. optionKeys;
  94. bandOptions = new Options();
  95. userSettings = userSettings ? userSettings : {};
  96. optionKeys = Object.keys(bandOptions);
  97. for(i = 0; i < optionKeys.length; i++) {
  98. key = optionKeys[i];
  99. val = userSettings[key];
  100. if ((typeof val !== 'undefined') && (val !== null))
  101. bandOptions[key] = val;
  102. }
  103. /* restore compatibility */
  104. bandOptions.polygons_full = !bandOptions.polygons;
  105. /* add interpolation functions (not yet user customizable) */
  106. bandOptions.interpolate = linear_ab;
  107. bandOptions.interpolate_a = linear_a;
  108. bandOptions.interpolate_b = linear_b;
  109. return bandOptions;
  110. }
  111. /* Compose settings specific to IsoLines algorithm */
  112. function isoLineOptions(userSettings) {
  113. var i,
  114. key,
  115. val,
  116. lineOptions,
  117. optionKeys;
  118. lineOptions = new Options();
  119. userSettings = userSettings ? userSettings : {};
  120. optionKeys = Object.keys(lineOptions);
  121. for(i = 0; i < optionKeys.length; i++) {
  122. key = optionKeys[i];
  123. val = userSettings[key];
  124. if ((typeof val !== 'undefined') && (val !== null))
  125. lineOptions[key] = val;
  126. }
  127. /* restore compatibility */
  128. lineOptions.polygons_full = !lineOptions.polygons;
  129. /* add interpolation functions (not yet user customizable) */
  130. lineOptions.interpolate = linear;
  131. return lineOptions;
  132. }
  133. function cell2Polygons(cell, x, y, settings) {
  134. var polygons = [];
  135. cell.polygons.forEach(function(p) {
  136. p.forEach(function(pp) {
  137. pp[0] += x;
  138. pp[1] += y;
  139. });
  140. if (settings.linearRing)
  141. p.push(p[0]);
  142. polygons.push(p);
  143. });
  144. return polygons;
  145. }
  146. function entry_coordinate(x, y, mode, path) {
  147. if (mode === 0) { /* down */
  148. x += 1;
  149. y += path[0][1];
  150. } else if (mode === 1) { /* left */
  151. x += path[0][0];
  152. } else if (mode === 2) { /* up */
  153. y += path[0][1];
  154. } else if (mode === 3) { /* right */
  155. x += path[0][0];
  156. y += 1;
  157. }
  158. return [ x, y ];
  159. }
  160. function skip_coordinate(x, y, mode) {
  161. if (mode === 0) { /* down */
  162. x++;
  163. } else if (mode === 1) ; else if (mode === 2) { /* up */
  164. y++;
  165. } else if (mode === 3) { /* right */
  166. x++;
  167. y++;
  168. }
  169. return [ x, y ];
  170. }
  171. function requireFrame(data, lowerBound, upperBound) {
  172. var frameRequired,
  173. cols,
  174. rows,
  175. i,
  176. j;
  177. frameRequired = true;
  178. cols = data[0].length;
  179. rows = data.length;
  180. for (j = 0; j < rows; j++) {
  181. if ((data[j][0] < lowerBound) ||
  182. (data[j][0] > upperBound) ||
  183. (data[j][cols - 1] < lowerBound) ||
  184. (data[j][cols - 1] > upperBound)) {
  185. frameRequired = false;
  186. break;
  187. }
  188. }
  189. if ((frameRequired) &&
  190. ((data[rows - 1][0] < lowerBound) ||
  191. (data[rows - 1][0] > upperBound) ||
  192. (data[rows - 1][cols - 1] < lowerBound) ||
  193. (data[rows - 1][cols - 1] > upperBound))) {
  194. frameRequired = false;
  195. }
  196. if (frameRequired)
  197. for (i = 0; i < cols - 1; i++) {
  198. if ((data[0][i] < lowerBound) ||
  199. (data[0][i] > upperBound) ||
  200. (data[rows - 1][i] < lowerBound) ||
  201. (data[rows - 1][i] > upperBound)) {
  202. frameRequired = false;
  203. break;
  204. }
  205. }
  206. return frameRequired;
  207. }
  208. function requireLineFrame(data, threshold) {
  209. var frameRequired,
  210. cols,
  211. rows,
  212. i,
  213. j;
  214. frameRequired = true;
  215. cols = data[0].length;
  216. rows = data.length;
  217. for (j = 0; j < rows; j++) {
  218. if ((data[j][0] >= threshold) ||
  219. (data[j][cols - 1] >= threshold)) {
  220. frameRequired = false;
  221. break;
  222. }
  223. }
  224. if ((frameRequired) &&
  225. ((data[rows - 1][0] >= threshold) ||
  226. (data[rows - 1][cols - 1] >= threshold))) {
  227. frameRequired = false;
  228. }
  229. if (frameRequired)
  230. for (i = 0; i < cols - 1; i++) {
  231. if ((data[0][i] >= threshold) ||
  232. (data[rows - 1][i] > threshold)) {
  233. frameRequired = false;
  234. break;
  235. }
  236. }
  237. return frameRequired;
  238. }
  239. function traceBandPaths(data, cellGrid, settings) {
  240. var nextedge,
  241. path,
  242. e,
  243. ee,
  244. s,
  245. ve,
  246. enter,
  247. x,
  248. y,
  249. finalized,
  250. origin,
  251. cc,
  252. dir,
  253. count,
  254. point,
  255. found_entry;
  256. var polygons = [];
  257. var rows = data.length - 1;
  258. var cols = data[0].length - 1;
  259. /*
  260. * directions for out-of-grid moves are:
  261. * 0 ... "down",
  262. * 1 ... "left",
  263. * 2 ... "up",
  264. * 3 ... "right"
  265. */
  266. var valid_entries = [ ['rt', 'rb'], /* down */
  267. ['br', 'bl'], /* left */
  268. ['lb', 'lt'], /* up */
  269. ['tl', 'tr'] /* right */
  270. ];
  271. var add_x = [ 0, -1, 0, 1 ];
  272. var add_y = [ -1, 0, 1, 0 ];
  273. var available_starts = [ 'bl', 'lb', 'lt', 'tl', 'tr', 'rt', 'rb', 'br' ];
  274. var entry_dir = {
  275. bl: 1, br: 1,
  276. lb: 2, lt: 2,
  277. tl: 3, tr: 3,
  278. rt: 0, rb: 0
  279. };
  280. if (requireFrame(data, settings.minV, settings.maxV)) {
  281. if (settings.linearRing)
  282. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0], [0, 0] ]);
  283. else
  284. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0] ]);
  285. }
  286. /* finally, start tracing back first polygon(s) */
  287. cellGrid.forEach(function(a, i) {
  288. a.forEach(function(cell, j) {
  289. nextedge = null;
  290. /* trace paths for all available edges that go through this cell */
  291. for (e = 0; e < 8; e++) {
  292. nextedge = available_starts[e];
  293. if (typeof cell.edges[nextedge] !== 'object')
  294. continue;
  295. /* start a new, full path */
  296. path = [];
  297. ee = cell.edges[nextedge];
  298. enter = nextedge;
  299. x = i;
  300. y = j;
  301. finalized = false;
  302. origin = [ i + ee.path[0][0], j + ee.path[0][1] ];
  303. /* add start coordinate */
  304. path.push(origin);
  305. /* start traceback */
  306. while (!finalized) {
  307. cc = cellGrid[x][y];
  308. if (typeof cc.edges[enter] !== 'object')
  309. break;
  310. ee = cc.edges[enter];
  311. /* remove edge from cell */
  312. delete cc.edges[enter];
  313. /* add last point of edge to path arra, since we extend a polygon */
  314. point = ee.path[1];
  315. point[0] += x;
  316. point[1] += y;
  317. path.push(point);
  318. enter = ee.move.enter;
  319. x = x + ee.move.x;
  320. y = y + ee.move.y;
  321. /* handle out-of-grid moves */
  322. if ((typeof cellGrid[x] === 'undefined') ||
  323. (typeof cellGrid[x][y] === 'undefined')) {
  324. dir = 0;
  325. count = 0;
  326. if (x === cols) {
  327. x--;
  328. dir = 0; /* move downwards */
  329. } else if (x < 0) {
  330. x++;
  331. dir = 2; /* move upwards */
  332. } else if (y === rows) {
  333. y--;
  334. dir = 3; /* move right */
  335. } else if (y < 0) {
  336. y++;
  337. dir = 1; /* move left */
  338. } else {
  339. throw new Error('Left the grid somewhere in the interior!');
  340. }
  341. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  342. finalized = true;
  343. enter = nextedge;
  344. break;
  345. }
  346. while (1) {
  347. found_entry = false;
  348. if (count > 4)
  349. throw new Error('Direction change counter overflow! This should never happen!');
  350. if (!((typeof cellGrid[x] === 'undefined') ||
  351. (typeof cellGrid[x][y] === 'undefined'))) {
  352. cc = cellGrid[x][y];
  353. /* check for re-entry */
  354. for (s = 0; s < valid_entries[dir].length; s++) {
  355. ve = valid_entries[dir][s];
  356. if (typeof cc.edges[ve] === 'object') {
  357. /* found re-entry */
  358. ee = cc.edges[ve];
  359. path.push(entry_coordinate(x, y, dir, ee.path));
  360. enter = ve;
  361. found_entry = true;
  362. break;
  363. }
  364. }
  365. }
  366. if (found_entry) {
  367. break;
  368. } else {
  369. path.push(skip_coordinate(x, y, dir));
  370. x += add_x[dir];
  371. y += add_y[dir];
  372. /* change direction if we'e moved out of grid again */
  373. if ((typeof cellGrid[x] === 'undefined') ||
  374. (typeof cellGrid[x][y] === 'undefined')) {
  375. if (((dir === 0) && (y < 0)) ||
  376. ((dir === 1) && (x < 0)) ||
  377. ((dir === 2) && (y === rows)) ||
  378. ((dir === 3) && (x === cols))) {
  379. x -= add_x[dir];
  380. y -= add_y[dir];
  381. dir = (dir + 1) % 4;
  382. count++;
  383. }
  384. }
  385. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  386. /* we are back where we started off, so finalize the polygon */
  387. finalized = true;
  388. enter = nextedge;
  389. break;
  390. }
  391. }
  392. }
  393. }
  394. }
  395. if ((settings.linearRing) &&
  396. ((path[path.length - 1][0] !== origin[0]) ||
  397. (path[path.length - 1][1] !== origin[1])))
  398. path.push(origin);
  399. polygons.push(path);
  400. } /* end forall entry sites */
  401. }); /* end foreach i */
  402. }); /* end foreach j */
  403. return polygons;
  404. }
  405. function traceLinePaths(data, cellGrid, settings) {
  406. var nextedge,
  407. e,
  408. ee,
  409. cc,
  410. path,
  411. enter,
  412. x,
  413. y,
  414. finalized,
  415. origin,
  416. point,
  417. dir,
  418. count,
  419. found_entry,
  420. ve;
  421. var polygons = [];
  422. var rows = data.length - 1;
  423. var cols = data[0].length - 1;
  424. /*
  425. * directions for out-of-grid moves are:
  426. * 0 ... "down",
  427. * 1 ... "left",
  428. * 2 ... "up",
  429. * 3 ... "right"
  430. */
  431. var valid_entries = [ 'right', /* down */
  432. 'bottom', /* left */
  433. 'left', /* up */
  434. 'top' /* right */
  435. ];
  436. var add_x = [ 0, -1, 0, 1 ];
  437. var add_y = [ -1, 0, 1, 0 ];
  438. var entry_dir = {
  439. bottom: 1,
  440. left: 2,
  441. top: 3,
  442. right: 0
  443. };
  444. /* first, detect whether we need any outer frame */
  445. if (!settings.noFrame)
  446. if (requireLineFrame(data, settings.threshold)) {
  447. if (settings.linearRing)
  448. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0], [0, 0] ]);
  449. else
  450. polygons.push([ [0, 0], [0, rows], [cols, rows], [cols, 0] ]);
  451. }
  452. /* finally, start tracing back first polygon(s) */
  453. cellGrid.forEach(function(a, i) {
  454. a.forEach(function(cell, j) {
  455. nextedge = null;
  456. /* trace paths for all available edges that go through this cell */
  457. for (e = 0; e < 4; e++) {
  458. nextedge = valid_entries[e];
  459. if (typeof cell.edges[nextedge] !== 'object')
  460. continue;
  461. /* start a new, full path */
  462. path = [];
  463. ee = cell.edges[nextedge];
  464. enter = nextedge;
  465. x = i;
  466. y = j;
  467. finalized = false;
  468. origin = [ i + ee.path[0][0], j + ee.path[0][1] ];
  469. /* add start coordinate */
  470. path.push(origin);
  471. /* start traceback */
  472. while (!finalized) {
  473. cc = cellGrid[x][y];
  474. if (typeof cc.edges[enter] !== 'object')
  475. break;
  476. ee = cc.edges[enter];
  477. /* remove edge from cell */
  478. delete cc.edges[enter];
  479. /* add last point of edge to path arra, since we extend a polygon */
  480. point = ee.path[1];
  481. point[0] += x;
  482. point[1] += y;
  483. path.push(point);
  484. enter = ee.move.enter;
  485. x = x + ee.move.x;
  486. y = y + ee.move.y;
  487. /* handle out-of-grid moves */
  488. if ((typeof cellGrid[x] === 'undefined') ||
  489. (typeof cellGrid[x][y] === 'undefined')) {
  490. if (!settings.linearRing)
  491. break;
  492. dir = 0;
  493. count = 0;
  494. if (x === cols) {
  495. x--;
  496. dir = 0; /* move downwards */
  497. } else if (x < 0) {
  498. x++;
  499. dir = 2; /* move upwards */
  500. } else if (y === rows) {
  501. y--;
  502. dir = 3; /* move right */
  503. } else if (y < 0) {
  504. y++;
  505. dir = 1; /* move left */
  506. }
  507. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  508. finalized = true;
  509. enter = nextedge;
  510. break;
  511. }
  512. while (1) {
  513. found_entry = false;
  514. if (count > 4)
  515. throw new Error('Direction change counter overflow! This should never happen!');
  516. if (!((typeof cellGrid[x] === 'undefined') ||
  517. (typeof cellGrid[x][y] === 'undefined'))) {
  518. cc = cellGrid[x][y];
  519. /* check for re-entry */
  520. ve = valid_entries[dir];
  521. if (typeof cc.edges[ve] === 'object') {
  522. /* found re-entry */
  523. ee = cc.edges[ve];
  524. path.push(entry_coordinate(x, y, dir, ee.path));
  525. enter = ve;
  526. found_entry = true;
  527. break;
  528. }
  529. }
  530. if (found_entry) {
  531. break;
  532. } else {
  533. path.push(skip_coordinate(x, y, dir));
  534. x += add_x[dir];
  535. y += add_y[dir];
  536. /* change direction if we'e moved out of grid again */
  537. if ((typeof cellGrid[x] === 'undefined') ||
  538. (typeof cellGrid[x][y] === 'undefined')) {
  539. if (((dir === 0) && (y < 0)) ||
  540. ((dir === 1) && (x < 0)) ||
  541. ((dir === 2) && (y === rows)) ||
  542. ((dir === 3) && (x === cols))) {
  543. x -= add_x[dir];
  544. y -= add_y[dir];
  545. dir = (dir + 1) % 4;
  546. count++;
  547. }
  548. }
  549. if ((x === i) && (y === j) && (dir === entry_dir[nextedge])) {
  550. /* we are back where we started off, so finalize the polygon */
  551. finalized = true;
  552. enter = nextedge;
  553. break;
  554. }
  555. }
  556. }
  557. }
  558. }
  559. if ((settings.linearRing) &&
  560. ((path[path.length - 1][0] !== origin[0]) ||
  561. (path[path.length - 1][1] !== origin[1])))
  562. path.push(origin);
  563. polygons.push(path);
  564. } /* end forall entry sites */
  565. }); /* end foreach i */
  566. }); /* end foreach j */
  567. return polygons;
  568. }
  569. /* quadTree node constructor */
  570. function TreeNode(data, x, y, dx, dy) {
  571. var dx_tmp = dx,
  572. dy_tmp = dy,
  573. msb_x = 0,
  574. msb_y = 0;
  575. /* left-bottom corner of current quadrant */
  576. this.x = x;
  577. this.y = y;
  578. /* minimum value in subtree under this node */
  579. this.lowerBound = null;
  580. /* maximum value in subtree under this node */
  581. this.upperBound = null;
  582. /*
  583. * child nodes are layed out in the following way:
  584. *
  585. * (x, y + 1) ---- (x + 1, y + 1)
  586. * | | |
  587. * | D | C |
  588. * | | |
  589. * |----------------------------|
  590. * | | |
  591. * | A | B |
  592. * | | |
  593. * (x, y) ------------ (x + 1, y)
  594. */
  595. this.childA = null;
  596. this.childB = null;
  597. this.childC = null;
  598. this.childD = null;
  599. if ((dx === 1) && (dy === 1)) {
  600. /* do not further subdivision */
  601. this.lowerBound = Math.min(
  602. data[y][x],
  603. data[y][x + 1],
  604. data[y + 1][x + 1],
  605. data[y + 1][x]
  606. );
  607. this.upperBound = Math.max(
  608. data[y][x],
  609. data[y][x + 1],
  610. data[y + 1][x + 1],
  611. data[y + 1][x]
  612. );
  613. } else {
  614. /* get most significant bit from dx */
  615. if (dx > 1) {
  616. while (dx_tmp !== 0) {
  617. dx_tmp = dx_tmp >> 1;
  618. msb_x++;
  619. }
  620. if (dx === (1 << (msb_x - 1)))
  621. msb_x--;
  622. dx_tmp = 1 << (msb_x - 1);
  623. }
  624. /* get most significant bit from dx */
  625. if (dy > 1) {
  626. while (dy_tmp !== 0) {
  627. dy_tmp = dy_tmp >> 1;
  628. msb_y++;
  629. }
  630. if (dy === (1 << (msb_y - 1)))
  631. msb_y--;
  632. dy_tmp = 1 << (msb_y - 1);
  633. }
  634. this.childA = new TreeNode(data, x, y, dx_tmp, dy_tmp);
  635. this.lowerBound = this.childA.lowerBound;
  636. this.upperBound = this.childA.upperBound;
  637. if (dx - dx_tmp > 0) {
  638. this.childB = new TreeNode(data, x + dx_tmp, y, dx - dx_tmp, dy_tmp);
  639. this.lowerBound = Math.min(this.lowerBound, this.childB.lowerBound);
  640. this.upperBound = Math.max(this.upperBound, this.childB.upperBound);
  641. if (dy - dy_tmp > 0) {
  642. this.childC = new TreeNode(data, x + dx_tmp, y + dy_tmp, dx - dx_tmp, dy - dy_tmp);
  643. this.lowerBound = Math.min(this.lowerBound, this.childC.lowerBound);
  644. this.upperBound = Math.max(this.upperBound, this.childC.upperBound);
  645. }
  646. }
  647. if (dy - dy_tmp > 0) {
  648. this.childD = new TreeNode(data, x, y + dy_tmp, dx_tmp, dy - dy_tmp);
  649. this.lowerBound = Math.min(this.lowerBound, this.childD.lowerBound);
  650. this.upperBound = Math.max(this.upperBound, this.childD.upperBound);
  651. }
  652. }
  653. }
  654. /**
  655. * Retrieve a list of cells within a particular range of values by
  656. * recursivly traversing the quad tree to it's leaves.
  657. *
  658. * @param subsumed If 'true' include all cells that are completely
  659. * subsumed within the specified range. Otherwise,
  660. * return only cells where at least one corner is
  661. * outside the specified range.
  662. *
  663. * @return An array of objects 'o' where each object has exactly two
  664. * properties: 'o.x' and 'o.y' denoting the left-bottom corner
  665. * of the corresponding cell.
  666. */
  667. TreeNode.prototype.cellsInBand = function(lowerBound, upperBound, subsumed) {
  668. var cells = [];
  669. subsumed = (typeof subsumed === 'undefined') ? true : subsumed;
  670. if ((this.lowerBound > upperBound) || (this.upperBound < lowerBound))
  671. return cells;
  672. if (!(this.childA || this.childB || this.childC || this.childD)) {
  673. if ((subsumed) ||
  674. (this.lowerBound <= lowerBound) ||
  675. (this.upperBound >= upperBound)) {
  676. cells.push({
  677. x: this.x,
  678. y: this.y
  679. });
  680. }
  681. } else {
  682. if (this.childA)
  683. cells = cells.concat(this.childA.cellsInBand(lowerBound, upperBound, subsumed));
  684. if (this.childB)
  685. cells = cells.concat(this.childB.cellsInBand(lowerBound, upperBound, subsumed));
  686. if (this.childD)
  687. cells = cells.concat(this.childD.cellsInBand(lowerBound, upperBound, subsumed));
  688. if (this.childC)
  689. cells = cells.concat(this.childC.cellsInBand(lowerBound, upperBound, subsumed));
  690. }
  691. return cells;
  692. };
  693. TreeNode.prototype.cellsBelowThreshold = function(threshold, subsumed) {
  694. var cells = [];
  695. subsumed = (typeof subsumed === 'undefined') ? true : subsumed;
  696. if (this.lowerBound > threshold)
  697. return cells;
  698. if (!(this.childA || this.childB || this.childC || this.childD)) {
  699. if ((subsumed) ||
  700. (this.upperBound >= threshold)) {
  701. cells.push({
  702. x: this.x,
  703. y: this.y
  704. });
  705. }
  706. } else {
  707. if (this.childA)
  708. cells = cells.concat(this.childA.cellsBelowThreshold(threshold, subsumed));
  709. if (this.childB)
  710. cells = cells.concat(this.childB.cellsBelowThreshold(threshold, subsumed));
  711. if (this.childD)
  712. cells = cells.concat(this.childD.cellsBelowThreshold(threshold, subsumed));
  713. if (this.childC)
  714. cells = cells.concat(this.childC.cellsBelowThreshold(threshold, subsumed));
  715. }
  716. return cells;
  717. };
  718. /*
  719. * Given a scalar field `data` construct a QuadTree
  720. * to efficiently lookup those parts of the scalar
  721. * field where values are within a particular
  722. * range of [lowerbound, upperbound] limits.
  723. */
  724. function QuadTree(data) {
  725. var i, cols;
  726. /* do some input checking */
  727. if (!data)
  728. throw new Error('data is required');
  729. if (!Array.isArray(data) ||
  730. !Array.isArray(data[0]))
  731. throw new Error('data must be scalar field, i.e. array of arrays');
  732. if (data.length < 2)
  733. throw new Error('data must contain at least two rows');
  734. /* check if we've got a regular grid */
  735. cols = data[0].length;
  736. if (cols < 2)
  737. throw new Error('data must contain at least two columns');
  738. for (i = 1; i < data.length; i++) {
  739. if (!Array.isArray(data[i]))
  740. throw new Error('Row ' + i + ' is not an array');
  741. if (data[i].length != cols)
  742. throw new Error('unequal row lengths detected, please provide a regular grid');
  743. }
  744. /* create pre-processing object */
  745. this.data = data;
  746. /* root node, i.e. entry to the data */
  747. this.root = new TreeNode(data, 0, 0, data[0].length - 1, data.length - 1);
  748. }
  749. /* eslint no-console: ["error", { allow: ["log"] }] */
  750. /*
  751. * Compute the iso lines for a scalar 2D field given
  752. * a certain threshold by applying the Marching Squares
  753. * Algorithm. The function returns a list of path coordinates
  754. */
  755. function isoLines(input, threshold, options) {
  756. var settings,
  757. i,
  758. j,
  759. useQuadTree = false,
  760. multiLine = false,
  761. tree = null,
  762. root = null,
  763. data = null,
  764. cellGrid = null,
  765. linePolygons = null,
  766. ret = [];
  767. /* validation */
  768. if (!input) throw new Error('data is required');
  769. if (threshold === undefined || threshold === null) throw new Error('threshold is required');
  770. if ((!!options) && (typeof options !== 'object')) throw new Error('options must be an object');
  771. /* process options */
  772. settings = isoLineOptions(options);
  773. /* check for input data */
  774. if (input instanceof QuadTree) {
  775. tree = input;
  776. root = input.root;
  777. data = input.data;
  778. if (!settings.noQuadTree)
  779. useQuadTree = true;
  780. } else if (Array.isArray(input) && Array.isArray(input[0])) {
  781. data = input;
  782. } else {
  783. throw new Error('input is neither array of arrays nor object retrieved from \'QuadTree()\'');
  784. }
  785. /* check and prepare input threshold(s) */
  786. if (Array.isArray(threshold)) {
  787. multiLine = true;
  788. /* activate QuadTree optimization if not explicitly forbidden by user settings */
  789. if (!settings.noQuadTree)
  790. useQuadTree = true;
  791. /* check if all minV are numbers */
  792. for (i = 0; i < threshold.length; i++)
  793. if (isNaN(+threshold[i]))
  794. throw new Error('threshold[' + i + '] is not a number');
  795. } else {
  796. if (isNaN(+threshold))
  797. throw new Error('threshold must be a number or array of numbers');
  798. threshold = [ threshold ];
  799. }
  800. /* create QuadTree root node if not already present */
  801. if ((useQuadTree) && (!root)) {
  802. tree = new QuadTree(data);
  803. root = tree.root;
  804. data = tree.data;
  805. }
  806. if (settings.verbose) {
  807. if(settings.polygons)
  808. console.log('MarchingSquaresJS-isoLines: returning single lines (polygons) for each grid cell');
  809. else
  810. console.log('MarchingSquaresJS-isoLines: returning line paths (polygons) for entire data grid');
  811. if (multiLine)
  812. console.log('MarchingSquaresJS-isoLines: multiple lines requested, returning array of line paths instead of lines for a single threshold');
  813. }
  814. /* Done with all input validation, now let's start computing stuff */
  815. /* loop over all threhsold values */
  816. threshold.forEach(function(t, i) {
  817. linePolygons = [];
  818. /* store bounds for current computation in settings object */
  819. settings.threshold = t;
  820. if(settings.verbose)
  821. console.log('MarchingSquaresJS-isoLines: computing iso lines for threshold ' + t);
  822. if (settings.polygons) {
  823. /* compose list of polygons for each single cell */
  824. if (useQuadTree) {
  825. /* go through list of cells retrieved from QuadTree */
  826. root
  827. .cellsBelowThreshold(settings.threshold, true)
  828. .forEach(function(c) {
  829. linePolygons = linePolygons.concat(
  830. cell2Polygons(
  831. prepareCell(data,
  832. c.x,
  833. c.y,
  834. settings),
  835. c.x,
  836. c.y,
  837. settings
  838. ));
  839. });
  840. } else {
  841. /* go through entire array of input data */
  842. for (j = 0; j < data.length - 1; ++j) {
  843. for (i = 0; i < data[0].length - 1; ++i)
  844. linePolygons = linePolygons.concat(
  845. cell2Polygons(
  846. prepareCell(data,
  847. i,
  848. j,
  849. settings),
  850. i,
  851. j,
  852. settings
  853. ));
  854. }
  855. }
  856. } else {
  857. /* sparse grid of input data cells */
  858. cellGrid = [];
  859. for (i = 0; i < data[0].length - 1; ++i)
  860. cellGrid[i] = [];
  861. /* compose list of polygons for entire input grid */
  862. if (useQuadTree) {
  863. /* collect the cells */
  864. root
  865. .cellsBelowThreshold(settings.threshold, false)
  866. .forEach(function(c) {
  867. cellGrid[c.x][c.y] = prepareCell(data,
  868. c.x,
  869. c.y,
  870. settings);
  871. });
  872. } else {
  873. /* prepare cells */
  874. for (i = 0; i < data[0].length - 1; ++i) {
  875. for (j = 0; j < data.length - 1; ++j) {
  876. cellGrid[i][j] = prepareCell(data,
  877. i,
  878. j,
  879. settings);
  880. }
  881. }
  882. }
  883. linePolygons = traceLinePaths(data, cellGrid, settings);
  884. }
  885. /* finally, add polygons to output array */
  886. if (multiLine)
  887. ret.push(linePolygons);
  888. else
  889. ret = linePolygons;
  890. if(typeof settings.successCallback === 'function')
  891. settings.successCallback(ret, t);
  892. });
  893. return ret;
  894. }
  895. /*
  896. * Thats all for the public interface, below follows the actual
  897. * implementation
  898. */
  899. /*
  900. * ################################
  901. * Isocontour implementation below
  902. * ################################
  903. */
  904. function prepareCell(grid, x, y, settings) {
  905. var left,
  906. right,
  907. top,
  908. bottom,
  909. average,
  910. cell;
  911. var cval = 0;
  912. var x3 = grid[y + 1][x];
  913. var x2 = grid[y + 1][x + 1];
  914. var x1 = grid[y][x + 1];
  915. var x0 = grid[y][x];
  916. var threshold = settings.threshold;
  917. /*
  918. * Note that missing data within the grid will result
  919. * in horribly failing to trace full polygon paths
  920. */
  921. if(isNaN(x0) || isNaN(x1) || isNaN(x2) || isNaN(x3)) {
  922. return;
  923. }
  924. /*
  925. * Here we detect the type of the cell
  926. *
  927. * x3 ---- x2
  928. * | |
  929. * | |
  930. * x0 ---- x1
  931. *
  932. * with edge points
  933. *
  934. * x0 = (x,y),
  935. * x1 = (x + 1, y),
  936. * x2 = (x + 1, y + 1), and
  937. * x3 = (x, y + 1)
  938. *
  939. * and compute the polygon intersections with the edges
  940. * of the cell. Each edge value may be (i) smaller, or (ii)
  941. * greater or equal to the iso line threshold. We encode
  942. * this property using 1 bit of information, where
  943. *
  944. * 0 ... below,
  945. * 1 ... above or equal
  946. *
  947. * Then we store the cells value as vector
  948. *
  949. * cval = (x0, x1, x2, x3)
  950. *
  951. * where x0 is the least significant bit (0th),
  952. * x1 the 2nd bit, and so on. This essentially
  953. * enables us to work with a single integer number
  954. */
  955. cval |= ((x3 >= threshold) ? 8 : 0);
  956. cval |= ((x2 >= threshold) ? 4 : 0);
  957. cval |= ((x1 >= threshold) ? 2 : 0);
  958. cval |= ((x0 >= threshold) ? 1 : 0);
  959. /* make sure cval is a number */
  960. cval = +cval;
  961. /* compose the cell object */
  962. cell = {
  963. cval: cval,
  964. polygons: [],
  965. edges: {},
  966. x0: x0,
  967. x1: x1,
  968. x2: x2,
  969. x3: x3
  970. };
  971. /*
  972. * Compute interpolated intersections of the polygon(s)
  973. * with the cell borders and (i) add edges for polygon
  974. * trace-back, or (ii) a list of small closed polygons
  975. */
  976. switch (cval) {
  977. case 0:
  978. if (settings.polygons)
  979. cell.polygons.push([ [0, 0], [0, 1], [1, 1], [1, 0] ]);
  980. break;
  981. case 15:
  982. /* cell is outside (above) threshold, no polygons */
  983. break;
  984. case 14: /* 1110 */
  985. left = settings.interpolate(x0, x3, threshold);
  986. bottom = settings.interpolate(x0, x1, threshold);
  987. if (settings.polygons_full) {
  988. cell.edges.left = {
  989. path: [ [0, left], [bottom, 0] ],
  990. move: {
  991. x: 0,
  992. y: -1,
  993. enter: 'top'
  994. }
  995. };
  996. }
  997. if (settings.polygons)
  998. cell.polygons.push([ [0, 0], [0, left], [bottom, 0] ]);
  999. break;
  1000. case 13: /* 1101 */
  1001. bottom = settings.interpolate(x0, x1, threshold);
  1002. right = settings.interpolate(x1, x2, threshold);
  1003. if (settings.polygons_full) {
  1004. cell.edges.bottom = {
  1005. path: [ [bottom, 0], [1, right] ],
  1006. move: {
  1007. x: 1,
  1008. y: 0,
  1009. enter: 'left'
  1010. }
  1011. };
  1012. }
  1013. if (settings.polygons)
  1014. cell.polygons.push([ [bottom, 0], [1, right], [1, 0] ]);
  1015. break;
  1016. case 11: /* 1011 */
  1017. right = settings.interpolate(x1, x2, threshold);
  1018. top = settings.interpolate(x3, x2, threshold);
  1019. if (settings.polygons_full) {
  1020. cell.edges.right = {
  1021. path: [ [1, right], [top, 1] ],
  1022. move: {
  1023. x: 0,
  1024. y: 1,
  1025. enter: 'bottom'
  1026. }
  1027. };
  1028. }
  1029. if (settings.polygons)
  1030. cell.polygons.push([ [1, right], [top, 1], [1, 1] ]);
  1031. break;
  1032. case 7: /* 0111 */
  1033. left = settings.interpolate(x0, x3, threshold);
  1034. top = settings.interpolate(x3, x2, threshold);
  1035. if (settings.polygons_full) {
  1036. cell.edges.top = {
  1037. path: [ [top, 1], [0, left] ],
  1038. move: {
  1039. x: -1,
  1040. y: 0,
  1041. enter: 'right'
  1042. }
  1043. };
  1044. }
  1045. if (settings.polygons)
  1046. cell.polygons.push([ [top, 1], [0, left], [0, 1] ]);
  1047. break;
  1048. case 1: /* 0001 */
  1049. left = settings.interpolate(x0, x3, threshold);
  1050. bottom = settings.interpolate(x0, x1, threshold);
  1051. if (settings.polygons_full) {
  1052. cell.edges.bottom = {
  1053. path: [ [bottom, 0], [0, left] ],
  1054. move: {
  1055. x: -1,
  1056. y: 0,
  1057. enter: 'right'
  1058. }
  1059. };
  1060. }
  1061. if (settings.polygons)
  1062. cell.polygons.push([ [bottom, 0], [0, left], [0, 1], [1, 1], [1, 0] ]);
  1063. break;
  1064. case 2: /* 0010 */
  1065. bottom = settings.interpolate(x0, x1, threshold);
  1066. right = settings.interpolate(x1, x2, threshold);
  1067. if (settings.polygons_full) {
  1068. cell.edges.right = {
  1069. path: [ [1, right], [bottom, 0] ],
  1070. move: {
  1071. x: 0,
  1072. y: -1,
  1073. enter: 'top'
  1074. }
  1075. };
  1076. }
  1077. if (settings.polygons)
  1078. cell.polygons.push([ [0, 0], [0, 1], [1, 1], [1, right], [bottom, 0] ]);
  1079. break;
  1080. case 4: /* 0100 */
  1081. right = settings.interpolate(x1, x2, threshold);
  1082. top = settings.interpolate(x3, x2, threshold);
  1083. if (settings.polygons_full) {
  1084. cell.edges.top = {
  1085. path: [ [top, 1], [1, right] ],
  1086. move: {
  1087. x: 1,
  1088. y: 0,
  1089. enter: 'left'
  1090. }
  1091. };
  1092. }
  1093. if (settings.polygons)
  1094. cell.polygons.push([ [0, 0], [0, 1], [top, 1], [1, right], [1, 0] ]);
  1095. break;
  1096. case 8: /* 1000 */
  1097. left = settings.interpolate(x0, x3, threshold);
  1098. top = settings.interpolate(x3, x2, threshold);
  1099. if (settings.polygons_full) {
  1100. cell.edges.left = {
  1101. path: [ [0, left], [top, 1] ],
  1102. move: {
  1103. x: 0,
  1104. y: 1,
  1105. enter: 'bottom'
  1106. }
  1107. };
  1108. }
  1109. if (settings.polygons)
  1110. cell.polygons.push([ [0, 0], [0, left], [top, 1], [1, 1], [1, 0] ]);
  1111. break;
  1112. case 12: /* 1100 */
  1113. left = settings.interpolate(x0, x3, threshold);
  1114. right = settings.interpolate(x1, x2, threshold);
  1115. if (settings.polygons_full) {
  1116. cell.edges.left = {
  1117. path: [ [0, left], [1, right] ],
  1118. move: {
  1119. x: 1,
  1120. y: 0,
  1121. enter: 'left'
  1122. }
  1123. };
  1124. }
  1125. if (settings.polygons)
  1126. cell.polygons.push([ [0, 0], [0, left], [1, right], [1, 0] ]);
  1127. break;
  1128. case 9: /* 1001 */
  1129. bottom = settings.interpolate(x0, x1, threshold);
  1130. top = settings.interpolate(x3, x2, threshold);
  1131. if (settings.polygons_full) {
  1132. cell.edges.bottom = {
  1133. path: [ [bottom, 0], [top, 1] ],
  1134. move: {
  1135. x: 0,
  1136. y: 1,
  1137. enter: 'bottom'
  1138. }
  1139. };
  1140. }
  1141. if (settings.polygons)
  1142. cell.polygons.push([ [bottom, 0], [top, 1], [1, 1], [1, 0] ]);
  1143. break;
  1144. case 3: /* 0011 */
  1145. left = settings.interpolate(x0, x3, threshold);
  1146. right = settings.interpolate(x1, x2, threshold);
  1147. if (settings.polygons_full) {
  1148. cell.edges.right = {
  1149. path: [ [1, right], [0, left] ],
  1150. move: {
  1151. x: -1,
  1152. y: 0,
  1153. enter: 'right'
  1154. }
  1155. };
  1156. }
  1157. if (settings.polygons)
  1158. cell.polygons.push([ [0, left], [0, 1], [1, 1], [1, right] ]);
  1159. break;
  1160. case 6: /* 0110 */
  1161. bottom = settings.interpolate(x0, x1, threshold);
  1162. top = settings.interpolate(x3, x2, threshold);
  1163. if (settings.polygons_full) {
  1164. cell.edges.top = {
  1165. path: [ [top, 1], [bottom, 0] ],
  1166. move: {
  1167. x: 0,
  1168. y: -1,
  1169. enter: 'top'
  1170. }
  1171. };
  1172. }
  1173. if (settings.polygons)
  1174. cell.polygons.push([ [0, 0], [0, 1], [top, 1], [bottom, 0] ]);
  1175. break;
  1176. case 10: /* 1010 */
  1177. left = settings.interpolate(x0, x3, threshold);
  1178. right = settings.interpolate(x1, x2, threshold);
  1179. bottom = settings.interpolate(x0, x1, threshold);
  1180. top = settings.interpolate(x3, x2, threshold);
  1181. average = (x0 + x1 + x2 + x3) / 4;
  1182. if (settings.polygons_full) {
  1183. if (average < threshold) {
  1184. cell.edges.left = {
  1185. path: [ [0, left], [top, 1] ],
  1186. move: {
  1187. x: 0,
  1188. y: 1,
  1189. enter: 'bottom'
  1190. }
  1191. };
  1192. cell.edges.right = {
  1193. path: [ [1, right], [bottom, 0] ],
  1194. move: {
  1195. x: 0,
  1196. y: -1,
  1197. enter: 'top'
  1198. }
  1199. };
  1200. } else {
  1201. cell.edges.right = {
  1202. path: [ [1, right], [top, 1] ],
  1203. move: {
  1204. x: 0,
  1205. y: 1,
  1206. enter: 'bottom'
  1207. }
  1208. };
  1209. cell.edges.left = {
  1210. path: [ [0, left], [bottom, 0] ],
  1211. move: {
  1212. x: 0,
  1213. y: -1,
  1214. enter: 'top'
  1215. }
  1216. };
  1217. }
  1218. }
  1219. if (settings.polygons) {
  1220. if (average < threshold) {
  1221. cell.polygons.push([ [0, 0], [0, left], [top, 1], [1, 1], [1, right], [bottom, 0] ]);
  1222. } else {
  1223. cell.polygons.push([ [0, 0], [0, left], [bottom, 0] ]);
  1224. cell.polygons.push([ [top, 1], [1, 1], [1, right] ]);
  1225. }
  1226. }
  1227. break;
  1228. case 5: /* 0101 */
  1229. left = settings.interpolate(x0, x3, threshold);
  1230. right = settings.interpolate(x1, x2, threshold);
  1231. bottom = settings.interpolate(x0, x1, threshold);
  1232. top = settings.interpolate(x3, x2, threshold);
  1233. average = (x0 + x1 + x2 + x3) / 4;
  1234. if (settings.polygons_full) {
  1235. if (average < threshold) {
  1236. cell.edges.bottom = {
  1237. path: [ [bottom, 0], [0, left] ],
  1238. move: {
  1239. x: -1,
  1240. y: 0,
  1241. enter: 'right'
  1242. }
  1243. };
  1244. cell.edges.top = {
  1245. path: [ [top, 1], [1, right] ],
  1246. move: {
  1247. x: 1,
  1248. y: 0,
  1249. enter: 'left'
  1250. }
  1251. };
  1252. } else {
  1253. cell.edges.top = {
  1254. path: [ [top, 1], [0, left] ],
  1255. move: {
  1256. x: -1,
  1257. y: 0,
  1258. enter: 'right'
  1259. }
  1260. };
  1261. cell.edges.bottom = {
  1262. path: [ [bottom, 0], [1, right] ],
  1263. move: {
  1264. x: 1,
  1265. y: 0,
  1266. enter: 'left'
  1267. }
  1268. };
  1269. }
  1270. }
  1271. if (settings.polygons) {
  1272. if (average < threshold) {
  1273. cell.polygons.push([ [0, left], [0, 1], [top, 1], [1, right], [1, 0], [bottom, 0] ]);
  1274. } else {
  1275. cell.polygons.push([ [0, left], [0, 1], [top, 1] ]);
  1276. cell.polygons.push([ [bottom, 0], [1, right], [1, 0] ]);
  1277. }
  1278. }
  1279. break;
  1280. }
  1281. return cell;
  1282. }
  1283. /* eslint no-console: ["error", { allow: ["log"] }] */
  1284. /*
  1285. * lookup table to generate polygon paths or edges required to
  1286. * trace the full polygon(s)
  1287. */
  1288. var shapeCoordinates = {
  1289. square: function(cell, x0, x1, x2, x3, opt) {
  1290. if (opt.polygons)
  1291. cell.polygons.push([ [0,0], [0, 1], [1, 1], [1, 0] ]);
  1292. },
  1293. triangle_bl: function(cell, x0, x1, x2, x3, opt) {
  1294. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1295. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1296. if (opt.polygons_full) {
  1297. cell.edges.lb = {
  1298. path: [ [0, leftbottom], [bottomleft, 0] ],
  1299. move: {
  1300. x: 0,
  1301. y: -1,
  1302. enter: 'tl'
  1303. }
  1304. };
  1305. }
  1306. if (opt.polygons)
  1307. cell.polygons.push([ [0, leftbottom], [bottomleft, 0], [0, 0] ]);
  1308. },
  1309. triangle_br: function(cell, x0, x1, x2, x3, opt) {
  1310. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1311. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1312. if (opt.polygons_full) {
  1313. cell.edges.br = {
  1314. path: [ [bottomright, 0], [1, rightbottom] ],
  1315. move: {
  1316. x: 1,
  1317. y: 0,
  1318. enter: 'lb'
  1319. }
  1320. };
  1321. }
  1322. if (opt.polygons)
  1323. cell.polygons.push([ [bottomright, 0], [1, rightbottom], [1, 0] ]);
  1324. },
  1325. triangle_tr: function(cell, x0, x1, x2, x3, opt) {
  1326. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1327. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1328. if (opt.polygons_full) {
  1329. cell.edges.rt = {
  1330. path: [ [1, righttop], [topright, 1] ],
  1331. move: {
  1332. x: 0,
  1333. y: 1,
  1334. enter: 'br'
  1335. }
  1336. };
  1337. }
  1338. if (opt.polygons)
  1339. cell.polygons.push([ [1, righttop], [topright, 1], [1, 1] ]);
  1340. },
  1341. triangle_tl: function(cell, x0, x1, x2, x3, opt) {
  1342. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1343. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1344. if (opt.polygons_full) {
  1345. cell.edges.tl = {
  1346. path: [ [topleft, 1], [0, lefttop] ],
  1347. move: {
  1348. x: -1,
  1349. y: 0,
  1350. enter: 'rt'
  1351. }
  1352. };
  1353. }
  1354. if (opt.polygons)
  1355. cell.polygons.push([ [0, lefttop], [0, 1], [topleft, 1] ]);
  1356. },
  1357. tetragon_t: function(cell, x0, x1, x2, x3, opt) {
  1358. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1359. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1360. if (opt.polygons_full) {
  1361. cell.edges.rt = {
  1362. path: [ [1, righttop], [0, lefttop] ],
  1363. move: {
  1364. x: -1,
  1365. y: 0,
  1366. enter: 'rt'
  1367. }
  1368. };
  1369. }
  1370. if (opt.polygons)
  1371. cell.polygons.push([ [0, lefttop], [0, 1], [1, 1], [1, righttop] ]);
  1372. },
  1373. tetragon_r: function(cell, x0, x1, x2, x3, opt) {
  1374. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1375. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1376. if (opt.polygons_full) {
  1377. cell.edges.br = {
  1378. path: [ [bottomright, 0], [topright, 1] ],
  1379. move: {
  1380. x: 0,
  1381. y: 1,
  1382. enter: 'br'
  1383. }
  1384. };
  1385. }
  1386. if (opt.polygons)
  1387. cell.polygons.push([ [bottomright, 0], [topright, 1], [1, 1], [1, 0] ]);
  1388. },
  1389. tetragon_b: function(cell, x0, x1, x2, x3, opt) {
  1390. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1391. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1392. if (opt.polygons_full) {
  1393. cell.edges.lb = {
  1394. path: [ [0, leftbottom], [1, rightbottom] ],
  1395. move: {
  1396. x: 1,
  1397. y: 0,
  1398. enter: 'lb'
  1399. }
  1400. };
  1401. }
  1402. if (opt.polygons)
  1403. cell.polygons.push([ [0, 0], [0, leftbottom], [1, rightbottom], [1, 0] ]);
  1404. },
  1405. tetragon_l: function(cell, x0, x1, x2, x3, opt) {
  1406. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1407. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1408. if (opt.polygons_full) {
  1409. cell.edges.tl = {
  1410. path: [ [topleft, 1], [bottomleft, 0] ],
  1411. move: {
  1412. x: 0,
  1413. y: -1,
  1414. enter: 'tl'
  1415. }
  1416. };
  1417. }
  1418. if (opt.polygons)
  1419. cell.polygons.push([ [0, 0], [0, 1], [topleft, 1], [bottomleft, 0] ]);
  1420. },
  1421. tetragon_bl: function(cell, x0, x1, x2, x3, opt) {
  1422. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  1423. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  1424. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  1425. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  1426. if (opt.polygons_full) {
  1427. cell.edges.bl = {
  1428. path: [ [bottomleft, 0], [0, leftbottom] ],
  1429. move: {
  1430. x: -1,
  1431. y: 0,
  1432. enter: 'rb'
  1433. }
  1434. };
  1435. cell.edges.lt = {
  1436. path: [ [0, lefttop], [bottomright, 0] ],
  1437. move: {
  1438. x: 0,
  1439. y: -1,
  1440. enter: 'tr'
  1441. }
  1442. };
  1443. }
  1444. if (opt.polygons)
  1445. cell.polygons.push([ [bottomleft, 0], [0, leftbottom], [0, lefttop], [bottomright, 0] ]);
  1446. },
  1447. tetragon_br: function(cell, x0, x1, x2, x3, opt) {
  1448. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  1449. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  1450. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  1451. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  1452. if (opt.polygons_full) {
  1453. cell.edges.bl = {
  1454. path: [ [bottomleft, 0], [1, righttop] ],
  1455. move: {
  1456. x: 1,
  1457. y: 0,
  1458. enter: 'lt'
  1459. }
  1460. };
  1461. cell.edges.rb = {
  1462. path: [ [1, rightbottom], [bottomright, 0] ],
  1463. move: {
  1464. x: 0,
  1465. y: -1,
  1466. enter: 'tr'
  1467. }
  1468. };
  1469. }
  1470. if (opt.polygons)
  1471. cell.polygons.push([ [bottomleft, 0], [1, righttop], [1, rightbottom], [bottomright, 0] ]);
  1472. },
  1473. tetragon_tr: function(cell, x0, x1, x2, x3, opt) {
  1474. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  1475. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  1476. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  1477. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  1478. if (opt.polygons_full) {
  1479. cell.edges.rb = {
  1480. path: [ [1, rightbottom], [topleft, 1] ],
  1481. move: {
  1482. x: 0,
  1483. y: 1,
  1484. enter: 'bl'
  1485. }
  1486. };
  1487. cell.edges.tr = {
  1488. path: [ [topright, 1], [1, righttop] ],
  1489. move: {
  1490. x: 1,
  1491. y: 0,
  1492. enter: 'lt'
  1493. }
  1494. };
  1495. }
  1496. if (opt.polygons)
  1497. cell.polygons.push([ [1, rightbottom], [topleft, 1], [topright, 1], [1, righttop] ]);
  1498. },
  1499. tetragon_tl: function(cell, x0, x1, x2, x3, opt) {
  1500. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  1501. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  1502. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  1503. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  1504. if (opt.polygons_full) {
  1505. cell.edges.tr = {
  1506. path: [ [topright, 1], [0, leftbottom] ],
  1507. move: {
  1508. x: -1,
  1509. y: 0,
  1510. enter: 'rb'
  1511. }
  1512. };
  1513. cell.edges.lt = {
  1514. path: [ [0, lefttop], [topleft, 1] ],
  1515. move: {
  1516. x: 0,
  1517. y: 1,
  1518. enter: 'bl'
  1519. }
  1520. };
  1521. }
  1522. if (opt.polygons)
  1523. cell.polygons.push([ [topright, 1], [0, leftbottom], [0, lefttop], [topleft, 1] ]);
  1524. },
  1525. tetragon_lr: function(cell, x0, x1, x2, x3, opt) {
  1526. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  1527. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  1528. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  1529. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  1530. if (opt.polygons_full) {
  1531. cell.edges.lt = {
  1532. path: [ [0, lefttop], [1, righttop] ],
  1533. move: {
  1534. x: 1,
  1535. y: 0,
  1536. enter: 'lt'
  1537. }
  1538. };
  1539. cell.edges.rb = {
  1540. path: [ [1, rightbottom], [0, leftbottom] ],
  1541. move: {
  1542. x: -1,
  1543. y: 0,
  1544. enter: 'rb'
  1545. }
  1546. };
  1547. }
  1548. if (opt.polygons)
  1549. cell.polygons.push([ [0, leftbottom], [0, lefttop], [1, righttop], [1, rightbottom] ]);
  1550. },
  1551. tetragon_tb: function(cell, x0, x1, x2, x3, opt) {
  1552. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  1553. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  1554. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  1555. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  1556. if (opt.polygons_full) {
  1557. cell.edges.tr = {
  1558. path: [ [topright, 1], [bottomright, 0] ],
  1559. move: {
  1560. x: 0,
  1561. y: -1,
  1562. enter: 'tr'
  1563. }
  1564. };
  1565. cell.edges.bl = {
  1566. path: [ [bottomleft, 0], [topleft, 1] ],
  1567. move: {
  1568. x: 0,
  1569. y: 1,
  1570. enter: 'bl'
  1571. }
  1572. };
  1573. }
  1574. if (opt.polygons)
  1575. cell.polygons.push([ [bottomleft, 0], [topleft, 1], [topright, 1], [bottomright, 0] ]);
  1576. },
  1577. pentagon_tr: function(cell, x0, x1, x2, x3, opt) {
  1578. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1579. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1580. if (opt.polygons_full) {
  1581. cell.edges.tl = {
  1582. path: [[topleft, 1], [1, rightbottom]],
  1583. move: {
  1584. x: 1,
  1585. y: 0,
  1586. enter: 'lb'
  1587. }
  1588. };
  1589. }
  1590. if (opt.polygons)
  1591. cell.polygons.push([ [0, 0], [0, 1], [topleft, 1], [1, rightbottom], [1, 0] ]);
  1592. },
  1593. pentagon_tl: function(cell, x0, x1, x2, x3, opt) {
  1594. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1595. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1596. if (opt.polygons_full) {
  1597. cell.edges.lb = {
  1598. path: [ [0, leftbottom], [topright, 1] ],
  1599. move: {
  1600. x: 0,
  1601. y: 1,
  1602. enter: 'br'
  1603. }
  1604. };
  1605. }
  1606. if (opt.polygons)
  1607. cell.polygons.push([ [0, 0], [0, leftbottom], [topright, 1], [1, 1], [1, 0] ]);
  1608. },
  1609. pentagon_br: function(cell, x0, x1, x2, x3, opt) {
  1610. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1611. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1612. if (opt.polygons_full) {
  1613. cell.edges.rt = {
  1614. path: [ [1, righttop], [bottomleft, 0] ],
  1615. move: {
  1616. x: 0,
  1617. y: -1,
  1618. enter: 'tl'
  1619. }
  1620. };
  1621. }
  1622. if (opt.polygons)
  1623. cell.polygons.push([ [0, 0], [0, 1], [1, 1], [1, righttop], [bottomleft, 0] ]);
  1624. },
  1625. pentagon_bl: function(cell, x0, x1, x2, x3, opt) {
  1626. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1627. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1628. if (opt.polygons_full) {
  1629. cell.edges.br = {
  1630. path: [ [bottomright, 0], [0, lefttop] ],
  1631. move: {
  1632. x: -1,
  1633. y: 0,
  1634. enter: 'rt'
  1635. }
  1636. };
  1637. }
  1638. if (opt.polygons)
  1639. cell.polygons.push([ [0, lefttop], [0, 1], [1, 1], [1, 0], [bottomright, 0] ]);
  1640. },
  1641. pentagon_tr_rl: function(cell, x0, x1, x2, x3, opt) {
  1642. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1643. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1644. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  1645. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  1646. if (opt.polygons_full) {
  1647. cell.edges.tl = {
  1648. path: [ [topleft, 1], [1, righttop] ],
  1649. move: {
  1650. x: 1,
  1651. y: 0,
  1652. enter: 'lt'
  1653. }
  1654. };
  1655. cell.edges.rb = {
  1656. path: [ [1, rightbottom], [0, lefttop] ],
  1657. move: {
  1658. x: -1,
  1659. y: 0,
  1660. enter: 'rt'
  1661. }
  1662. };
  1663. }
  1664. if (opt.polygons)
  1665. cell.polygons.push([ [0, lefttop], [0, 1], [topleft, 1], [1, righttop], [1, rightbottom] ]);
  1666. },
  1667. pentagon_rb_bt: function(cell, x0, x1, x2, x3, opt) {
  1668. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1669. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  1670. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  1671. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1672. if (opt.polygons_full) {
  1673. cell.edges.rt = {
  1674. path: [ [1, righttop], [bottomright, 0] ],
  1675. move: {
  1676. x: 0,
  1677. y: -1,
  1678. enter: 'tr'
  1679. }
  1680. };
  1681. cell.edges.bl = {
  1682. path: [ [bottomleft, 0], [topright, 1] ],
  1683. move: {
  1684. x: 0,
  1685. y: 1,
  1686. enter: 'br'
  1687. }
  1688. };
  1689. }
  1690. if (opt.polygons)
  1691. cell.polygons.push([ [topright, 1], [1, 1], [1, righttop], [bottomright, 0], [bottomleft, 0] ]);
  1692. },
  1693. pentagon_bl_lr: function(cell, x0, x1, x2, x3, opt) {
  1694. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1695. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  1696. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  1697. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1698. if (opt.polygons_full) {
  1699. cell.edges.br = {
  1700. path: [ [bottomright, 0], [0, leftbottom] ],
  1701. move: {
  1702. x: -1,
  1703. y: 0,
  1704. enter: 'rb'
  1705. }
  1706. };
  1707. cell.edges.lt = {
  1708. path: [ [0, lefttop], [1, rightbottom] ],
  1709. move: {
  1710. x: 1,
  1711. y: 0,
  1712. enter: 'lb'
  1713. }
  1714. };
  1715. }
  1716. if (opt.polygons)
  1717. cell.polygons.push([ [bottomright, 0], [0, leftbottom], [0, lefttop], [1, rightbottom], [1, 0] ]);
  1718. },
  1719. pentagon_lt_tb: function(cell, x0, x1, x2, x3, opt) {
  1720. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1721. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  1722. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  1723. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1724. if (opt.polygons_full) {
  1725. cell.edges.lb = {
  1726. path: [ [0, leftbottom], [topleft, 1] ],
  1727. move: {
  1728. x: 0,
  1729. y: 1,
  1730. enter: 'bl'
  1731. }
  1732. };
  1733. cell.edges.tr = {
  1734. path: [ [topright, 1], [bottomleft, 0] ],
  1735. move: {
  1736. x: 0,
  1737. y: -1,
  1738. enter: 'tl'
  1739. }
  1740. };
  1741. }
  1742. if (opt.polygons)
  1743. cell.polygons.push([ [0, 0], [0, leftbottom], [topleft, 1], [topright, 1], [bottomleft, 0] ]);
  1744. },
  1745. pentagon_bl_tb: function(cell, x0, x1, x2, x3, opt) {
  1746. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1747. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1748. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  1749. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  1750. if (opt.polygons_full) {
  1751. cell.edges.bl = {
  1752. path: [ [bottomleft, 0], [0, lefttop] ],
  1753. move: {
  1754. x: -1,
  1755. y: 0,
  1756. enter: 'rt'
  1757. }
  1758. };
  1759. cell.edges.tl = {
  1760. path: [ [ topleft, 1], [bottomright, 0] ],
  1761. move: {
  1762. x: 0,
  1763. y: -1,
  1764. enter: 'tr'
  1765. }
  1766. };
  1767. }
  1768. if (opt.polygons)
  1769. cell.polygons.push([ [0, lefttop], [0, 1], [topleft, 1], [bottomright, 0], [bottomleft, 0] ]);
  1770. },
  1771. pentagon_lt_rl: function(cell, x0, x1, x2, x3, opt) {
  1772. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  1773. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  1774. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1775. var righttop = opt.interpolate(x1, x3, opt.minV, opt.maxV);
  1776. if (opt.polygons_full) {
  1777. cell.edges.lt = {
  1778. path: [ [0, lefttop], [topright, 1] ],
  1779. move: {
  1780. x: 0,
  1781. y: 1,
  1782. enter: 'br'
  1783. }
  1784. };
  1785. cell.edges.rt = {
  1786. path: [ [1, righttop], [0, leftbottom] ],
  1787. move: {
  1788. x: -1,
  1789. y: 0,
  1790. enter: 'rb'
  1791. }
  1792. };
  1793. }
  1794. if (opt.polygons)
  1795. cell.polygons.push([ [0, leftbottom], [0, lefttop], [topright, 1], [1, 1], [1, righttop] ]);
  1796. },
  1797. pentagon_tr_bt: function(cell, x0, x1, x2, x3, opt) {
  1798. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  1799. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  1800. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1801. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1802. if (opt.polygons_full) {
  1803. cell.edges.br = {
  1804. path: [ [bottomright, 0], [topleft, 1] ],
  1805. move: {
  1806. x: 0,
  1807. y: 1,
  1808. enter: 'bl'
  1809. }
  1810. };
  1811. cell.edges.tr = {
  1812. path: [ [topright, 1], [1, rightbottom] ],
  1813. move: {
  1814. x: 1,
  1815. y: 0,
  1816. enter: 'lb'
  1817. }
  1818. };
  1819. }
  1820. if (opt.polygons)
  1821. cell.polygons.push([ [topleft, 1], [topright, 1], [1, rightbottom], [1, 0], [bottomright, 0] ]);
  1822. },
  1823. pentagon_rb_lr: function(cell, x0, x1, x2, x3, opt) {
  1824. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1825. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  1826. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  1827. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1828. if (opt.polygons_full) {
  1829. cell.edges.lb = {
  1830. path: [ [0, leftbottom], [1, righttop] ],
  1831. move: {
  1832. x: 1,
  1833. y: 0,
  1834. enter: 'lt'
  1835. }
  1836. };
  1837. cell.edges.rb = {
  1838. path: [ [1, rightbottom], [bottomleft, 0] ],
  1839. move: {
  1840. x: 0,
  1841. y: -1,
  1842. enter: 'tl'
  1843. }
  1844. };
  1845. }
  1846. if (opt.polygons)
  1847. cell.polygons.push([ [0, 0], [0, leftbottom], [1, righttop], [1, rightbottom], [bottomleft, 0] ]);
  1848. },
  1849. hexagon_lt_tr: function(cell, x0, x1, x2, x3, opt) {
  1850. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1851. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  1852. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  1853. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1854. if (opt.polygons_full) {
  1855. cell.edges.lb = {
  1856. path: [ [0, leftbottom], [topleft, 1] ],
  1857. move: {
  1858. x: 0,
  1859. y: 1,
  1860. enter: 'bl'
  1861. }
  1862. };
  1863. cell.edges.tr = {
  1864. path: [ [topright, 1], [1, rightbottom] ],
  1865. move: {
  1866. x: 1,
  1867. y: 0,
  1868. enter: 'lb'
  1869. }
  1870. };
  1871. }
  1872. if (opt.polygons)
  1873. cell.polygons.push([ [0, 0], [0, leftbottom], [topleft, 1], [topright, 1], [1, rightbottom], [1, 0] ]);
  1874. },
  1875. hexagon_bl_lt: function(cell, x0, x1, x2, x3, opt) {
  1876. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1877. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  1878. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  1879. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1880. if (opt.polygons_full) {
  1881. cell.edges.br = {
  1882. path: [ [bottomright, 0], [0, leftbottom] ],
  1883. move: {
  1884. x: -1,
  1885. y: 0,
  1886. enter: 'rb'
  1887. }
  1888. };
  1889. cell.edges.lt = {
  1890. path: [ [0, lefttop], [topright, 1] ],
  1891. move: {
  1892. x: 0,
  1893. y: 1,
  1894. enter: 'br'
  1895. }
  1896. };
  1897. }
  1898. if (opt.polygons)
  1899. cell.polygons.push([ [bottomright, 0], [0, leftbottom], [0, lefttop], [topright, 1], [1, 1], [1, 0] ]);
  1900. },
  1901. hexagon_bl_rb: function(cell, x0, x1, x2, x3, opt) {
  1902. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  1903. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  1904. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1905. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1906. if (opt.polygons_full) {
  1907. cell.edges.bl = {
  1908. path: [ [bottomleft, 0], [0, lefttop] ],
  1909. move: {
  1910. x: -1,
  1911. y: 0,
  1912. enter: 'rt'
  1913. }
  1914. };
  1915. cell.edges.rt = {
  1916. path: [ [1, righttop], [bottomright, 0] ],
  1917. move: {
  1918. x: 0,
  1919. y: -1,
  1920. enter: 'tr'
  1921. }
  1922. };
  1923. }
  1924. if (opt.polygons)
  1925. cell.polygons.push([ [bottomleft, 0], [0, lefttop], [0, 1], [1, 1], [1, righttop], [bottomright, 0] ]);
  1926. },
  1927. hexagon_tr_rb: function(cell, x0, x1, x2, x3, opt) {
  1928. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1929. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1930. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  1931. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  1932. if (opt.polygons_full) {
  1933. cell.edges.tl = {
  1934. path: [ [topleft, 1], [1, righttop] ],
  1935. move: {
  1936. x: 1,
  1937. y: 0,
  1938. enter: 'lt'
  1939. }
  1940. };
  1941. cell.edges.rb = {
  1942. path: [ [1, rightbottom], [bottomleft, 0] ],
  1943. move: {
  1944. x: 0,
  1945. y: -1,
  1946. enter: 'tl'
  1947. }
  1948. };
  1949. }
  1950. if (opt.polygons)
  1951. cell.polygons.push([ [0, 0], [0, 1], [topleft, 1], [1, righttop], [1, rightbottom], [bottomleft, 0] ]);
  1952. },
  1953. hexagon_lt_rb: function(cell, x0, x1, x2, x3, opt) {
  1954. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1955. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1956. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1957. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1958. if (opt.polygons_full) {
  1959. cell.edges.lb = {
  1960. path: [ [0, leftbottom], [topright, 1] ],
  1961. move: {
  1962. x: 0,
  1963. y: 1,
  1964. enter: 'br'
  1965. }
  1966. };
  1967. cell.edges.rt = {
  1968. path: [ [1, righttop], [bottomleft, 0] ],
  1969. move: {
  1970. x: 0,
  1971. y: -1,
  1972. enter: 'tl'
  1973. }
  1974. };
  1975. }
  1976. if (opt.polygons)
  1977. cell.polygons.push([ [0, 0], [0, leftbottom], [topright, 1], [1, 1], [1, righttop], [bottomleft, 0] ]);
  1978. },
  1979. hexagon_bl_tr: function(cell, x0, x1, x2, x3, opt) {
  1980. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  1981. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  1982. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  1983. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  1984. if (opt.polygons_full) {
  1985. cell.edges.br = {
  1986. path: [ [bottomright, 0], [0, lefttop] ],
  1987. move: {
  1988. x: -1,
  1989. y: 0,
  1990. enter: 'rt'
  1991. }
  1992. };
  1993. cell.edges.tl = {
  1994. path: [ [topleft, 1], [1, rightbottom] ],
  1995. move: {
  1996. x: 1,
  1997. y: 0,
  1998. enter: 'lb'
  1999. }
  2000. };
  2001. }
  2002. if (opt.polygons)
  2003. cell.polygons.push([ [bottomright, 0], [0, lefttop], [0, 1], [topleft, 1], [1, rightbottom], [1, 0] ]);
  2004. },
  2005. heptagon_tr: function(cell, x0, x1, x2, x3, opt) {
  2006. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  2007. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  2008. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  2009. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  2010. var topright = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  2011. var righttop = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  2012. if (opt.polygons_full) {
  2013. cell.edges.bl = {
  2014. path: [ [bottomleft, 0], [0, leftbottom] ],
  2015. move: {
  2016. x: -1,
  2017. y: 0,
  2018. enter: 'rb'
  2019. }
  2020. };
  2021. cell.edges.lt = {
  2022. path: [ [0, lefttop], [topright, 1] ],
  2023. move: {
  2024. x: 0,
  2025. y: 1,
  2026. enter: 'br'
  2027. }
  2028. };
  2029. cell.edges.rt = {
  2030. path: [ [1, righttop], [bottomright, 0] ],
  2031. move: {
  2032. x: 0,
  2033. y: -1,
  2034. enter: 'tr'
  2035. }
  2036. };
  2037. }
  2038. if (opt.polygons)
  2039. cell.polygons.push([ [bottomleft, 0], [0, leftbottom], [0, lefttop], [topright, 1], [1, 1], [1, righttop], [bottomright, 0] ]);
  2040. },
  2041. heptagon_bl: function(cell, x0, x1, x2, x3, opt) {
  2042. var bottomleft = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  2043. var leftbottom = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  2044. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  2045. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  2046. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  2047. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  2048. if (opt.polygons_full) {
  2049. cell.edges.lb = {
  2050. path: [ [0, leftbottom], [topleft, 1] ],
  2051. move: {
  2052. x: 0,
  2053. y: 1,
  2054. enter: 'bl'
  2055. }
  2056. };
  2057. cell.edges.tr = {
  2058. path: [ [topright, 1], [1, righttop] ],
  2059. move: {
  2060. x: 1,
  2061. y: 0,
  2062. enter: 'lt'
  2063. }
  2064. };
  2065. cell.edges.rb = {
  2066. path: [ [1, rightbottom], [bottomleft, 0] ],
  2067. move: {
  2068. x: 0,
  2069. y: -1,
  2070. enter: 'tl'
  2071. }
  2072. };
  2073. }
  2074. if (opt.polygons)
  2075. cell.polygons.push([ [0, 0], [0, leftbottom], [topleft, 1], [topright, 1], [1, righttop], [1, rightbottom], [bottomleft, 0] ]);
  2076. },
  2077. heptagon_tl: function(cell, x0, x1, x2, x3, opt) {
  2078. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  2079. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  2080. var lefttop = opt.interpolate(x0, x3, opt.minV, opt.maxV);
  2081. var topleft = opt.interpolate(x3, x2, opt.minV, opt.maxV);
  2082. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  2083. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  2084. if (opt.polygons_full) {
  2085. cell.edges.bl = {
  2086. path: [ [bottomleft, 0], [0, lefttop] ],
  2087. move: {
  2088. x: -1,
  2089. y: 0,
  2090. enter: 'rt'
  2091. }
  2092. };
  2093. cell.edges.tl = {
  2094. path: [ [topleft, 1], [1, righttop] ],
  2095. move: {
  2096. x: 1,
  2097. y: 0,
  2098. enter: 'lt'
  2099. }
  2100. };
  2101. cell.edges.rb = {
  2102. path: [ [1, rightbottom], [bottomright, 0] ],
  2103. move: {
  2104. x: 0,
  2105. y: -1,
  2106. enter: 'tr'
  2107. }
  2108. };
  2109. }
  2110. if (opt.polygons)
  2111. cell.polygons.push([ [bottomleft, 0], [0, lefttop], [0, 1], [topleft, 1], [1, righttop], [1, rightbottom], [bottomright, 0] ]);
  2112. },
  2113. heptagon_br: function(cell, x0, x1, x2, x3, opt) {
  2114. var bottomright = opt.interpolate(x0, x1, opt.minV, opt.maxV);
  2115. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  2116. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  2117. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  2118. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  2119. var rightbottom = opt.interpolate(x1, x2, opt.minV, opt.maxV);
  2120. if (opt.polygons_full) {
  2121. cell.edges.br = {
  2122. path: [ [bottomright, 0], [0, leftbottom] ],
  2123. move: {
  2124. x: -1,
  2125. y: 0,
  2126. enter: 'rb'
  2127. }
  2128. };
  2129. cell.edges.lt = {
  2130. path: [ [0, lefttop], [topleft, 1] ],
  2131. move: {
  2132. x: 0,
  2133. y: 1,
  2134. enter: 'bl'
  2135. }
  2136. };
  2137. cell.edges.tr = {
  2138. path: [ [topright, 1], [1, rightbottom] ],
  2139. move: {
  2140. x: 1,
  2141. y: 0,
  2142. enter: 'lb'
  2143. }
  2144. };
  2145. }
  2146. if (opt.polygons)
  2147. cell.polygons.push([ [bottomright,0], [0, leftbottom], [0, lefttop], [topleft, 1], [topright, 1], [1, rightbottom], [1, 0] ]);
  2148. },
  2149. octagon: function(cell, x0, x1, x2, x3, opt) {
  2150. var bottomleft = opt.interpolate_a(x0, x1, opt.minV, opt.maxV);
  2151. var bottomright = opt.interpolate_b(x0, x1, opt.minV, opt.maxV);
  2152. var leftbottom = opt.interpolate_a(x0, x3, opt.minV, opt.maxV);
  2153. var lefttop = opt.interpolate_b(x0, x3, opt.minV, opt.maxV);
  2154. var topleft = opt.interpolate_a(x3, x2, opt.minV, opt.maxV);
  2155. var topright = opt.interpolate_b(x3, x2, opt.minV, opt.maxV);
  2156. var righttop = opt.interpolate_b(x1, x2, opt.minV, opt.maxV);
  2157. var rightbottom = opt.interpolate_a(x1, x2, opt.minV, opt.maxV);
  2158. if (opt.polygons_full) {
  2159. cell.edges.bl = {
  2160. path: [ [bottomleft, 0], [0, leftbottom] ],
  2161. move: {
  2162. x: -1,
  2163. y: 0,
  2164. enter: 'rb'
  2165. }
  2166. };
  2167. cell.edges.lt = {
  2168. path: [ [0, lefttop], [topleft, 1] ],
  2169. move: {
  2170. x: 0,
  2171. y: 1,
  2172. enter: 'bl'
  2173. }
  2174. };
  2175. cell.edges.tr = {
  2176. path: [ [topright, 1], [1, righttop] ],
  2177. move: {
  2178. x: 1,
  2179. y: 0,
  2180. enter: 'lt'
  2181. }
  2182. };
  2183. cell.edges.rb = {
  2184. path: [ [1, rightbottom], [bottomright, 0] ],
  2185. move: {
  2186. x: 0,
  2187. y: -1,
  2188. enter: 'tr'
  2189. }
  2190. };
  2191. }
  2192. if (opt.polygons)
  2193. cell.polygons.push([ [bottomleft, 0], [0, leftbottom], [0, lefttop], [topleft, 1], [topright, 1], [1, righttop], [1, rightbottom], [bottomright, 0] ]);
  2194. }
  2195. };
  2196. /*
  2197. * Compute isobands(s) for a scalar 2D field given a certain
  2198. * threshold and a bandwidth by applying the Marching Squares
  2199. * Algorithm. The function returns a list of path coordinates
  2200. * either for individual polygons within each grid cell, or the
  2201. * outline of connected polygons.
  2202. */
  2203. function isoBands(input, minV, bandWidth, options) {
  2204. var i,
  2205. j,
  2206. settings,
  2207. useQuadTree = false,
  2208. tree = null,
  2209. root = null,
  2210. data = null,
  2211. cellGrid = null,
  2212. multiBand = false,
  2213. bw = [],
  2214. bandPolygons = [],
  2215. ret = [];
  2216. /* basic input validation */
  2217. if (!input) throw new Error('data is required');
  2218. if (minV === undefined || minV === null) throw new Error('lowerBound is required');
  2219. if (bandWidth === undefined || bandWidth === null) throw new Error('bandWidth is required');
  2220. if ((!!options) && (typeof options !== 'object')) throw new Error('options must be an object');
  2221. settings = isoBandOptions(options);
  2222. /* check for input data */
  2223. if (input instanceof QuadTree) {
  2224. tree = input;
  2225. root = input.root;
  2226. data = input.data;
  2227. if (!settings.noQuadTree)
  2228. useQuadTree = true;
  2229. } else if (Array.isArray(input) && Array.isArray(input[0])) {
  2230. data = input;
  2231. } else {
  2232. throw new Error('input is neither array of arrays nor object retrieved from \'QuadTree()\'');
  2233. }
  2234. /* check and prepare input thresholds */
  2235. if (Array.isArray(minV)) {
  2236. multiBand = true;
  2237. /* activate QuadTree optimization if not explicitly forbidden by user settings */
  2238. if (!settings.noQuadTree)
  2239. useQuadTree = true;
  2240. /* check if all minV are numbers */
  2241. for (i = 0; i < minV.length; i++)
  2242. if (isNaN(+minV[i]))
  2243. throw new Error('lowerBound[' + i + '] is not a number');
  2244. if (Array.isArray(bandWidth)) {
  2245. if (minV.length !== bandWidth.length)
  2246. throw new Error('lowerBound and bandWidth have unequal lengths');
  2247. /* check bandwidth values */
  2248. for (i = 0; i < bandWidth.length; i++)
  2249. if (isNaN(+bandWidth[i]))
  2250. throw new Error('bandWidth[' + i + '] is not a number');
  2251. } else {
  2252. if (isNaN(+bandWidth))
  2253. throw new Error('bandWidth must be a number');
  2254. bw = [];
  2255. for (i = 0; i < minV.length; i++) {
  2256. bw.push(bandWidth);
  2257. }
  2258. bandWidth = bw;
  2259. }
  2260. } else {
  2261. if (isNaN(+minV))
  2262. throw new Error('lowerBound must be a number');
  2263. minV = [ minV ];
  2264. if (isNaN(+bandWidth))
  2265. throw new Error('bandWidth must be a number');
  2266. bandWidth = [ bandWidth ];
  2267. }
  2268. /* create QuadTree root node if not already present */
  2269. if ((useQuadTree) && (!root)) {
  2270. tree = new QuadTree(data);
  2271. root = tree.root;
  2272. data = tree.data;
  2273. }
  2274. if (settings.verbose) {
  2275. if(settings.polygons)
  2276. console.log('MarchingSquaresJS-isoBands: returning single polygons for each grid cell');
  2277. else
  2278. console.log('MarchingSquaresJS-isoBands: returning polygon paths for entire data grid');
  2279. if (multiBand)
  2280. console.log('MarchingSquaresJS-isoBands: multiple bands requested, returning array of band polygons instead of polygons for a single band');
  2281. }
  2282. /* Done with all input validation, now let's start computing stuff */
  2283. /* loop over all minV values */
  2284. minV.forEach(function(lowerBound, b) {
  2285. bandPolygons = [];
  2286. /* store bounds for current computation in settings object */
  2287. settings.minV = lowerBound;
  2288. settings.maxV = lowerBound + bandWidth[b];
  2289. if(settings.verbose)
  2290. console.log('MarchingSquaresJS-isoBands: computing isobands for [' + lowerBound + ':' + (lowerBound + bandWidth[b]) + ']');
  2291. if (settings.polygons) {
  2292. /* compose list of polygons for each single cell */
  2293. if (useQuadTree) {
  2294. /* go through list of cells retrieved from QuadTree */
  2295. root
  2296. .cellsInBand(settings.minV, settings.maxV, true)
  2297. .forEach(function(c) {
  2298. bandPolygons = bandPolygons.concat(
  2299. cell2Polygons(
  2300. prepareCell$1(data,
  2301. c.x,
  2302. c.y,
  2303. settings),
  2304. c.x,
  2305. c.y,
  2306. settings
  2307. ));
  2308. });
  2309. } else {
  2310. /* go through entire array of input data */
  2311. for (j = 0; j < data.length - 1; ++j) {
  2312. for (i = 0; i < data[0].length - 1; ++i)
  2313. bandPolygons = bandPolygons.concat(
  2314. cell2Polygons(
  2315. prepareCell$1(data,
  2316. i,
  2317. j,
  2318. settings),
  2319. i,
  2320. j,
  2321. settings
  2322. ));
  2323. }
  2324. }
  2325. } else {
  2326. /* sparse grid of input data cells */
  2327. cellGrid = [];
  2328. for (i = 0; i < data[0].length - 1; ++i)
  2329. cellGrid[i] = [];
  2330. /* compose list of polygons for entire input grid */
  2331. if (useQuadTree) {
  2332. /* collect the cells */
  2333. root
  2334. .cellsInBand(settings.minV, settings.maxV, false)
  2335. .forEach(function(c) {
  2336. cellGrid[c.x][c.y] = prepareCell$1(data,
  2337. c.x,
  2338. c.y,
  2339. settings);
  2340. });
  2341. } else {
  2342. /* prepare cells */
  2343. for (i = 0; i < data[0].length - 1; ++i) {
  2344. for (j = 0; j < data.length - 1; ++j) {
  2345. cellGrid[i][j] = prepareCell$1(data,
  2346. i,
  2347. j,
  2348. settings);
  2349. }
  2350. }
  2351. }
  2352. bandPolygons = traceBandPaths(data, cellGrid, settings);
  2353. }
  2354. /* finally, add polygons to output array */
  2355. if (multiBand)
  2356. ret.push(bandPolygons);
  2357. else
  2358. ret = bandPolygons;
  2359. if(typeof settings.successCallback === 'function')
  2360. settings.successCallback(ret, lowerBound, bandWidth[b]);
  2361. });
  2362. return ret;
  2363. }
  2364. /*
  2365. * Thats all for the public interface, below follows the actual
  2366. * implementation
  2367. */
  2368. /*
  2369. * For isoBands, each square is defined by the three states
  2370. * of its corner points. However, since computers use power-2
  2371. * values, we use 2bits per trit, i.e.:
  2372. *
  2373. * 00 ... below minV
  2374. * 01 ... between minV and maxV
  2375. * 10 ... above maxV
  2376. *
  2377. * Hence we map the 4-trit configurations as follows:
  2378. *
  2379. * 0000 => 0
  2380. * 0001 => 1
  2381. * 0002 => 2
  2382. * 0010 => 4
  2383. * 0011 => 5
  2384. * 0012 => 6
  2385. * 0020 => 8
  2386. * 0021 => 9
  2387. * 0022 => 10
  2388. * 0100 => 16
  2389. * 0101 => 17
  2390. * 0102 => 18
  2391. * 0110 => 20
  2392. * 0111 => 21
  2393. * 0112 => 22
  2394. * 0120 => 24
  2395. * 0121 => 25
  2396. * 0122 => 26
  2397. * 0200 => 32
  2398. * 0201 => 33
  2399. * 0202 => 34
  2400. * 0210 => 36
  2401. * 0211 => 37
  2402. * 0212 => 38
  2403. * 0220 => 40
  2404. * 0221 => 41
  2405. * 0222 => 42
  2406. * 1000 => 64
  2407. * 1001 => 65
  2408. * 1002 => 66
  2409. * 1010 => 68
  2410. * 1011 => 69
  2411. * 1012 => 70
  2412. * 1020 => 72
  2413. * 1021 => 73
  2414. * 1022 => 74
  2415. * 1100 => 80
  2416. * 1101 => 81
  2417. * 1102 => 82
  2418. * 1110 => 84
  2419. * 1111 => 85
  2420. * 1112 => 86
  2421. * 1120 => 88
  2422. * 1121 => 89
  2423. * 1122 => 90
  2424. * 1200 => 96
  2425. * 1201 => 97
  2426. * 1202 => 98
  2427. * 1210 => 100
  2428. * 1211 => 101
  2429. * 1212 => 102
  2430. * 1220 => 104
  2431. * 1221 => 105
  2432. * 1222 => 106
  2433. * 2000 => 128
  2434. * 2001 => 129
  2435. * 2002 => 130
  2436. * 2010 => 132
  2437. * 2011 => 133
  2438. * 2012 => 134
  2439. * 2020 => 136
  2440. * 2021 => 137
  2441. * 2022 => 138
  2442. * 2100 => 144
  2443. * 2101 => 145
  2444. * 2102 => 146
  2445. * 2110 => 148
  2446. * 2111 => 149
  2447. * 2112 => 150
  2448. * 2120 => 152
  2449. * 2121 => 153
  2450. * 2122 => 154
  2451. * 2200 => 160
  2452. * 2201 => 161
  2453. * 2202 => 162
  2454. * 2210 => 164
  2455. * 2211 => 165
  2456. * 2212 => 166
  2457. * 2220 => 168
  2458. * 2221 => 169
  2459. * 2222 => 170
  2460. */
  2461. /*
  2462. * ####################################
  2463. * Some small helper functions
  2464. * ####################################
  2465. */
  2466. function computeCenterAverage(bl, br, tr, tl, minV, maxV) {
  2467. var average = (tl + tr + br + bl) / 4;
  2468. if (average > maxV)
  2469. return 2; /* above isoband limits */
  2470. if (average < minV)
  2471. return 0; /* below isoband limits */
  2472. return 1; /* within isoband limits */
  2473. }
  2474. function prepareCell$1(grid, x, y, opt) {
  2475. var cell,
  2476. center_avg;
  2477. /* compose the 4-trit corner representation */
  2478. var cval = 0;
  2479. var x3 = grid[y + 1][x];
  2480. var x2 = grid[y + 1][x + 1];
  2481. var x1 = grid[y][x + 1];
  2482. var x0 = grid[y][x];
  2483. var minV = opt.minV;
  2484. var maxV = opt.maxV;
  2485. /*
  2486. * Note that missing data within the grid will result
  2487. * in horribly failing to trace full polygon paths
  2488. */
  2489. if(isNaN(x0) || isNaN(x1) || isNaN(x2) || isNaN(x3)) {
  2490. return;
  2491. }
  2492. /*
  2493. * Here we detect the type of the cell
  2494. *
  2495. * x3 ---- x2
  2496. * | |
  2497. * | |
  2498. * x0 ---- x1
  2499. *
  2500. * with edge points
  2501. *
  2502. * x0 = (x,y),
  2503. * x1 = (x + 1, y),
  2504. * x2 = (x + 1, y + 1), and
  2505. * x3 = (x, y + 1)
  2506. *
  2507. * and compute the polygon intersections with the edges
  2508. * of the cell. Each edge value may be (i) below, (ii) within,
  2509. * or (iii) above the values of the isoband limits. We
  2510. * encode this property using 2 bits of information, where
  2511. *
  2512. * 00 ... below,
  2513. * 01 ... within, and
  2514. * 10 ... above
  2515. *
  2516. * Then we store the cells value as vector
  2517. *
  2518. * cval = (x0, x1, x2, x3)
  2519. *
  2520. * where x0 are the two least significant bits (0th, 1st),
  2521. * x1 the 2nd and 3rd bit, and so on. This essentially
  2522. * enables us to work with a single integer number
  2523. */
  2524. cval |= (x3 < minV) ? 0 : (x3 > maxV) ? 128 : 64;
  2525. cval |= (x2 < minV) ? 0 : (x2 > maxV) ? 32 : 16;
  2526. cval |= (x1 < minV) ? 0 : (x1 > maxV) ? 8 : 4;
  2527. cval |= (x0 < minV) ? 0 : (x0 > maxV) ? 2 : 1;
  2528. /* make sure cval is a number */
  2529. cval = +cval;
  2530. /*
  2531. * cell center average trit for ambiguous cases, where
  2532. * 0 ... below iso band
  2533. * 1 ... within iso band
  2534. * 2 ... above isoband
  2535. */
  2536. center_avg = 0;
  2537. cell = {
  2538. cval: cval,
  2539. polygons: [],
  2540. edges: {},
  2541. x0: x0,
  2542. x1: x1,
  2543. x2: x2,
  2544. x3: x3,
  2545. x: x,
  2546. y: y
  2547. };
  2548. /*
  2549. * Compute interpolated intersections of the polygon(s)
  2550. * with the cell borders and (i) add edges for polygon
  2551. * trace-back, or (ii) a list of small closed polygons
  2552. * according to look-up table
  2553. */
  2554. switch (cval) {
  2555. case 85: /* 1111 */
  2556. shapeCoordinates.square(cell, x0, x1, x2, x3, opt);
  2557. /* fall through */
  2558. case 0: /* 0000 */
  2559. /* fall through */
  2560. case 170: /* 2222 */
  2561. break;
  2562. /* single triangle cases */
  2563. case 169: /* 2221 */
  2564. shapeCoordinates.triangle_bl(cell, x0, x1, x2, x3, opt);
  2565. break;
  2566. case 166: /* 2212 */
  2567. shapeCoordinates.triangle_br(cell, x0, x1, x2, x3, opt);
  2568. break;
  2569. case 154: /* 2122 */
  2570. shapeCoordinates.triangle_tr(cell, x0, x1, x2, x3, opt);
  2571. break;
  2572. case 106: /* 1222 */
  2573. shapeCoordinates.triangle_tl(cell, x0, x1, x2, x3, opt);
  2574. break;
  2575. case 1: /* 0001 */
  2576. shapeCoordinates.triangle_bl(cell, x0, x1, x2, x3, opt);
  2577. break;
  2578. case 4: /* 0010 */
  2579. shapeCoordinates.triangle_br(cell, x0, x1, x2, x3, opt);
  2580. break;
  2581. case 16: /* 0100 */
  2582. shapeCoordinates.triangle_tr(cell, x0, x1, x2, x3, opt);
  2583. break;
  2584. case 64: /* 1000 */
  2585. shapeCoordinates.triangle_tl(cell, x0, x1, x2, x3, opt);
  2586. break;
  2587. /* single trapezoid cases */
  2588. case 168: /* 2220 */
  2589. shapeCoordinates.tetragon_bl(cell, x0, x1, x2, x3, opt);
  2590. break;
  2591. case 162: /* 2202 */
  2592. shapeCoordinates.tetragon_br(cell, x0, x1, x2, x3, opt);
  2593. break;
  2594. case 138: /* 2022 */
  2595. shapeCoordinates.tetragon_tr(cell, x0, x1, x2, x3, opt);
  2596. break;
  2597. case 42: /* 0222 */
  2598. shapeCoordinates.tetragon_tl(cell, x0, x1, x2, x3, opt);
  2599. break;
  2600. case 2: /* 0002 */
  2601. shapeCoordinates.tetragon_bl(cell, x0, x1, x2, x3, opt);
  2602. break;
  2603. case 8: /* 0020 */
  2604. shapeCoordinates.tetragon_br(cell, x0, x1, x2, x3, opt);
  2605. break;
  2606. case 32: /* 0200 */
  2607. shapeCoordinates.tetragon_tr(cell, x0, x1, x2, x3, opt);
  2608. break;
  2609. case 128: /* 2000 */
  2610. shapeCoordinates.tetragon_tl(cell, x0, x1, x2, x3, opt);
  2611. break;
  2612. /* single rectangle cases */
  2613. case 5: /* 0011 */
  2614. shapeCoordinates.tetragon_b(cell, x0, x1, x2, x3, opt);
  2615. break;
  2616. case 20: /* 0110 */
  2617. shapeCoordinates.tetragon_r(cell, x0, x1, x2, x3, opt);
  2618. break;
  2619. case 80: /* 1100 */
  2620. shapeCoordinates.tetragon_t(cell, x0, x1, x2, x3, opt);
  2621. break;
  2622. case 65: /* 1001 */
  2623. shapeCoordinates.tetragon_l(cell, x0, x1, x2, x3, opt);
  2624. break;
  2625. case 165: /* 2211 */
  2626. shapeCoordinates.tetragon_b(cell, x0, x1, x2, x3, opt);
  2627. break;
  2628. case 150: /* 2112 */
  2629. shapeCoordinates.tetragon_r(cell, x0, x1, x2, x3, opt);
  2630. break;
  2631. case 90: /* 1122 */
  2632. shapeCoordinates.tetragon_t(cell, x0, x1, x2, x3, opt);
  2633. break;
  2634. case 105: /* 1221 */
  2635. shapeCoordinates.tetragon_l(cell, x0, x1, x2, x3, opt);
  2636. break;
  2637. case 160: /* 2200 */
  2638. shapeCoordinates.tetragon_lr(cell, x0, x1, x2, x3, opt);
  2639. break;
  2640. case 130: /* 2002 */
  2641. shapeCoordinates.tetragon_tb(cell, x0, x1, x2, x3, opt);
  2642. break;
  2643. case 10: /* 0022 */
  2644. shapeCoordinates.tetragon_lr(cell, x0, x1, x2, x3, opt);
  2645. break;
  2646. case 40: /* 0220 */
  2647. shapeCoordinates.tetragon_tb(cell, x0, x1, x2, x3, opt);
  2648. break;
  2649. /* single pentagon cases */
  2650. case 101: /* 1211 */
  2651. shapeCoordinates.pentagon_tr(cell, x0, x1, x2, x3, opt);
  2652. break;
  2653. case 149: /* 2111 */
  2654. shapeCoordinates.pentagon_tl(cell, x0, x1, x2, x3, opt);
  2655. break;
  2656. case 86: /* 1112 */
  2657. shapeCoordinates.pentagon_bl(cell, x0, x1, x2, x3, opt);
  2658. break;
  2659. case 89: /* 1121 */
  2660. shapeCoordinates.pentagon_br(cell, x0, x1, x2, x3, opt);
  2661. break;
  2662. case 69: /* 1011 */
  2663. shapeCoordinates.pentagon_tr(cell, x0, x1, x2, x3, opt);
  2664. break;
  2665. case 21: /* 0111 */
  2666. shapeCoordinates.pentagon_tl(cell, x0, x1, x2, x3, opt);
  2667. break;
  2668. case 84: /* 1110 */
  2669. shapeCoordinates.pentagon_bl(cell, x0, x1, x2, x3, opt);
  2670. break;
  2671. case 81: /* 1101 */
  2672. shapeCoordinates.pentagon_br(cell, x0, x1, x2, x3, opt);
  2673. break;
  2674. case 96: /* 1200 */
  2675. shapeCoordinates.pentagon_tr_rl(cell, x0, x1, x2, x3, opt);
  2676. break;
  2677. case 24: /* 0120 */
  2678. shapeCoordinates.pentagon_rb_bt(cell, x0, x1, x2, x3, opt);
  2679. break;
  2680. case 6: /* 0012 */
  2681. shapeCoordinates.pentagon_bl_lr(cell, x0, x1, x2, x3, opt);
  2682. break;
  2683. case 129: /* 2001 */
  2684. shapeCoordinates.pentagon_lt_tb(cell, x0, x1, x2, x3, opt);
  2685. break;
  2686. case 74: /* 1022 */
  2687. shapeCoordinates.pentagon_tr_rl(cell, x0, x1, x2, x3, opt);
  2688. break;
  2689. case 146: /* 2102 */
  2690. shapeCoordinates.pentagon_rb_bt(cell, x0, x1, x2, x3, opt);
  2691. break;
  2692. case 164: /* 2210 */
  2693. shapeCoordinates.pentagon_bl_lr(cell, x0, x1, x2, x3, opt);
  2694. break;
  2695. case 41: /* 0221 */
  2696. shapeCoordinates.pentagon_lt_tb(cell, x0, x1, x2, x3, opt);
  2697. break;
  2698. case 66: /* 1002 */
  2699. shapeCoordinates.pentagon_bl_tb(cell, x0, x1, x2, x3, opt);
  2700. break;
  2701. case 144: /* 2100 */
  2702. shapeCoordinates.pentagon_lt_rl(cell, x0, x1, x2, x3, opt);
  2703. break;
  2704. case 36: /* 0210 */
  2705. shapeCoordinates.pentagon_tr_bt(cell, x0, x1, x2, x3, opt);
  2706. break;
  2707. case 9: /* 0021 */
  2708. shapeCoordinates.pentagon_rb_lr(cell, x0, x1, x2, x3, opt);
  2709. break;
  2710. case 104: /* 1220 */
  2711. shapeCoordinates.pentagon_bl_tb(cell, x0, x1, x2, x3, opt);
  2712. break;
  2713. case 26: /* 0122 */
  2714. shapeCoordinates.pentagon_lt_rl(cell, x0, x1, x2, x3, opt);
  2715. break;
  2716. case 134: /* 2012 */
  2717. shapeCoordinates.pentagon_tr_bt(cell, x0, x1, x2, x3, opt);
  2718. break;
  2719. case 161: /* 2201 */
  2720. shapeCoordinates.pentagon_rb_lr(cell, x0, x1, x2, x3, opt);
  2721. break;
  2722. /* single hexagon cases */
  2723. case 37: /* 0211 */
  2724. shapeCoordinates.hexagon_lt_tr(cell, x0, x1, x2, x3, opt);
  2725. break;
  2726. case 148: /* 2110 */
  2727. shapeCoordinates.hexagon_bl_lt(cell, x0, x1, x2, x3, opt);
  2728. break;
  2729. case 82: /* 1102 */
  2730. shapeCoordinates.hexagon_bl_rb(cell, x0, x1, x2, x3, opt);
  2731. break;
  2732. case 73: /* 1021 */
  2733. shapeCoordinates.hexagon_tr_rb(cell, x0, x1, x2, x3, opt);
  2734. break;
  2735. case 133: /* 2011 */
  2736. shapeCoordinates.hexagon_lt_tr(cell, x0, x1, x2, x3, opt);
  2737. break;
  2738. case 22: /* 0112 */
  2739. shapeCoordinates.hexagon_bl_lt(cell, x0, x1, x2, x3, opt);
  2740. break;
  2741. case 88: /* 1120 */
  2742. shapeCoordinates.hexagon_bl_rb(cell, x0, x1, x2, x3, opt);
  2743. break;
  2744. case 97: /* 1201 */
  2745. shapeCoordinates.hexagon_tr_rb(cell, x0, x1, x2, x3, opt);
  2746. break;
  2747. case 145: /* 2101 */
  2748. shapeCoordinates.hexagon_lt_rb(cell, x0, x1, x2, x3, opt);
  2749. break;
  2750. case 25: /* 0121 */
  2751. shapeCoordinates.hexagon_lt_rb(cell, x0, x1, x2, x3, opt);
  2752. break;
  2753. case 70: /* 1012 */
  2754. shapeCoordinates.hexagon_bl_tr(cell, x0, x1, x2, x3, opt);
  2755. break;
  2756. case 100: /* 1210 */
  2757. shapeCoordinates.hexagon_bl_tr(cell, x0, x1, x2, x3, opt);
  2758. break;
  2759. /* 6-sided saddles */
  2760. case 17: /* 0101 */
  2761. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2762. /* should never be center_avg === 2 */
  2763. if (center_avg === 0) {
  2764. shapeCoordinates.triangle_bl(cell, x0, x1, x2, x3, opt);
  2765. shapeCoordinates.triangle_tr(cell, x0, x1, x2, x3, opt);
  2766. } else {
  2767. shapeCoordinates.hexagon_lt_rb(cell, x0, x1, x2, x3, opt);
  2768. }
  2769. break;
  2770. case 68: /* 1010 */
  2771. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2772. /* should never be center_avg === 2 */
  2773. if (center_avg === 0) {
  2774. shapeCoordinates.triangle_tl(cell, x0, x1, x2, x3, opt);
  2775. shapeCoordinates.triangle_br(cell, x0, x1, x2, x3, opt);
  2776. } else {
  2777. shapeCoordinates.hexagon_bl_tr(cell, x0, x1, x2, x3, opt);
  2778. }
  2779. break;
  2780. case 153: /* 2121 */
  2781. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2782. /* should never be center_avg === 0 */
  2783. if (center_avg === 2) {
  2784. shapeCoordinates.triangle_bl(cell, x0, x1, x2, x3, opt);
  2785. shapeCoordinates.triangle_tr(cell, x0, x1, x2, x3, opt);
  2786. } else {
  2787. shapeCoordinates.hexagon_lt_rb(cell, x0, x1, x2, x3, opt);
  2788. }
  2789. break;
  2790. case 102: /* 1212 */
  2791. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2792. /* should never be center_avg === 0 */
  2793. if (center_avg === 2) {
  2794. shapeCoordinates.triangle_tl(cell, x0, x1, x2, x3, opt);
  2795. shapeCoordinates.triangle_br(cell, x0, x1, x2, x3, opt);
  2796. } else {
  2797. shapeCoordinates.hexagon_bl_tr(cell, x0, x1, x2, x3, opt);
  2798. }
  2799. break;
  2800. /* 7-sided saddles */
  2801. case 152: /* 2120 */
  2802. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2803. /* should never be center_avg === 0 */
  2804. if (center_avg === 2) {
  2805. shapeCoordinates.triangle_tr(cell, x0, x1, x2, x3, opt);
  2806. shapeCoordinates.tetragon_bl(cell, x0, x1, x2, x3, opt);
  2807. } else {
  2808. shapeCoordinates.heptagon_tr(cell, x0, x1, x2, x3, opt);
  2809. }
  2810. break;
  2811. case 137: /* 2021 */
  2812. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2813. /* should never be center_avg === 0 */
  2814. if (center_avg === 2) {
  2815. shapeCoordinates.triangle_bl(cell, x0, x1, x2, x3, opt);
  2816. shapeCoordinates.tetragon_tr(cell, x0, x1, x2, x3, opt);
  2817. } else {
  2818. shapeCoordinates.heptagon_bl(cell, x0, x1, x2, x3, opt);
  2819. }
  2820. break;
  2821. case 98: /* 1202 */
  2822. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2823. /* should never be center_avg === 0 */
  2824. if (center_avg === 2) {
  2825. shapeCoordinates.triangle_tl(cell, x0, x1, x2, x3, opt);
  2826. shapeCoordinates.tetragon_br(cell, x0, x1, x2, x3, opt);
  2827. } else {
  2828. shapeCoordinates.heptagon_tl(cell, x0, x1, x2, x3, opt);
  2829. }
  2830. break;
  2831. case 38: /* 0212 */
  2832. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2833. /* should never be center_avg === 0 */
  2834. if (center_avg === 2) {
  2835. shapeCoordinates.triangle_br(cell, x0, x1, x2, x3, opt);
  2836. shapeCoordinates.tetragon_tl(cell, x0, x1, x2, x3, opt);
  2837. } else {
  2838. shapeCoordinates.heptagon_br(cell, x0, x1, x2, x3, opt);
  2839. }
  2840. break;
  2841. case 18: /* 0102 */
  2842. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2843. /* should never be center_avg === 2 */
  2844. if (center_avg === 0) {
  2845. shapeCoordinates.triangle_tr(cell, x0, x1, x2, x3, opt);
  2846. shapeCoordinates.tetragon_bl(cell, x0, x1, x2, x3, opt);
  2847. } else {
  2848. shapeCoordinates.heptagon_tr(cell, x0, x1, x2, x3, opt);
  2849. }
  2850. break;
  2851. case 33: /* 0201 */
  2852. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2853. /* should never be center_avg === 2 */
  2854. if (center_avg === 0) {
  2855. shapeCoordinates.triangle_bl(cell, x0, x1, x2, x3, opt);
  2856. shapeCoordinates.tetragon_tr(cell, x0, x1, x2, x3, opt);
  2857. } else {
  2858. shapeCoordinates.heptagon_bl(cell, x0, x1, x2, x3, opt);
  2859. }
  2860. break;
  2861. case 72: /* 1020 */
  2862. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2863. /* should never be center_avg === 2 */
  2864. if (center_avg === 0) {
  2865. shapeCoordinates.triangle_tl(cell, x0, x1, x2, x3, opt);
  2866. shapeCoordinates.tetragon_br(cell, x0, x1, x2, x3, opt);
  2867. } else {
  2868. shapeCoordinates.heptagon_tl(cell, x0, x1, x2, x3, opt);
  2869. }
  2870. break;
  2871. case 132: /* 2010 */
  2872. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2873. /* should never be center_avg === 2 */
  2874. if (center_avg === 0) {
  2875. shapeCoordinates.triangle_br(cell, x0, x1, x2, x3, opt);
  2876. shapeCoordinates.tetragon_tl(cell, x0, x1, x2, x3, opt);
  2877. } else {
  2878. shapeCoordinates.heptagon_br(cell, x0, x1, x2, x3, opt);
  2879. }
  2880. break;
  2881. /* 8-sided saddles */
  2882. case 136: /* 2020 */
  2883. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2884. if (center_avg === 0) {
  2885. shapeCoordinates.tetragon_tl(cell, x0, x1, x2, x3, opt);
  2886. shapeCoordinates.tetragon_br(cell, x0, x1, x2, x3, opt);
  2887. } else if (center_avg === 1) {
  2888. shapeCoordinates.octagon(cell, x0, x1, x2, x3, opt);
  2889. } else {
  2890. shapeCoordinates.tetragon_bl(cell, x0, x1, x2, x3, opt);
  2891. shapeCoordinates.tetragon_tr(cell, x0, x1, x2, x3, opt);
  2892. }
  2893. break;
  2894. case 34: /* 0202 */
  2895. center_avg = computeCenterAverage(x0, x1, x2, x3, minV, maxV);
  2896. if (center_avg === 0) {
  2897. shapeCoordinates.tetragon_bl(cell, x0, x1, x2, x3, opt);
  2898. shapeCoordinates.tetragon_tr(cell, x0, x1, x2, x3, opt);
  2899. } else if (center_avg === 1) {
  2900. shapeCoordinates.octagon(cell, x0, x1, x2, x3, opt);
  2901. } else {
  2902. shapeCoordinates.tetragon_tl(cell, x0, x1, x2, x3, opt);
  2903. shapeCoordinates.tetragon_br(cell, x0, x1, x2, x3, opt);
  2904. }
  2905. break;
  2906. }
  2907. return cell;
  2908. }
  2909. exports.isoLines = isoLines;
  2910. exports.isoContours = isoLines;
  2911. exports.isoBands = isoBands;
  2912. exports.QuadTree = QuadTree;
  2913. exports.quadTree = QuadTree;
  2914. Object.defineProperty(exports, '__esModule', { value: true });
  2915. })));