diff --git a/src/voronoi.js b/src/voronoi.js index 5764eae..200394d 100644 --- a/src/voronoi.js +++ b/src/voronoi.js @@ -152,17 +152,16 @@ export default class Voronoi { return this.delaunay._step(i, x, y) === i; } *neighbors(i) { - const epsilon = 1e-12; const ci = this._clip(i); if (ci) for (const j of this.delaunay.neighbors(i)) { const cj = this._clip(j); // find the common edge if (cj) loop: for (let ai = 0, li = ci.length; ai < li; ai += 2) { for (let aj = 0, lj = cj.length; aj < lj; aj += 2) { - if (Math.abs(ci[ai] - cj[aj]) < epsilon - && Math.abs(ci[ai + 1] - cj[aj + 1]) < epsilon - && Math.abs(ci[(ai + 2) % li] - cj[(aj + lj - 2) % lj]) < epsilon - && Math.abs(ci[(ai + 3) % li] - cj[(aj + lj - 1) % lj]) < epsilon) { + if (ci[ai] === cj[aj] + && ci[ai + 1] === cj[aj + 1] + && ci[(ai + 2) % li] === cj[(aj + lj - 2) % lj] + && ci[(ai + 3) % li] === cj[(aj + lj - 1) % lj]) { yield j; break loop; } @@ -239,8 +238,11 @@ export default class Voronoi { return P; } _clipSegment(x0, y0, x1, y1, c0, c1) { + // for more robustness, always consider the segment in the same order + const flip = c0 < c1; + if (flip) [x0, y0, x1, y1, c0, c1] = [x1, y1, x0, y0, c1, c0]; while (true) { - if (c0 === 0 && c1 === 0) return [x0, y0, x1, y1]; + if (c0 === 0 && c1 === 0) return flip ? [x1, y1, x0, y0] : [x0, y0, x1, y1]; if (c0 & c1) return null; let x, y, c = c0 || c1; if (c & 0b1000) x = x0 + (x1 - x0) * (this.ymax - y0) / (y1 - y0), y = this.ymax; diff --git a/test/voronoi-test.js b/test/voronoi-test.js index 9cc1cb1..c543a98 100644 --- a/test/voronoi-test.js +++ b/test/voronoi-test.js @@ -88,3 +88,21 @@ it("voronoi.neighbors returns the correct neighbors", () => { const voronoi = Delaunay.from(points).voronoi([0, 0, 100, 90]); assert.deepStrictEqual([0, 1, 2, 3].map(i => [...voronoi.neighbors(i)].sort()), [[1], [0, 2, 3], [1, 3], [1, 2]]); }); + +it("voronoi.neighbors returns the correct neighbors, flipped vertically", () => { + const points = [[10, -10], [36, -27], [90, -19], [50, -75]]; + const voronoi = Delaunay.from(points).voronoi([0, -90, 100, 0]); + assert.deepStrictEqual([0, 1, 2, 3].map(i => [...voronoi.neighbors(i)].sort()), [[1], [0, 2, 3], [1, 3], [1, 2]]); +}); + +it("voronoi.neighbors returns the correct neighbors, flipped horizontally", () => { + const points = [[-10, 10], [-36, 27], [-90, 19], [-50, 75]]; + const voronoi = Delaunay.from(points).voronoi([-100, 0, 0, 90]); + assert.deepStrictEqual([0, 1, 2, 3].map(i => [...voronoi.neighbors(i)].sort()), [[1], [0, 2, 3], [1, 3], [1, 2]]); +}); + +it("voronoi.neighbors returns the correct neighbors, rotated", () => { + const points = [[-10, -10], [-36, -27], [-90, -19], [-50, -75]]; + const voronoi = Delaunay.from(points).voronoi([-100, -90, 0, 0]); + assert.deepStrictEqual([0, 1, 2, 3].map(i => [...voronoi.neighbors(i)].sort()), [[1], [0, 2, 3], [1, 3], [1, 2]]); +});