From dc894a162e72eb749fe45909fd4145c5da425fc6 Mon Sep 17 00:00:00 2001 From: Aria Kassaei Date: Wed, 29 May 2024 23:36:05 +0200 Subject: [PATCH] Make `Delaunay.find` return `-1` instead of `NaN` for edge cases (#145) * test: add one unit test that covers the empty points array case * fix: fix NaN cases for delaunay find * fixes for odd-length points --------- Co-authored-by: Mike Bostock --- src/delaunay.js | 7 ++++--- test/delaunay-test.js | 30 +++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/delaunay.js b/src/delaunay.js index fcb9d24..adc006d 100644 --- a/src/delaunay.js +++ b/src/delaunay.js @@ -54,7 +54,7 @@ export default class Delaunay { // check for collinear if (d.hull && d.hull.length > 2 && collinear(d)) { - this.collinear = Int32Array.from({length: points.length/2}, (_,i) => i) + this.collinear = Int32Array.from({length: points.length / 2}, (_, i) => i) .sort((i, j) => points[2 * i] - points[2 * j] || points[2 * i + 1] - points[2 * j + 1]); // for exact neighbors const e = this.collinear[0], f = this.collinear[this.collinear.length - 1], bounds = [ points[2 * e], points[2 * e + 1], points[2 * f], points[2 * f + 1] ], @@ -137,7 +137,8 @@ export default class Delaunay { } _step(i, x, y) { const {inedges, hull, _hullIndex, halfedges, triangles, points} = this; - if (inedges[i] === -1 || !points.length) return (i + 1) % (points.length >> 1); + if (points.length < 2) return -1; + if (inedges[i] === -1) return (i + 1) % (points.length >> 1); let c = i; let dc = pow(x - points[i * 2], 2) + pow(y - points[i * 2 + 1], 2); const e0 = inedges[i]; @@ -178,7 +179,7 @@ export default class Delaunay { r = r == undefined ? 2 : +r; const buffer = context == null ? context = new Path : undefined; const {points} = this; - for (let i = 0, n = points.length; i < n; i += 2) { + for (let i = 0, n = points.length & ~1; i < n; i += 2) { // round down if points has odd length const x = points[i], y = points[i + 1]; context.moveTo(x + r, y); context.arc(x, y, r, 0, tau); diff --git a/test/delaunay-test.js b/test/delaunay-test.js index ece0d66..f0804ff 100644 --- a/test/delaunay-test.js +++ b/test/delaunay-test.js @@ -127,17 +127,37 @@ it("delaunay.find(x, y) returns the index of the cell that contains the specifie assert.strictEqual(delaunay.find(51, 51), 4); }); -it("delaunay.find(x, y) works with one or two points", () => { - const points = [[0, 1], [0, 2]]; - const delaunay = Delaunay.from(points); - assert.strictEqual(points[delaunay.find(0, -1)][1], 1); - assert.strictEqual(points[delaunay.find(0, 2.2)][1], 2); +it("delaunay.find(x, y) works with one point", () => { + const delaunay = new Delaunay([0, 1]); + assert.strictEqual(delaunay.find(0, -1), 0); + assert.strictEqual(delaunay.find(0, 2.2), 0); delaunay.points.fill(0); delaunay.update(); assert.strictEqual(delaunay.find(0, -1), 0); assert.strictEqual(delaunay.find(0, 1.2), 0); }); +it("delaunay.find(x, y) works with two points", () => { + const delaunay = new Delaunay([0, 1, 0, 2]); + assert.strictEqual(delaunay.find(0, -1), 0); + assert.strictEqual(delaunay.find(0, 2.2), 1); + delaunay.points.fill(0); + delaunay.update(); + assert.strictEqual(delaunay.find(0, -1), 0); + assert.strictEqual(delaunay.find(0, 1.2), 0); +}); + +it("delaunay.find(x, y) returns -1 for empty points array", () => { + const delaunay = new Delaunay([]); + assert.strictEqual(delaunay.find(0, -1), -1); +}); + +it("delaunay.find(x, y) returns -1 for half a point", () => { + const delaunay = new Delaunay([0]); // invalid; considered empty + assert.strictEqual(delaunay.find(0, -1), -1); + assert.strictEqual(delaunay.find(0, 2.2), -1); +}); + it("delaunay.find(x, y) works with collinear points", () => { const points = [[0, 1], [0, 2], [0, 4], [0, 0], [0, 3], [0, 4], [0, 4]]; const delaunay = Delaunay.from(points);