Skip to content

Commit 6c9103a

Browse files
authored
Add first-class TypeScript types (#90)
* add first-class TypeScript types * fixup: unused typedef
1 parent e094737 commit 6c9103a

File tree

6 files changed

+499
-372
lines changed

6 files changed

+499
-372
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ tmp
44
coverage
55
delaunator.js
66
delaunator.min.js
7+
index.d.ts

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
ISC License
22

3-
Copyright (c) 2024, Mapbox
3+
Copyright (c) 2025, Mapbox
44

55
Permission to use, copy, modify, and/or distribute this software for any purpose
66
with or without fee is hereby granted, provided that the above copyright notice

index.js

Lines changed: 142 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,19 @@ const EDGE_STACK = new Uint32Array(512);
44

55
import {orient2d} from 'robust-predicates';
66

7+
/** @template {ArrayLike<number>} T */
78
export default class Delaunator {
89

10+
/**
11+
* Constructs a delaunay triangulation object given an array of points (`[x, y]` by default).
12+
* `getX` and `getY` are optional functions of the form `(point) => value` for custom point formats.
13+
*
14+
* @template P
15+
* @param {P[]} points
16+
* @param {(p: P) => number} [getX]
17+
* @param {(p: P) => number} [getY]
18+
*/
19+
// @ts-expect-error TS2322
920
static from(points, getX = defaultGetX, getY = defaultGetY) {
1021
const n = points.length;
1122
const coords = new Float64Array(n * 2);
@@ -19,6 +30,12 @@ export default class Delaunator {
1930
return new Delaunator(coords);
2031
}
2132

33+
/**
34+
* Constructs a delaunay triangulation object given an array of point coordinates of the form:
35+
* `[x0, y0, x1, y1, ...]` (use a typed array for best performance). Duplicate points are skipped.
36+
*
37+
* @param {T} coords
38+
*/
2239
constructor(coords) {
2340
const n = coords.length >> 1;
2441
if (n > 0 && typeof coords[0] !== 'number') throw new Error('Expected coords to contain numbers.');
@@ -27,23 +44,44 @@ export default class Delaunator {
2744

2845
// arrays that will store the triangulation graph
2946
const maxTriangles = Math.max(2 * n - 5, 0);
30-
this._triangles = new Uint32Array(maxTriangles * 3);
31-
this._halfedges = new Int32Array(maxTriangles * 3);
47+
/** @private */ this._triangles = new Uint32Array(maxTriangles * 3);
48+
/** @private */ this._halfedges = new Int32Array(maxTriangles * 3);
3249

3350
// temporary arrays for tracking the edges of the advancing convex hull
34-
this._hashSize = Math.ceil(Math.sqrt(n));
35-
this._hullPrev = new Uint32Array(n); // edge to prev edge
36-
this._hullNext = new Uint32Array(n); // edge to next edge
37-
this._hullTri = new Uint32Array(n); // edge to adjacent triangle
38-
this._hullHash = new Int32Array(this._hashSize); // angular edge hash
51+
/** @private */ this._hashSize = Math.ceil(Math.sqrt(n));
52+
/** @private */ this._hullPrev = new Uint32Array(n); // edge to prev edge
53+
/** @private */ this._hullNext = new Uint32Array(n); // edge to next edge
54+
/** @private */ this._hullTri = new Uint32Array(n); // edge to adjacent triangle
55+
/** @private */ this._hullHash = new Int32Array(this._hashSize); // angular edge hash
3956

4057
// temporary arrays for sorting points
41-
this._ids = new Uint32Array(n);
42-
this._dists = new Float64Array(n);
58+
/** @private */ this._ids = new Uint32Array(n);
59+
/** @private */ this._dists = new Float64Array(n);
60+
61+
/** @private */ this.trianglesLen = 0;
62+
/** @private */ this._cx = 0;
63+
/** @private */ this._cy = 0;
64+
/** @private */ this._hullStart = 0;
65+
66+
67+
/** A `Uint32Array` array of indices that reference points on the convex hull of the input data, counter-clockwise. */
68+
this.hull = this._triangles;
69+
/** A `Uint32Array` array of triangle vertex indices (each group of three numbers forms a triangle). All triangles are directed counterclockwise. */
70+
this.triangles = this._triangles;
71+
/**
72+
* A `Int32Array` array of triangle half-edge indices that allows you to traverse the triangulation.
73+
* `i`-th half-edge in the array corresponds to vertex `triangles[i]` the half-edge is coming from.
74+
* `halfedges[i]` is the index of a twin half-edge in an adjacent triangle (or `-1` for outer half-edges on the convex hull).
75+
*/
76+
this.halfedges = this._halfedges;
4377

4478
this.update();
4579
}
4680

81+
/**
82+
* Updates the triangulation if you modified `delaunay.coords` values in place, avoiding expensive memory allocations.
83+
* Useful for iterative relaxation algorithms such as Lloyd's.
84+
*/
4785
update() {
4886
const {coords, _hullPrev: hullPrev, _hullNext: hullNext, _hullTri: hullTri, _hullHash: hullHash} = this;
4987
const n = coords.length >> 1;
@@ -66,7 +104,7 @@ export default class Delaunator {
66104
const cx = (minX + maxX) / 2;
67105
const cy = (minY + maxY) / 2;
68106

69-
let i0, i1, i2;
107+
let i0 = 0, i1 = 0, i2 = 0;
70108

71109
// pick a seed point close to the center
72110
for (let i = 0, minDist = Infinity; i < n; i++) {
@@ -124,7 +162,7 @@ export default class Delaunator {
124162
}
125163
this.hull = hull.subarray(0, j);
126164
this.triangles = new Uint32Array(0);
127-
this.halfedges = new Uint32Array(0);
165+
this.halfedges = new Int32Array(0);
128166
return;
129167
}
130168

@@ -172,7 +210,7 @@ export default class Delaunator {
172210
this.trianglesLen = 0;
173211
this._addTriangle(i0, i1, i2, -1, -1, -1);
174212

175-
for (let k = 0, xp, yp; k < this._ids.length; k++) {
213+
for (let k = 0, xp = 0, yp = 0; k < this._ids.length; k++) {
176214
const i = this._ids[k];
177215
const x = coords[2 * i];
178216
const y = coords[2 * i + 1];
@@ -254,10 +292,23 @@ export default class Delaunator {
254292
this.halfedges = this._halfedges.subarray(0, this.trianglesLen);
255293
}
256294

295+
/**
296+
* Calculate an angle-based key for the edge hash used for advancing convex hull.
297+
*
298+
* @param {number} x
299+
* @param {number} y
300+
* @private
301+
*/
257302
_hashKey(x, y) {
258303
return Math.floor(pseudoAngle(x - this._cx, y - this._cy) * this._hashSize) % this._hashSize;
259304
}
260305

306+
/**
307+
* Flip an edge in a pair of triangles if it doesn't satisfy the Delaunay condition.
308+
*
309+
* @param {number} a
310+
* @private
311+
*/
261312
_legalize(a) {
262313
const {_triangles: triangles, _halfedges: halfedges, coords} = this;
263314

@@ -313,7 +364,7 @@ export default class Delaunator {
313364

314365
const hbl = halfedges[bl];
315366

316-
// edge swapped on the other side of the hull (rare); fix the halfedge reference
367+
// edge swapped on the other side of the hull (rare); fix the half-edge reference
317368
if (hbl === -1) {
318369
let e = this._hullStart;
319370
do {
@@ -343,12 +394,28 @@ export default class Delaunator {
343394
return ar;
344395
}
345396

397+
/**
398+
* Link two half-edges to each other.
399+
* @param {number} a
400+
* @param {number} b
401+
* @private
402+
*/
346403
_link(a, b) {
347404
this._halfedges[a] = b;
348405
if (b !== -1) this._halfedges[b] = a;
349406
}
350407

351-
// add a new triangle given vertex indices and adjacent half-edge ids
408+
/**
409+
* Add a new triangle given vertex indices and adjacent half-edge ids.
410+
*
411+
* @param {number} i0
412+
* @param {number} i1
413+
* @param {number} i2
414+
* @param {number} a
415+
* @param {number} b
416+
* @param {number} c
417+
* @private
418+
*/
352419
_addTriangle(i0, i1, i2, a, b, c) {
353420
const t = this.trianglesLen;
354421

@@ -366,18 +433,43 @@ export default class Delaunator {
366433
}
367434
}
368435

369-
// monotonically increases with real angle, but doesn't need expensive trigonometry
436+
/**
437+
* Monotonically increases with real angle, but doesn't need expensive trigonometry.
438+
*
439+
* @param {number} dx
440+
* @param {number} dy
441+
*/
370442
function pseudoAngle(dx, dy) {
371443
const p = dx / (Math.abs(dx) + Math.abs(dy));
372444
return (dy > 0 ? 3 - p : 1 + p) / 4; // [0..1]
373445
}
374446

447+
/**
448+
* Squared distance between two points.
449+
*
450+
* @param {number} ax
451+
* @param {number} ay
452+
* @param {number} bx
453+
* @param {number} by
454+
*/
375455
function dist(ax, ay, bx, by) {
376456
const dx = ax - bx;
377457
const dy = ay - by;
378458
return dx * dx + dy * dy;
379459
}
380460

461+
/**
462+
* Checker whether point P is inside a circle formed by points A, B, C.
463+
*
464+
* @param {number} ax
465+
* @param {number} ay
466+
* @param {number} bx
467+
* @param {number} by
468+
* @param {number} cx
469+
* @param {number} cy
470+
* @param {number} px
471+
* @param {number} py
472+
*/
381473
function inCircle(ax, ay, bx, by, cx, cy, px, py) {
382474
const dx = ax - px;
383475
const dy = ay - py;
@@ -395,6 +487,16 @@ function inCircle(ax, ay, bx, by, cx, cy, px, py) {
395487
ap * (ex * fy - ey * fx) < 0;
396488
}
397489

490+
/**
491+
* Checker whether point P is inside a circle formed by points A, B, C.
492+
*
493+
* @param {number} ax
494+
* @param {number} ay
495+
* @param {number} bx
496+
* @param {number} by
497+
* @param {number} cx
498+
* @param {number} cy
499+
*/
398500
function circumradius(ax, ay, bx, by, cx, cy) {
399501
const dx = bx - ax;
400502
const dy = by - ay;
@@ -411,6 +513,16 @@ function circumradius(ax, ay, bx, by, cx, cy) {
411513
return x * x + y * y;
412514
}
413515

516+
/**
517+
* Get coordinates of a circumcenter for points A, B, C.
518+
*
519+
* @param {number} ax
520+
* @param {number} ay
521+
* @param {number} bx
522+
* @param {number} by
523+
* @param {number} cx
524+
* @param {number} cy
525+
*/
414526
function circumcenter(ax, ay, bx, by, cx, cy) {
415527
const dx = bx - ax;
416528
const dy = by - ay;
@@ -427,6 +539,14 @@ function circumcenter(ax, ay, bx, by, cx, cy) {
427539
return {x, y};
428540
}
429541

542+
/**
543+
* Sort points by distance via an array of point indices and an array of calculated distances.
544+
*
545+
* @param {Uint32Array} ids
546+
* @param {Float64Array} dists
547+
* @param {number} left
548+
* @param {number} right
549+
*/
430550
function quicksort(ids, dists, left, right) {
431551
if (right - left <= 20) {
432552
for (let i = left + 1; i <= right; i++) {
@@ -466,15 +586,22 @@ function quicksort(ids, dists, left, right) {
466586
}
467587
}
468588

589+
/**
590+
* @param {Uint32Array} arr
591+
* @param {number} i
592+
* @param {number} j
593+
*/
469594
function swap(arr, i, j) {
470595
const tmp = arr[i];
471596
arr[i] = arr[j];
472597
arr[j] = tmp;
473598
}
474599

600+
/** @param {[number, number]} p */
475601
function defaultGetX(p) {
476602
return p[0];
477603
}
604+
/** @param {[number, number]} p */
478605
function defaultGetY(p) {
479606
return p[1];
480607
}

0 commit comments

Comments
 (0)