source: node_modules/d3-delaunay/src/delaunay.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: 7.9 KB
Line 
1import Delaunator from "delaunator";
2import Path from "./path.js";
3import Polygon from "./polygon.js";
4import Voronoi from "./voronoi.js";
5
6const tau = 2 * Math.PI, pow = Math.pow;
7
8function pointX(p) {
9 return p[0];
10}
11
12function pointY(p) {
13 return p[1];
14}
15
16// A triangulation is collinear if all its triangles have a non-null area
17function collinear(d) {
18 const {triangles, coords} = d;
19 for (let i = 0; i < triangles.length; i += 3) {
20 const a = 2 * triangles[i],
21 b = 2 * triangles[i + 1],
22 c = 2 * triangles[i + 2],
23 cross = (coords[c] - coords[a]) * (coords[b + 1] - coords[a + 1])
24 - (coords[b] - coords[a]) * (coords[c + 1] - coords[a + 1]);
25 if (cross > 1e-10) return false;
26 }
27 return true;
28}
29
30function jitter(x, y, r) {
31 return [x + Math.sin(x + y) * r, y + Math.cos(x - y) * r];
32}
33
34export default class Delaunay {
35 static from(points, fx = pointX, fy = pointY, that) {
36 return new Delaunay("length" in points
37 ? flatArray(points, fx, fy, that)
38 : Float64Array.from(flatIterable(points, fx, fy, that)));
39 }
40 constructor(points) {
41 this._delaunator = new Delaunator(points);
42 this.inedges = new Int32Array(points.length / 2);
43 this._hullIndex = new Int32Array(points.length / 2);
44 this.points = this._delaunator.coords;
45 this._init();
46 }
47 update() {
48 this._delaunator.update();
49 this._init();
50 return this;
51 }
52 _init() {
53 const d = this._delaunator, points = this.points;
54
55 // check for collinear
56 if (d.hull && d.hull.length > 2 && collinear(d)) {
57 this.collinear = Int32Array.from({length: points.length/2}, (_,i) => i)
58 .sort((i, j) => points[2 * i] - points[2 * j] || points[2 * i + 1] - points[2 * j + 1]); // for exact neighbors
59 const e = this.collinear[0], f = this.collinear[this.collinear.length - 1],
60 bounds = [ points[2 * e], points[2 * e + 1], points[2 * f], points[2 * f + 1] ],
61 r = 1e-8 * Math.hypot(bounds[3] - bounds[1], bounds[2] - bounds[0]);
62 for (let i = 0, n = points.length / 2; i < n; ++i) {
63 const p = jitter(points[2 * i], points[2 * i + 1], r);
64 points[2 * i] = p[0];
65 points[2 * i + 1] = p[1];
66 }
67 this._delaunator = new Delaunator(points);
68 } else {
69 delete this.collinear;
70 }
71
72 const halfedges = this.halfedges = this._delaunator.halfedges;
73 const hull = this.hull = this._delaunator.hull;
74 const triangles = this.triangles = this._delaunator.triangles;
75 const inedges = this.inedges.fill(-1);
76 const hullIndex = this._hullIndex.fill(-1);
77
78 // Compute an index from each point to an (arbitrary) incoming halfedge
79 // Used to give the first neighbor of each point; for this reason,
80 // on the hull we give priority to exterior halfedges
81 for (let e = 0, n = halfedges.length; e < n; ++e) {
82 const p = triangles[e % 3 === 2 ? e - 2 : e + 1];
83 if (halfedges[e] === -1 || inedges[p] === -1) inedges[p] = e;
84 }
85 for (let i = 0, n = hull.length; i < n; ++i) {
86 hullIndex[hull[i]] = i;
87 }
88
89 // degenerate case: 1 or 2 (distinct) points
90 if (hull.length <= 2 && hull.length > 0) {
91 this.triangles = new Int32Array(3).fill(-1);
92 this.halfedges = new Int32Array(3).fill(-1);
93 this.triangles[0] = hull[0];
94 inedges[hull[0]] = 1;
95 if (hull.length === 2) {
96 inedges[hull[1]] = 0;
97 this.triangles[1] = hull[1];
98 this.triangles[2] = hull[1];
99 }
100 }
101 }
102 voronoi(bounds) {
103 return new Voronoi(this, bounds);
104 }
105 *neighbors(i) {
106 const {inedges, hull, _hullIndex, halfedges, triangles, collinear} = this;
107
108 // degenerate case with several collinear points
109 if (collinear) {
110 const l = collinear.indexOf(i);
111 if (l > 0) yield collinear[l - 1];
112 if (l < collinear.length - 1) yield collinear[l + 1];
113 return;
114 }
115
116 const e0 = inedges[i];
117 if (e0 === -1) return; // coincident point
118 let e = e0, p0 = -1;
119 do {
120 yield p0 = triangles[e];
121 e = e % 3 === 2 ? e - 2 : e + 1;
122 if (triangles[e] !== i) return; // bad triangulation
123 e = halfedges[e];
124 if (e === -1) {
125 const p = hull[(_hullIndex[i] + 1) % hull.length];
126 if (p !== p0) yield p;
127 return;
128 }
129 } while (e !== e0);
130 }
131 find(x, y, i = 0) {
132 if ((x = +x, x !== x) || (y = +y, y !== y)) return -1;
133 const i0 = i;
134 let c;
135 while ((c = this._step(i, x, y)) >= 0 && c !== i && c !== i0) i = c;
136 return c;
137 }
138 _step(i, x, y) {
139 const {inedges, hull, _hullIndex, halfedges, triangles, points} = this;
140 if (inedges[i] === -1 || !points.length) return (i + 1) % (points.length >> 1);
141 let c = i;
142 let dc = pow(x - points[i * 2], 2) + pow(y - points[i * 2 + 1], 2);
143 const e0 = inedges[i];
144 let e = e0;
145 do {
146 let t = triangles[e];
147 const dt = pow(x - points[t * 2], 2) + pow(y - points[t * 2 + 1], 2);
148 if (dt < dc) dc = dt, c = t;
149 e = e % 3 === 2 ? e - 2 : e + 1;
150 if (triangles[e] !== i) break; // bad triangulation
151 e = halfedges[e];
152 if (e === -1) {
153 e = hull[(_hullIndex[i] + 1) % hull.length];
154 if (e !== t) {
155 if (pow(x - points[e * 2], 2) + pow(y - points[e * 2 + 1], 2) < dc) return e;
156 }
157 break;
158 }
159 } while (e !== e0);
160 return c;
161 }
162 render(context) {
163 const buffer = context == null ? context = new Path : undefined;
164 const {points, halfedges, triangles} = this;
165 for (let i = 0, n = halfedges.length; i < n; ++i) {
166 const j = halfedges[i];
167 if (j < i) continue;
168 const ti = triangles[i] * 2;
169 const tj = triangles[j] * 2;
170 context.moveTo(points[ti], points[ti + 1]);
171 context.lineTo(points[tj], points[tj + 1]);
172 }
173 this.renderHull(context);
174 return buffer && buffer.value();
175 }
176 renderPoints(context, r) {
177 if (r === undefined && (!context || typeof context.moveTo !== "function")) r = context, context = null;
178 r = r == undefined ? 2 : +r;
179 const buffer = context == null ? context = new Path : undefined;
180 const {points} = this;
181 for (let i = 0, n = points.length; i < n; i += 2) {
182 const x = points[i], y = points[i + 1];
183 context.moveTo(x + r, y);
184 context.arc(x, y, r, 0, tau);
185 }
186 return buffer && buffer.value();
187 }
188 renderHull(context) {
189 const buffer = context == null ? context = new Path : undefined;
190 const {hull, points} = this;
191 const h = hull[0] * 2, n = hull.length;
192 context.moveTo(points[h], points[h + 1]);
193 for (let i = 1; i < n; ++i) {
194 const h = 2 * hull[i];
195 context.lineTo(points[h], points[h + 1]);
196 }
197 context.closePath();
198 return buffer && buffer.value();
199 }
200 hullPolygon() {
201 const polygon = new Polygon;
202 this.renderHull(polygon);
203 return polygon.value();
204 }
205 renderTriangle(i, context) {
206 const buffer = context == null ? context = new Path : undefined;
207 const {points, triangles} = this;
208 const t0 = triangles[i *= 3] * 2;
209 const t1 = triangles[i + 1] * 2;
210 const t2 = triangles[i + 2] * 2;
211 context.moveTo(points[t0], points[t0 + 1]);
212 context.lineTo(points[t1], points[t1 + 1]);
213 context.lineTo(points[t2], points[t2 + 1]);
214 context.closePath();
215 return buffer && buffer.value();
216 }
217 *trianglePolygons() {
218 const {triangles} = this;
219 for (let i = 0, n = triangles.length / 3; i < n; ++i) {
220 yield this.trianglePolygon(i);
221 }
222 }
223 trianglePolygon(i) {
224 const polygon = new Polygon;
225 this.renderTriangle(i, polygon);
226 return polygon.value();
227 }
228}
229
230function flatArray(points, fx, fy, that) {
231 const n = points.length;
232 const array = new Float64Array(n * 2);
233 for (let i = 0; i < n; ++i) {
234 const p = points[i];
235 array[i * 2] = fx.call(that, p, i, points);
236 array[i * 2 + 1] = fy.call(that, p, i, points);
237 }
238 return array;
239}
240
241function* flatIterable(points, fx, fy, that) {
242 let i = 0;
243 for (const p of points) {
244 yield fx.call(that, p, i, points);
245 yield fy.call(that, p, i, points);
246 ++i;
247 }
248}
Note: See TracBrowser for help on using the repository browser.