source: node_modules/d3-contour/dist/d3-contour.js@ e4c61dd

Last change on this file since e4c61dd was e4c61dd, checked in by istevanoska <ilinastevanoska@…>, 6 months ago

Prototype 1.1

  • Property mode set to 100644
File size: 12.3 KB
Line 
1// https://d3js.org/d3-contour/ v4.0.2 Copyright 2012-2023 Mike Bostock
2(function (global, factory) {
3typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array')) :
4typeof define === 'function' && define.amd ? define(['exports', 'd3-array'], factory) :
5(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {}, global.d3));
6})(this, (function (exports, d3Array) { 'use strict';
7
8var array = Array.prototype;
9
10var slice = array.slice;
11
12function ascending(a, b) {
13 return a - b;
14}
15
16function area(ring) {
17 var i = 0, n = ring.length, area = ring[n - 1][1] * ring[0][0] - ring[n - 1][0] * ring[0][1];
18 while (++i < n) area += ring[i - 1][1] * ring[i][0] - ring[i - 1][0] * ring[i][1];
19 return area;
20}
21
22var constant = x => () => x;
23
24function contains(ring, hole) {
25 var i = -1, n = hole.length, c;
26 while (++i < n) if (c = ringContains(ring, hole[i])) return c;
27 return 0;
28}
29
30function ringContains(ring, point) {
31 var x = point[0], y = point[1], contains = -1;
32 for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
33 var pi = ring[i], xi = pi[0], yi = pi[1], pj = ring[j], xj = pj[0], yj = pj[1];
34 if (segmentContains(pi, pj, point)) return 0;
35 if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) contains = -contains;
36 }
37 return contains;
38}
39
40function segmentContains(a, b, c) {
41 var i; return collinear(a, b, c) && within(a[i = +(a[0] === b[0])], c[i], b[i]);
42}
43
44function collinear(a, b, c) {
45 return (b[0] - a[0]) * (c[1] - a[1]) === (c[0] - a[0]) * (b[1] - a[1]);
46}
47
48function within(p, q, r) {
49 return p <= q && q <= r || r <= q && q <= p;
50}
51
52function noop() {}
53
54var cases = [
55 [],
56 [[[1.0, 1.5], [0.5, 1.0]]],
57 [[[1.5, 1.0], [1.0, 1.5]]],
58 [[[1.5, 1.0], [0.5, 1.0]]],
59 [[[1.0, 0.5], [1.5, 1.0]]],
60 [[[1.0, 1.5], [0.5, 1.0]], [[1.0, 0.5], [1.5, 1.0]]],
61 [[[1.0, 0.5], [1.0, 1.5]]],
62 [[[1.0, 0.5], [0.5, 1.0]]],
63 [[[0.5, 1.0], [1.0, 0.5]]],
64 [[[1.0, 1.5], [1.0, 0.5]]],
65 [[[0.5, 1.0], [1.0, 0.5]], [[1.5, 1.0], [1.0, 1.5]]],
66 [[[1.5, 1.0], [1.0, 0.5]]],
67 [[[0.5, 1.0], [1.5, 1.0]]],
68 [[[1.0, 1.5], [1.5, 1.0]]],
69 [[[0.5, 1.0], [1.0, 1.5]]],
70 []
71];
72
73function Contours() {
74 var dx = 1,
75 dy = 1,
76 threshold = d3Array.thresholdSturges,
77 smooth = smoothLinear;
78
79 function contours(values) {
80 var tz = threshold(values);
81
82 // Convert number of thresholds into uniform thresholds.
83 if (!Array.isArray(tz)) {
84 const e = d3Array.extent(values, finite);
85 tz = d3Array.ticks(...d3Array.nice(e[0], e[1], tz), tz);
86 while (tz[tz.length - 1] >= e[1]) tz.pop();
87 while (tz[1] < e[0]) tz.shift();
88 } else {
89 tz = tz.slice().sort(ascending);
90 }
91
92 return tz.map(value => contour(values, value));
93 }
94
95 // Accumulate, smooth contour rings, assign holes to exterior rings.
96 // Based on https://github.com/mbostock/shapefile/blob/v0.6.2/shp/polygon.js
97 function contour(values, value) {
98 const v = value == null ? NaN : +value;
99 if (isNaN(v)) throw new Error(`invalid value: ${value}`);
100
101 var polygons = [],
102 holes = [];
103
104 isorings(values, v, function(ring) {
105 smooth(ring, values, v);
106 if (area(ring) > 0) polygons.push([ring]);
107 else holes.push(ring);
108 });
109
110 holes.forEach(function(hole) {
111 for (var i = 0, n = polygons.length, polygon; i < n; ++i) {
112 if (contains((polygon = polygons[i])[0], hole) !== -1) {
113 polygon.push(hole);
114 return;
115 }
116 }
117 });
118
119 return {
120 type: "MultiPolygon",
121 value: value,
122 coordinates: polygons
123 };
124 }
125
126 // Marching squares with isolines stitched into rings.
127 // Based on https://github.com/topojson/topojson-client/blob/v3.0.0/src/stitch.js
128 function isorings(values, value, callback) {
129 var fragmentByStart = new Array,
130 fragmentByEnd = new Array,
131 x, y, t0, t1, t2, t3;
132
133 // Special case for the first row (y = -1, t2 = t3 = 0).
134 x = y = -1;
135 t1 = above(values[0], value);
136 cases[t1 << 1].forEach(stitch);
137 while (++x < dx - 1) {
138 t0 = t1, t1 = above(values[x + 1], value);
139 cases[t0 | t1 << 1].forEach(stitch);
140 }
141 cases[t1 << 0].forEach(stitch);
142
143 // General case for the intermediate rows.
144 while (++y < dy - 1) {
145 x = -1;
146 t1 = above(values[y * dx + dx], value);
147 t2 = above(values[y * dx], value);
148 cases[t1 << 1 | t2 << 2].forEach(stitch);
149 while (++x < dx - 1) {
150 t0 = t1, t1 = above(values[y * dx + dx + x + 1], value);
151 t3 = t2, t2 = above(values[y * dx + x + 1], value);
152 cases[t0 | t1 << 1 | t2 << 2 | t3 << 3].forEach(stitch);
153 }
154 cases[t1 | t2 << 3].forEach(stitch);
155 }
156
157 // Special case for the last row (y = dy - 1, t0 = t1 = 0).
158 x = -1;
159 t2 = values[y * dx] >= value;
160 cases[t2 << 2].forEach(stitch);
161 while (++x < dx - 1) {
162 t3 = t2, t2 = above(values[y * dx + x + 1], value);
163 cases[t2 << 2 | t3 << 3].forEach(stitch);
164 }
165 cases[t2 << 3].forEach(stitch);
166
167 function stitch(line) {
168 var start = [line[0][0] + x, line[0][1] + y],
169 end = [line[1][0] + x, line[1][1] + y],
170 startIndex = index(start),
171 endIndex = index(end),
172 f, g;
173 if (f = fragmentByEnd[startIndex]) {
174 if (g = fragmentByStart[endIndex]) {
175 delete fragmentByEnd[f.end];
176 delete fragmentByStart[g.start];
177 if (f === g) {
178 f.ring.push(end);
179 callback(f.ring);
180 } else {
181 fragmentByStart[f.start] = fragmentByEnd[g.end] = {start: f.start, end: g.end, ring: f.ring.concat(g.ring)};
182 }
183 } else {
184 delete fragmentByEnd[f.end];
185 f.ring.push(end);
186 fragmentByEnd[f.end = endIndex] = f;
187 }
188 } else if (f = fragmentByStart[endIndex]) {
189 if (g = fragmentByEnd[startIndex]) {
190 delete fragmentByStart[f.start];
191 delete fragmentByEnd[g.end];
192 if (f === g) {
193 f.ring.push(end);
194 callback(f.ring);
195 } else {
196 fragmentByStart[g.start] = fragmentByEnd[f.end] = {start: g.start, end: f.end, ring: g.ring.concat(f.ring)};
197 }
198 } else {
199 delete fragmentByStart[f.start];
200 f.ring.unshift(start);
201 fragmentByStart[f.start = startIndex] = f;
202 }
203 } else {
204 fragmentByStart[startIndex] = fragmentByEnd[endIndex] = {start: startIndex, end: endIndex, ring: [start, end]};
205 }
206 }
207 }
208
209 function index(point) {
210 return point[0] * 2 + point[1] * (dx + 1) * 4;
211 }
212
213 function smoothLinear(ring, values, value) {
214 ring.forEach(function(point) {
215 var x = point[0],
216 y = point[1],
217 xt = x | 0,
218 yt = y | 0,
219 v1 = valid(values[yt * dx + xt]);
220 if (x > 0 && x < dx && xt === x) {
221 point[0] = smooth1(x, valid(values[yt * dx + xt - 1]), v1, value);
222 }
223 if (y > 0 && y < dy && yt === y) {
224 point[1] = smooth1(y, valid(values[(yt - 1) * dx + xt]), v1, value);
225 }
226 });
227 }
228
229 contours.contour = contour;
230
231 contours.size = function(_) {
232 if (!arguments.length) return [dx, dy];
233 var _0 = Math.floor(_[0]), _1 = Math.floor(_[1]);
234 if (!(_0 >= 0 && _1 >= 0)) throw new Error("invalid size");
235 return dx = _0, dy = _1, contours;
236 };
237
238 contours.thresholds = function(_) {
239 return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), contours) : threshold;
240 };
241
242 contours.smooth = function(_) {
243 return arguments.length ? (smooth = _ ? smoothLinear : noop, contours) : smooth === smoothLinear;
244 };
245
246 return contours;
247}
248
249// When computing the extent, ignore infinite values (as well as invalid ones).
250function finite(x) {
251 return isFinite(x) ? x : NaN;
252}
253
254// Is the (possibly invalid) x greater than or equal to the (known valid) value?
255// Treat any invalid value as below negative infinity.
256function above(x, value) {
257 return x == null ? false : +x >= value;
258}
259
260// During smoothing, treat any invalid value as negative infinity.
261function valid(v) {
262 return v == null || isNaN(v = +v) ? -Infinity : v;
263}
264
265function smooth1(x, v0, v1, value) {
266 const a = value - v0;
267 const b = v1 - v0;
268 const d = isFinite(a) || isFinite(b) ? a / b : Math.sign(a) / Math.sign(b);
269 return isNaN(d) ? x : x + d - 0.5;
270}
271
272function defaultX(d) {
273 return d[0];
274}
275
276function defaultY(d) {
277 return d[1];
278}
279
280function defaultWeight() {
281 return 1;
282}
283
284function density() {
285 var x = defaultX,
286 y = defaultY,
287 weight = defaultWeight,
288 dx = 960,
289 dy = 500,
290 r = 20, // blur radius
291 k = 2, // log2(grid cell size)
292 o = r * 3, // grid offset, to pad for blur
293 n = (dx + o * 2) >> k, // grid width
294 m = (dy + o * 2) >> k, // grid height
295 threshold = constant(20);
296
297 function grid(data) {
298 var values = new Float32Array(n * m),
299 pow2k = Math.pow(2, -k),
300 i = -1;
301
302 for (const d of data) {
303 var xi = (x(d, ++i, data) + o) * pow2k,
304 yi = (y(d, i, data) + o) * pow2k,
305 wi = +weight(d, i, data);
306 if (wi && xi >= 0 && xi < n && yi >= 0 && yi < m) {
307 var x0 = Math.floor(xi),
308 y0 = Math.floor(yi),
309 xt = xi - x0 - 0.5,
310 yt = yi - y0 - 0.5;
311 values[x0 + y0 * n] += (1 - xt) * (1 - yt) * wi;
312 values[x0 + 1 + y0 * n] += xt * (1 - yt) * wi;
313 values[x0 + 1 + (y0 + 1) * n] += xt * yt * wi;
314 values[x0 + (y0 + 1) * n] += (1 - xt) * yt * wi;
315 }
316 }
317
318 d3Array.blur2({data: values, width: n, height: m}, r * pow2k);
319 return values;
320 }
321
322 function density(data) {
323 var values = grid(data),
324 tz = threshold(values),
325 pow4k = Math.pow(2, 2 * k);
326
327 // Convert number of thresholds into uniform thresholds.
328 if (!Array.isArray(tz)) {
329 tz = d3Array.ticks(Number.MIN_VALUE, d3Array.max(values) / pow4k, tz);
330 }
331
332 return Contours()
333 .size([n, m])
334 .thresholds(tz.map(d => d * pow4k))
335 (values)
336 .map((c, i) => (c.value = +tz[i], transform(c)));
337 }
338
339 density.contours = function(data) {
340 var values = grid(data),
341 contours = Contours().size([n, m]),
342 pow4k = Math.pow(2, 2 * k),
343 contour = value => {
344 value = +value;
345 var c = transform(contours.contour(values, value * pow4k));
346 c.value = value; // preserve exact threshold value
347 return c;
348 };
349 Object.defineProperty(contour, "max", {get: () => d3Array.max(values) / pow4k});
350 return contour;
351 };
352
353 function transform(geometry) {
354 geometry.coordinates.forEach(transformPolygon);
355 return geometry;
356 }
357
358 function transformPolygon(coordinates) {
359 coordinates.forEach(transformRing);
360 }
361
362 function transformRing(coordinates) {
363 coordinates.forEach(transformPoint);
364 }
365
366 // TODO Optimize.
367 function transformPoint(coordinates) {
368 coordinates[0] = coordinates[0] * Math.pow(2, k) - o;
369 coordinates[1] = coordinates[1] * Math.pow(2, k) - o;
370 }
371
372 function resize() {
373 o = r * 3;
374 n = (dx + o * 2) >> k;
375 m = (dy + o * 2) >> k;
376 return density;
377 }
378
379 density.x = function(_) {
380 return arguments.length ? (x = typeof _ === "function" ? _ : constant(+_), density) : x;
381 };
382
383 density.y = function(_) {
384 return arguments.length ? (y = typeof _ === "function" ? _ : constant(+_), density) : y;
385 };
386
387 density.weight = function(_) {
388 return arguments.length ? (weight = typeof _ === "function" ? _ : constant(+_), density) : weight;
389 };
390
391 density.size = function(_) {
392 if (!arguments.length) return [dx, dy];
393 var _0 = +_[0], _1 = +_[1];
394 if (!(_0 >= 0 && _1 >= 0)) throw new Error("invalid size");
395 return dx = _0, dy = _1, resize();
396 };
397
398 density.cellSize = function(_) {
399 if (!arguments.length) return 1 << k;
400 if (!((_ = +_) >= 1)) throw new Error("invalid cell size");
401 return k = Math.floor(Math.log(_) / Math.LN2), resize();
402 };
403
404 density.thresholds = function(_) {
405 return arguments.length ? (threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(slice.call(_)) : constant(_), density) : threshold;
406 };
407
408 density.bandwidth = function(_) {
409 if (!arguments.length) return Math.sqrt(r * (r + 1));
410 if (!((_ = +_) >= 0)) throw new Error("invalid bandwidth");
411 return r = (Math.sqrt(4 * _ * _ + 1) - 1) / 2, resize();
412 };
413
414 return density;
415}
416
417exports.contourDensity = density;
418exports.contours = Contours;
419
420}));
Note: See TracBrowser for help on using the repository browser.