d3-jsnext
Version:
d3, but futuristic
272 lines (262 loc) • 9.85 kB
JavaScript
require( 'babel/register' );
var vows = require("vows"),
_ = require("../d3"),
load = require("../load"),
assert = require("../assert");
var suite = vows.describe("d3.geom.voronoi");
suite.addBatch({
"voronoi": {
topic: load("geom/voronoi").expression("d3.geom.voronoi"),
"the default voronoi layout": {
topic: function(voronoi) {
return voronoi();
},
"has no defined clip extent": function(v) {
assert.isNull(v.clipExtent());
},
"has no defined size": function(v) {
assert.isNull(v.size());
},
"returns the configured clip extent": function(v) {
try {
assert.deepEqual(v.clipExtent([[1, 2], [3, 4]]).clipExtent(), [[1, 2], [3, 4]]);
} finally {
v.clipExtent(null);
}
},
"returns the configured size": function(v) {
try {
assert.deepEqual(v.size([1, 2]).size(), [1, 2]);
} finally {
v.size(null);
}
},
"size implies a clip extent from [0, 0]": function(v) {
try {
assert.deepEqual(v.size([1, 2]).clipExtent(), [[0, 0], [1, 2]]);
} finally {
v.size(null);
}
},
"clip extent implies a size, assuming [0, 0]": function(v) {
try {
assert.deepEqual(v.clipExtent([[1, 2], [3, 4]]).size(), [3, 4]);
} finally {
v.clipExtent(null);
}
},
"has the default x-accessor, d[0]": function(v) {
assert.strictEqual(v.x()([42, 43]), 42);
},
"has the default y-accessor, d[1]": function(v) {
assert.strictEqual(v.y()([42, 43]), 43);
},
"of two points": {
topic: function(v) {
return v([[200, 200], [760, 300]]);
},
"returns two cells with the expected geometry": function(cells) {
assert.inDelta(cells, [
[[-178046.7857142857, 1e6], [179096.07142857145, -1e6], [-1e6, -1e6], [-1e6, 1e6]],
[[179096.07142857145, -1e6], [-178046.7857142857, 1e6], [1e6, 1e6], [1e6, -1e6]]
], 1e-6);
},
"the returned cells are open polygons": function(cells) {
cells.forEach(function(cell) {
assert.greater(cell.length, 2);
cell.forEach(function(point) {
assert.equal(point.length, 2);
assert.isNumber(point[0]);
assert.isNumber(point[1]);
});
assert.notDeepEqual(cell[0], cell[cell.length - 1]);
});
},
"the returned cells are not clipped to the layout size": function(cells) {
var x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity;
cells.forEach(function(cell) {
cell.forEach(function(point) {
if (point[0] < x0) x0 = point[0];
if (point[0] > x1) x1 = point[0];
if (point[1] < y0) y0 = point[1];
if (point[1] > y1) y1 = point[1];
});
});
assert.strictEqual(x0, -1e6);
assert.strictEqual(x1, 1e6);
assert.strictEqual(y0, -1e6);
assert.strictEqual(y1, 1e6);
},
"the returned cells' point property points back to the input point": function(cells) {
assert.deepEqual(cells.map(function(cell) { return cell.point; }), [[200, 200], [760, 300]]);
},
"the returned cells’ have positive area": function(cells) {
cells.forEach(function(cell) {
assert.ok(_.geom.polygon(cell).area() > 0);
});
}
},
"links": {
"for two points": function(v) {
assert.deepEqual(v.links([[200, 200], [760, 300]]), [
{source: [200, 200], target: [760, 300]}
]);
},
"for three points": function(v) {
assert.deepEqual(v.links([[200, 200], [500, 250], [760, 300]]), [
{source: [200, 200], target: [500, 250]},
{source: [500, 250], target: [760, 300]},
{source: [760, 300], target: [200, 200]}
]);
}
},
"triangles": {
"for three points": function(v) {
assert.deepEqual(v.triangles([[200, 200], [500, 250], [760, 300]]), [
[[200, 200], [760, 300], [500, 250]]
]);
}
}
},
"a voronoi layout with custom x- and y-accessors": {
topic: function(voronoi) {
return voronoi()
.x(function(d) { return d.x; })
.y(43);
},
"observes the specified x-accessor, a function": function(v) {
assert.strictEqual(v.x()({x: 42, y: 43}), 42);
},
"observes the specified y-accessor, a constant": function(v) {
assert.strictEqual(v.y(), 43);
},
"of two points": {
topic: function(v) {
return v([{x: 200}, {x: 760}]);
},
"returns two cells with the expected geometry": function(cells) {
assert.inDelta(cells, [
[[480, 1e6], [480, -1e6], [-1e6, -1e6], [-1e6, 1e6]],
[[480, -1e6], [480, 1e6], [1e6, 1e6], [1e6, -1e6]]
], 1e-6);
}
},
"links": {
topic: function(v) {
return v.y(function(d) { return d.y; });
},
"for two points": function(v) {
assert.deepEqual(v.links([{x: 200, y: 200}, {x: 760, y: 300}]), [
{source: {x: 200, y: 200}, target: {x: 760, y: 300}}
]);
},
"for three points": function(v) {
assert.deepEqual(v.links([{x: 200, y: 200}, {x: 500, y: 250}, {x: 760, y: 300}]), [
{source: {x: 200, y: 200}, target: {x: 500, y: 250}},
{source: {x: 500, y: 250}, target: {x: 760, y: 300}},
{source: {x: 760, y: 300}, target: {x: 200, y: 200}}
]);
}
}
},
"a voronoi layout with clip extent [[0, 0], [960, 500]]": {
topic: function(voronoi) {
return voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.clipExtent([[0, 0], [960, 500]]);
},
"of two points": {
topic: function(v) {
return v([{x: 200, y: 200}, {x: 760, y: 300}]);
},
"returns two cells with the expected geometry": function(cells) {
assert.inDelta(cells, [
[[435.35714285715324, 500], [524.6428571428696, 0], [0, 0], [0, 500]],
[[524.6428571428696, 0], [435.35714285715324, 500], [960, 500], [960, 0]]
], 1e-6);
},
"the returned cells are clipped to the layout size": function(cells) {
assert.isTrue(cells.every(function(cell) {
return cell.every(function(point) {
return point[0] >= 0 && point[0] <= 960
&& point[1] >= 0 && point[1] <= 500;
});
}));
}
}
},
"the default voronoi layout applied directly": {
"with zero points": {
"returns the empty array": function(voronoi) {
assert.deepEqual(polygons(voronoi([])), []);
}
},
"with one point": {
topic: function(v) {
return v([[50, 50]]);
},
"returns the semi-infinite bounding box": function(cells) {
assert.deepEqual(polygons(cells), [
[[-1e6, 1e6], [1e6, 1e6], [1e6, -1e6], [-1e6, -1e6]]
]);
},
"the returned cell has positive area": function(cells) {
assert.ok(_.geom.polygon(cells[0]).area() > 0);
}
},
"with two points": {
"separated by a horizontal line": function(voronoi) {
assert.deepEqual(polygons(voronoi([[0, -100], [0, 100]])), [
[[-1e6, 0], [1e6, 0], [1e6, -1e6], [-1e6, -1e6]],
[[1e6, 0], [-1e6, 0], [-1e6, 1e6], [1e6, 1e6]],
]);
assert.deepEqual(polygons(voronoi([[0, 100], [0, -100]])), [
[[1e6, 0], [-1e6, 0], [-1e6, 1e6], [1e6, 1e6]],
[[-1e6, 0], [1e6, 0], [1e6, -1e6], [-1e6, -1e6]]
]);
},
"separated by a vertical line": function(voronoi) {
assert.deepEqual(polygons(voronoi([[100, 0], [-100, 0]])), [
[[0, -1e6], [0, 1e6], [1e6, 1e6], [1e6, -1e6]],
[[0, 1e6], [0, -1e6], [-1e6, -1e6], [-1e6, 1e6]]
]);
assert.deepEqual(polygons(voronoi([[-100, 0], [100, 0]])), [
[[0, 1e6], [0, -1e6], [-1e6, -1e6], [-1e6, 1e6]],
[[0, -1e6], [0, 1e6], [1e6, 1e6], [1e6, -1e6]]
]);
},
"separated by a diagonal line": function(voronoi) {
assert.deepEqual(polygons(voronoi([[-100, -100], [100, 100]])), [
[[-1e6, 1e6], [1e6, -1e6], [-1e6, -1e6]],
[[1e6, -1e6], [-1e6, 1e6], [1e6, 1e6]]
]);
assert.deepEqual(polygons(voronoi([[100, 100], [-100, -100]])), [
[[1e6, -1e6], [-1e6, 1e6], [1e6, 1e6]],
[[-1e6, 1e6], [1e6, -1e6], [-1e6, -1e6]]
]);
},
"separated by an arbitrary diagonal": function(voronoi) {
assert.deepEqual(polygons(voronoi([[-100, -100], [100, 0]])), [
[[-500025, 1e6], [499975, -1e6], [-1e6, -1e6], [-1e6, 1e6]],
[ [499975, -1e6], [-500025, 1e6], [1e6, 1e6], [1e6, -1e6]]
]);
}
},
"with three points": {
"collinear": function(voronoi) {
assert.deepEqual(polygons(voronoi([[-100, -100], [0, 0], [100, 100]])), [
[[-1e6, 999900], [999900, -1e6], [-1e6, -1e6]],
[[-999900, 1e6], [1e6, -999900], [1e6, -1e6], [999900, -1e6], [-1e6, 999900], [-1e6, 1e6]],
[[1e6, -999900], [-999900, 1e6], [1e6, 1e6]]
]);
}
}
}
}
});
function polygons(cells) {
cells.forEach(function(cell) { delete cell.point; });
return cells;
}
suite.export(module);