See #1145.
This commit is contained in:
Jason Davies 2013-03-19 10:33:56 +00:00
Родитель 6d62cc2060
Коммит 788f8cd958
7 изменённых файлов: 718 добавлений и 732 удалений

781
d3.js поставляемый
Просмотреть файл

@ -3793,68 +3793,399 @@ d3 = function() {
var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21);
return [ x1 + ua * x21, y1 + ua * y21 ];
}
d3.geom.voronoi = function(vertices) {
var polygons = vertices.map(function() {
return [];
}), Z = 1e6;
d3_geom_voronoiTessellate(vertices, function(e) {
var s1, s2, x1, x2, y1, y2;
if (e.a === 1 && e.b >= 0) {
s1 = e.ep.r;
s2 = e.ep.l;
} else {
s1 = e.ep.l;
s2 = e.ep.r;
d3.svg = {};
function d3_svg_line(projection) {
var x = d3_svg_lineX, y = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
function line(data) {
var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
function segment() {
segments.push("M", interpolate(projection(points), tension));
}
if (e.a === 1) {
y1 = s1 ? s1.y : -Z;
x1 = e.c - e.b * y1;
y2 = s2 ? s2.y : Z;
x2 = e.c - e.b * y2;
} else {
x1 = s1 ? s1.x : -Z;
y1 = e.c - e.a * x1;
x2 = s2 ? s2.x : Z;
y2 = e.c - e.a * x2;
}
var v1 = [ x1, y1 ], v2 = [ x2, y2 ];
polygons[e.region.l.index].push(v1, v2);
polygons[e.region.r.index].push(v1, v2);
});
polygons = polygons.map(function(polygon, i) {
var cx = vertices[i][0], cy = vertices[i][1], angle = polygon.map(function(v) {
return Math.atan2(v[0] - cx, v[1] - cy);
}), order = d3.range(polygon.length).sort(function(a, b) {
return angle[a] - angle[b];
});
return order.filter(function(d, i) {
return !i || angle[d] - angle[order[i - 1]] > ε;
}).map(function(d) {
return polygon[d];
});
});
polygons.forEach(function(polygon, i) {
var n = polygon.length;
if (!n) return polygon.push([ -Z, -Z ], [ -Z, Z ], [ Z, Z ], [ Z, -Z ]);
if (n > 2) return;
var p0 = vertices[i], p1 = polygon[0], p2 = polygon[1], x0 = p0[0], y0 = p0[1], x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], dx = Math.abs(x2 - x1), dy = y2 - y1;
if (Math.abs(dy) < ε) {
var y = y0 < y1 ? -Z : Z;
polygon.push([ -Z, y ], [ Z, y ]);
} else if (dx < ε) {
var x = x0 < x1 ? -Z : Z;
polygon.push([ x, -Z ], [ x, Z ]);
} else {
var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z, z = Math.abs(dy) - dx;
if (Math.abs(z) < ε) {
polygon.push([ dy < 0 ? y : -y, y ]);
} else {
if (z > 0) y *= -1;
polygon.push([ -Z, y ], [ Z, y ]);
while (++i < n) {
if (defined.call(this, d = data[i], i)) {
points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
} else if (points.length) {
segment();
points = [];
}
}
if (points.length) segment();
return segments.length ? segments.join("") : null;
}
line.x = function(_) {
if (!arguments.length) return x;
x = _;
return line;
};
line.y = function(_) {
if (!arguments.length) return y;
y = _;
return line;
};
line.defined = function(_) {
if (!arguments.length) return defined;
defined = _;
return line;
};
line.interpolate = function(_) {
if (!arguments.length) return interpolateKey;
if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
return line;
};
line.tension = function(_) {
if (!arguments.length) return tension;
tension = _;
return line;
};
return line;
}
d3.svg.line = function() {
return d3_svg_line(d3_identity);
};
function d3_svg_lineX(d) {
return d[0];
}
function d3_svg_lineY(d) {
return d[1];
}
var d3_svg_lineInterpolators = d3.map({
linear: d3_svg_lineLinear,
"linear-closed": d3_svg_lineLinearClosed,
"step-before": d3_svg_lineStepBefore,
"step-after": d3_svg_lineStepAfter,
basis: d3_svg_lineBasis,
"basis-open": d3_svg_lineBasisOpen,
"basis-closed": d3_svg_lineBasisClosed,
bundle: d3_svg_lineBundle,
cardinal: d3_svg_lineCardinal,
"cardinal-open": d3_svg_lineCardinalOpen,
"cardinal-closed": d3_svg_lineCardinalClosed,
monotone: d3_svg_lineMonotone
});
d3_svg_lineInterpolators.forEach(function(key, value) {
value.key = key;
value.closed = /-closed$/.test(key);
});
function d3_svg_lineLinear(points) {
return points.join("L");
}
function d3_svg_lineLinearClosed(points) {
return d3_svg_lineLinear(points) + "Z";
}
function d3_svg_lineStepBefore(points) {
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
return path.join("");
}
function d3_svg_lineStepAfter(points) {
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
return path.join("");
}
function d3_svg_lineCardinalOpen(points, tension) {
return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension));
}
function d3_svg_lineCardinalClosed(points, tension) {
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
}
function d3_svg_lineCardinal(points, tension) {
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
}
function d3_svg_lineHermite(points, tangents) {
if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
return d3_svg_lineLinear(points);
}
var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
if (quad) {
path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
p0 = points[1];
pi = 2;
}
if (tangents.length > 1) {
t = tangents[1];
p = points[pi];
pi++;
path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
for (var i = 2; i < tangents.length; i++, pi++) {
p = points[pi];
t = tangents[i];
path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
}
}
if (quad) {
var lp = points[pi];
path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
}
return path;
}
function d3_svg_lineCardinalTangents(points, tension) {
var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
while (++i < n) {
p0 = p1;
p1 = p2;
p2 = points[i];
tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
}
return tangents;
}
function d3_svg_lineBasis(points) {
if (points.length < 3) return d3_svg_lineLinear(points);
var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0 ];
d3_svg_lineBasisBezier(path, px, py);
while (++i < n) {
pi = points[i];
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
i = -1;
while (++i < 2) {
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
return path.join("");
}
function d3_svg_lineBasisOpen(points) {
if (points.length < 4) return d3_svg_lineLinear(points);
var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
while (++i < 3) {
pi = points[i];
px.push(pi[0]);
py.push(pi[1]);
}
path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
--i;
while (++i < n) {
pi = points[i];
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
return path.join("");
}
function d3_svg_lineBasisClosed(points) {
var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
while (++i < 4) {
pi = points[i % n];
px.push(pi[0]);
py.push(pi[1]);
}
path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
--i;
while (++i < m) {
pi = points[i % n];
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
return path.join("");
}
function d3_svg_lineBundle(points, tension) {
var n = points.length - 1;
if (n) {
var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
while (++i <= n) {
p = points[i];
t = i / n;
p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
}
}
return d3_svg_lineBasis(points);
}
function d3_svg_lineDot4(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
}
var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
function d3_svg_lineBasisBezier(path, x, y) {
path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
}
function d3_svg_lineSlope(p0, p1) {
return (p1[1] - p0[1]) / (p1[0] - p0[0]);
}
function d3_svg_lineFiniteDifferences(points) {
var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
while (++i < j) {
m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
}
m[i] = d;
return m;
}
function d3_svg_lineMonotoneTangents(points) {
var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
while (++i < j) {
d = d3_svg_lineSlope(points[i], points[i + 1]);
if (Math.abs(d) < 1e-6) {
m[i] = m[i + 1] = 0;
} else {
a = m[i] / d;
b = m[i + 1] / d;
s = a * a + b * b;
if (s > 9) {
s = d * 3 / Math.sqrt(s);
m[i] = s * a;
m[i + 1] = s * b;
}
}
}
i = -1;
while (++i <= j) {
s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
tangents.push([ s || 0, m[i] * s || 0 ]);
}
return tangents;
}
function d3_svg_lineMonotone(points) {
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
}
d3.geom.delaunay = function(vertices) {
var edges = vertices.map(function() {
return [];
}), triangles = [];
d3_geom_voronoiTessellate(vertices, function(e) {
edges[e.region.l.index].push(vertices[e.region.r.index]);
});
return polygons;
edges.forEach(function(edge, i) {
var v = vertices[i], cx = v[0], cy = v[1];
edge.forEach(function(v) {
v.angle = Math.atan2(v[0] - cx, v[1] - cy);
});
edge.sort(function(a, b) {
return a.angle - b.angle;
});
for (var j = 0, m = edge.length - 1; j < m; j++) {
triangles.push([ v, edge[j], edge[j + 1] ]);
}
});
return triangles;
};
d3.geom.voronoi = function(vertices) {
var size = null, x = d3_svg_lineX, y = d3_svg_lineY, clip, compat;
if (compat = arguments.length) return voronoi(vertices);
function voronoi(data) {
var points = [], polygons = [], fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length, Z = 1e6;
for (i = 0; i < n; ++i) {
points.push([ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]);
polygons.push([]);
}
d3_geom_voronoiTessellate(points, function(e) {
var s1, s2, x1, x2, y1, y2;
if (e.a === 1 && e.b >= 0) {
s1 = e.ep.r;
s2 = e.ep.l;
} else {
s1 = e.ep.l;
s2 = e.ep.r;
}
if (e.a === 1) {
y1 = s1 ? s1.y : -Z;
x1 = e.c - e.b * y1;
y2 = s2 ? s2.y : Z;
x2 = e.c - e.b * y2;
} else {
x1 = s1 ? s1.x : -Z;
y1 = e.c - e.a * x1;
x2 = s2 ? s2.x : Z;
y2 = e.c - e.a * x2;
}
var v1 = [ x1, y1 ], v2 = [ x2, y2 ];
polygons[e.region.l.index].push(v1, v2);
polygons[e.region.r.index].push(v1, v2);
});
polygons = polygons.map(function(polygon, i) {
var cx = points[i][0], cy = points[i][1], angle = polygon.map(function(v) {
return Math.atan2(v[0] - cx, v[1] - cy);
}), order = d3.range(polygon.length).sort(function(a, b) {
return angle[a] - angle[b];
});
return order.filter(function(d, i) {
return !i || angle[d] - angle[order[i - 1]] > ε;
}).map(function(d) {
return polygon[d];
});
});
polygons.forEach(function(polygon, i) {
var n = polygon.length;
if (!n) return polygon.push([ -Z, -Z ], [ -Z, Z ], [ Z, Z ], [ Z, -Z ]);
if (n > 2) return;
var p0 = points[i], p1 = polygon[0], p2 = polygon[1], x0 = p0[0], y0 = p0[1], x1 = p1[0], y1 = p1[1], x2 = p2[0], y2 = p2[1], dx = Math.abs(x2 - x1), dy = y2 - y1;
if (Math.abs(dy) < ε) {
var y = y0 < y1 ? -Z : Z;
polygon.push([ -Z, y ], [ Z, y ]);
} else if (dx < ε) {
var x = x0 < x1 ? -Z : Z;
polygon.push([ x, -Z ], [ x, Z ]);
} else {
var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z, z = Math.abs(dy) - dx;
if (Math.abs(z) < ε) {
polygon.push([ dy < 0 ? y : -y, y ]);
} else {
if (z > 0) y *= -1;
polygon.push([ -Z, y ], [ Z, y ]);
}
}
});
if (clip) for (i = 0; i < n; ++i) clip(polygons[i]);
if (!compat) for (i = 0; i < n; ++i) polygons[i].point = data[i];
return polygons;
}
voronoi.x = function(_) {
return arguments.length ? (x = _, voronoi) : x;
};
voronoi.y = function(_) {
return arguments.length ? (y = _, voronoi) : y;
};
voronoi.size = function(_) {
if (!arguments.length) return size;
if (_ == null) {
clip = null;
} else {
var w = +_[0], h = +_[1];
clip = d3.geom.polygon([ [ 0, 0 ], [ 0, h ], [ w, h ], [ w, 0 ] ]).clip;
}
return voronoi;
};
voronoi.links = function(data) {
var points = [], graph = [], links = [], fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length;
for (i = 0; i < n; ++i) {
points.push([ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]);
graph.push([]);
}
d3_geom_voronoiTessellate(points, function(e) {
var l = e.region.l.index, r = e.region.r.index;
if (graph[l][r]) return;
graph[l][r] = graph[r][l] = true;
links.push({
source: data[l],
target: data[r]
});
});
return links;
};
voronoi.triangles = function(data) {
var points = [], point, fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length;
for (i = 0; i < n; ++i) {
point = [ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ];
point.data = d;
points.push(point);
}
return d3.geom.delaunay(points).map(function(triangle) {
return triangle.map(function(vertex) {
return vertex.data;
});
});
};
return voronoi;
};
var d3_geom_voronoiOpposite = {
l: "r",
@ -4130,27 +4461,6 @@ d3 = function() {
callback(lbnd.edge);
}
}
d3.geom.delaunay = function(vertices) {
var edges = vertices.map(function() {
return [];
}), triangles = [];
d3_geom_voronoiTessellate(vertices, function(e) {
edges[e.region.l.index].push(vertices[e.region.r.index]);
});
edges.forEach(function(edge, i) {
var v = vertices[i], cx = v[0], cy = v[1];
edge.forEach(function(v) {
v.angle = Math.atan2(v[0] - cx, v[1] - cy);
});
edge.sort(function(a, b) {
return a.angle - b.angle;
});
for (var j = 0, m = edge.length - 1; j < m; j++) {
triangles.push([ v, edge[j], edge[j + 1] ]);
}
});
return triangles;
};
d3.geom.quadtree = function(points, x1, y1, x2, y2) {
var p, i = -1, n = points.length;
if (arguments.length < 5) {
@ -5926,319 +6236,6 @@ d3 = function() {
dy: dy
};
}
d3.svg = {};
function d3_svg_line(projection) {
var x = d3_svg_lineX, y = d3_svg_lineY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7;
function line(data) {
var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y);
function segment() {
segments.push("M", interpolate(projection(points), tension));
}
while (++i < n) {
if (defined.call(this, d = data[i], i)) {
points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]);
} else if (points.length) {
segment();
points = [];
}
}
if (points.length) segment();
return segments.length ? segments.join("") : null;
}
line.x = function(_) {
if (!arguments.length) return x;
x = _;
return line;
};
line.y = function(_) {
if (!arguments.length) return y;
y = _;
return line;
};
line.defined = function(_) {
if (!arguments.length) return defined;
defined = _;
return line;
};
line.interpolate = function(_) {
if (!arguments.length) return interpolateKey;
if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key;
return line;
};
line.tension = function(_) {
if (!arguments.length) return tension;
tension = _;
return line;
};
return line;
}
d3.svg.line = function() {
return d3_svg_line(d3_identity);
};
function d3_svg_lineX(d) {
return d[0];
}
function d3_svg_lineY(d) {
return d[1];
}
var d3_svg_lineInterpolators = d3.map({
linear: d3_svg_lineLinear,
"linear-closed": d3_svg_lineLinearClosed,
"step-before": d3_svg_lineStepBefore,
"step-after": d3_svg_lineStepAfter,
basis: d3_svg_lineBasis,
"basis-open": d3_svg_lineBasisOpen,
"basis-closed": d3_svg_lineBasisClosed,
bundle: d3_svg_lineBundle,
cardinal: d3_svg_lineCardinal,
"cardinal-open": d3_svg_lineCardinalOpen,
"cardinal-closed": d3_svg_lineCardinalClosed,
monotone: d3_svg_lineMonotone
});
d3_svg_lineInterpolators.forEach(function(key, value) {
value.key = key;
value.closed = /-closed$/.test(key);
});
function d3_svg_lineLinear(points) {
return points.join("L");
}
function d3_svg_lineLinearClosed(points) {
return d3_svg_lineLinear(points) + "Z";
}
function d3_svg_lineStepBefore(points) {
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]);
return path.join("");
}
function d3_svg_lineStepAfter(points) {
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ];
while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]);
return path.join("");
}
function d3_svg_lineCardinalOpen(points, tension) {
return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, points.length - 1), d3_svg_lineCardinalTangents(points, tension));
}
function d3_svg_lineCardinalClosed(points, tension) {
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]),
points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension));
}
function d3_svg_lineCardinal(points, tension) {
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension));
}
function d3_svg_lineHermite(points, tangents) {
if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) {
return d3_svg_lineLinear(points);
}
var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1;
if (quad) {
path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1];
p0 = points[1];
pi = 2;
}
if (tangents.length > 1) {
t = tangents[1];
p = points[pi];
pi++;
path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
for (var i = 2; i < tangents.length; i++, pi++) {
p = points[pi];
t = tangents[i];
path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1];
}
}
if (quad) {
var lp = points[pi];
path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1];
}
return path;
}
function d3_svg_lineCardinalTangents(points, tension) {
var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length;
while (++i < n) {
p0 = p1;
p1 = p2;
p2 = points[i];
tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]);
}
return tangents;
}
function d3_svg_lineBasis(points) {
if (points.length < 3) return d3_svg_lineLinear(points);
var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0 ];
d3_svg_lineBasisBezier(path, px, py);
while (++i < n) {
pi = points[i];
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
i = -1;
while (++i < 2) {
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
return path.join("");
}
function d3_svg_lineBasisOpen(points) {
if (points.length < 4) return d3_svg_lineLinear(points);
var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ];
while (++i < 3) {
pi = points[i];
px.push(pi[0]);
py.push(pi[1]);
}
path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py));
--i;
while (++i < n) {
pi = points[i];
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
return path.join("");
}
function d3_svg_lineBasisClosed(points) {
var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = [];
while (++i < 4) {
pi = points[i % n];
px.push(pi[0]);
py.push(pi[1]);
}
path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ];
--i;
while (++i < m) {
pi = points[i % n];
px.shift();
px.push(pi[0]);
py.shift();
py.push(pi[1]);
d3_svg_lineBasisBezier(path, px, py);
}
return path.join("");
}
function d3_svg_lineBundle(points, tension) {
var n = points.length - 1;
if (n) {
var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t;
while (++i <= n) {
p = points[i];
t = i / n;
p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx);
p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy);
}
}
return d3_svg_lineBasis(points);
}
function d3_svg_lineDot4(a, b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
}
var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ];
function d3_svg_lineBasisBezier(path, x, y) {
path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y));
}
function d3_svg_lineSlope(p0, p1) {
return (p1[1] - p0[1]) / (p1[0] - p0[0]);
}
function d3_svg_lineFiniteDifferences(points) {
var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1);
while (++i < j) {
m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2;
}
m[i] = d;
return m;
}
function d3_svg_lineMonotoneTangents(points) {
var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1;
while (++i < j) {
d = d3_svg_lineSlope(points[i], points[i + 1]);
if (Math.abs(d) < 1e-6) {
m[i] = m[i + 1] = 0;
} else {
a = m[i] / d;
b = m[i + 1] / d;
s = a * a + b * b;
if (s > 9) {
s = d * 3 / Math.sqrt(s);
m[i] = s * a;
m[i + 1] = s * b;
}
}
}
i = -1;
while (++i <= j) {
s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i]));
tangents.push([ s || 0, m[i] * s || 0 ]);
}
return tangents;
}
function d3_svg_lineMonotone(points) {
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points));
}
d3.layout.voronoi = function() {
var size = null, x = d3_svg_lineX, y = d3_svg_lineY, clip;
function voronoi(data) {
var points = [], cells, fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length;
for (i = 0; i < n; ++i) points.push([ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]);
cells = d3.geom.voronoi(points);
for (i = 0; i < n; ++i) cells[i].data = data[i];
if (clip) for (i = 0; i < n; ++i) clip(cells[i]);
return cells;
}
voronoi.x = function(_) {
return arguments.length ? (x = _, voronoi) : x;
};
voronoi.y = function(_) {
return arguments.length ? (y = _, voronoi) : y;
};
voronoi.size = function(_) {
if (!arguments.length) return size;
if (_ == null) {
clip = null;
} else {
var w = +_[0], h = +_[1];
clip = d3.geom.polygon([ [ 0, 0 ], [ 0, h ], [ w, h ], [ w, 0 ] ]).clip;
}
return voronoi;
};
voronoi.links = function(data) {
var points = [], graph = [], links = [], fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length;
for (i = 0; i < n; ++i) {
points.push([ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ]);
graph.push([]);
}
d3_geom_voronoiTessellate(points, function(e) {
var l = e.region.l.index, r = e.region.r.index;
if (graph[l][r]) return;
graph[l][r] = graph[r][l] = true;
links.push({
source: data[l],
target: data[r]
});
});
return links;
};
voronoi.triangles = function(data) {
var points = [], point, fx = d3_functor(x), fy = d3_functor(y), d, i, n = data.length;
for (i = 0; i < n; ++i) {
point = [ +fx.call(this, d = data[i], i), +fy.call(this, d, i) ];
point.data = d;
points.push(point);
}
return d3.geom.delaunay(points).map(function(triangle) {
return triangle.map(function(vertex) {
return vertex.data;
});
});
};
return voronoi;
};
d3.random = {
normal: function(µ, σ) {
var n = arguments.length;

8
d3.min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,6 +1,10 @@
import "../arrays/range";
import "../core/functor";
import "../math/trigonometry";
import "../svg/line";
import "delaunay";
import "geom";
import "polygon";
// Adapted from Nicolas Garcia Belmonte's JIT implementation:
// http://blog.thejit.org/2010/02/12/voronoi-tessellation/
@ -23,84 +27,177 @@ import "geom";
* @returns polygons [[[x1, y1], [x2, y2], ], ]
*/
d3.geom.voronoi = function(vertices) {
var polygons = vertices.map(function() { return []; }),
Z = 1e6;
var size = null,
x = d3_svg_lineX,
y = d3_svg_lineY,
clip,
compat;
d3_geom_voronoiTessellate(vertices, function(e) {
var s1,
s2,
x1,
x2,
y1,
y2;
if (e.a === 1 && e.b >= 0) {
s1 = e.ep.r;
s2 = e.ep.l;
} else {
s1 = e.ep.l;
s2 = e.ep.r;
// For backwards-compatibility.
if (compat = arguments.length) return voronoi(vertices);
function voronoi(data) {
var points = [],
polygons = [],
fx = d3_functor(x),
fy = d3_functor(y),
d,
i,
n = data.length,
Z = 1e6;
for (i = 0; i < n; ++i) {
points.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]);
polygons.push([]);
}
if (e.a === 1) {
y1 = s1 ? s1.y : -Z;
x1 = e.c - e.b * y1;
y2 = s2 ? s2.y : Z;
x2 = e.c - e.b * y2;
} else {
x1 = s1 ? s1.x : -Z;
y1 = e.c - e.a * x1;
x2 = s2 ? s2.x : Z;
y2 = e.c - e.a * x2;
}
var v1 = [x1, y1],
v2 = [x2, y2];
polygons[e.region.l.index].push(v1, v2);
polygons[e.region.r.index].push(v1, v2);
});
// Connect edges into counterclockwise polygons without coincident points.
polygons = polygons.map(function(polygon, i) {
var cx = vertices[i][0],
cy = vertices[i][1],
angle = polygon.map(function(v) { return Math.atan2(v[0] - cx, v[1] - cy); }),
order = d3.range(polygon.length).sort(function(a, b) { return angle[a] - angle[b]; });
return order
.filter(function(d, i) { return !i || (angle[d] - angle[order[i - 1]] > ε); })
.map(function(d) { return polygon[d]; });
});
// Fix degenerate polygons.
polygons.forEach(function(polygon, i) {
var n = polygon.length;
if (!n) return polygon.push([-Z, -Z], [-Z, Z], [Z, Z], [Z, -Z]);
if (n > 2) return;
var p0 = vertices[i],
p1 = polygon[0],
p2 = polygon[1],
x0 = p0[0], y0 = p0[1],
x1 = p1[0], y1 = p1[1],
x2 = p2[0], y2 = p2[1],
dx = Math.abs(x2 - x1), dy = y2 - y1;
if (Math.abs(dy) < ε) { // 0°
var y = y0 < y1 ? -Z : Z;
polygon.push([-Z, y], [Z, y]);
} else if (dx < ε) { // ±90°
var x = x0 < x1 ? -Z : Z;
polygon.push([x, -Z], [x, Z]);
} else {
var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z,
z = Math.abs(dy) - dx;
if (Math.abs(z) < ε) { // ±45°
polygon.push([dy < 0 ? y : -y, y]);
d3_geom_voronoiTessellate(points, function(e) {
var s1,
s2,
x1,
x2,
y1,
y2;
if (e.a === 1 && e.b >= 0) {
s1 = e.ep.r;
s2 = e.ep.l;
} else {
if (z > 0) y *= -1;
polygon.push([-Z, y], [Z, y]);
s1 = e.ep.l;
s2 = e.ep.r;
}
}
});
if (e.a === 1) {
y1 = s1 ? s1.y : -Z;
x1 = e.c - e.b * y1;
y2 = s2 ? s2.y : Z;
x2 = e.c - e.b * y2;
} else {
x1 = s1 ? s1.x : -Z;
y1 = e.c - e.a * x1;
x2 = s2 ? s2.x : Z;
y2 = e.c - e.a * x2;
}
var v1 = [x1, y1],
v2 = [x2, y2];
polygons[e.region.l.index].push(v1, v2);
polygons[e.region.r.index].push(v1, v2);
});
return polygons;
// Connect edges into counterclockwise polygons without coincident points.
polygons = polygons.map(function(polygon, i) {
var cx = points[i][0],
cy = points[i][1],
angle = polygon.map(function(v) { return Math.atan2(v[0] - cx, v[1] - cy); }),
order = d3.range(polygon.length).sort(function(a, b) { return angle[a] - angle[b]; });
return order
.filter(function(d, i) { return !i || (angle[d] - angle[order[i - 1]] > ε); })
.map(function(d) { return polygon[d]; });
});
// Fix degenerate polygons.
polygons.forEach(function(polygon, i) {
var n = polygon.length;
if (!n) return polygon.push([-Z, -Z], [-Z, Z], [Z, Z], [Z, -Z]);
if (n > 2) return;
var p0 = points[i],
p1 = polygon[0],
p2 = polygon[1],
x0 = p0[0], y0 = p0[1],
x1 = p1[0], y1 = p1[1],
x2 = p2[0], y2 = p2[1],
dx = Math.abs(x2 - x1), dy = y2 - y1;
if (Math.abs(dy) < ε) { // 0°
var y = y0 < y1 ? -Z : Z;
polygon.push([-Z, y], [Z, y]);
} else if (dx < ε) { // ±90°
var x = x0 < x1 ? -Z : Z;
polygon.push([x, -Z], [x, Z]);
} else {
var y = (x2 - x1) * (y1 - y0) < (x1 - x0) * (y2 - y1) ? Z : -Z,
z = Math.abs(dy) - dx;
if (Math.abs(z) < ε) { // ±45°
polygon.push([dy < 0 ? y : -y, y]);
} else {
if (z > 0) y *= -1;
polygon.push([-Z, y], [Z, y]);
}
}
});
if (clip) for (i = 0; i < n; ++i) clip(polygons[i]);
if (!compat) for (i = 0; i < n; ++i) polygons[i].point = data[i];
return polygons;
}
voronoi.x = function(_) {
return arguments.length ? (x = _, voronoi) : x;
};
voronoi.y = function(_) {
return arguments.length ? (y = _, voronoi) : y;
};
voronoi.size = function(_) {
if (!arguments.length) return size;
if (_ == null) {
clip = null;
} else {
var w = +_[0], h = +_[1];
clip = d3.geom.polygon([[0, 0], [0, h], [w, h], [w, 0]]).clip;
}
return voronoi;
};
voronoi.links = function(data) {
var points = [],
graph = [],
links = [],
fx = d3_functor(x),
fy = d3_functor(y),
d,
i,
n = data.length;
for (i = 0; i < n; ++i) {
points.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]);
graph.push([]);
}
d3_geom_voronoiTessellate(points, function(e) {
var l = e.region.l.index,
r = e.region.r.index;
if (graph[l][r]) return;
graph[l][r] = graph[r][l] = true;
links.push({source: data[l], target: data[r]});
});
return links;
};
voronoi.triangles = function(data) {
var points = [],
point,
fx = d3_functor(x),
fy = d3_functor(y),
d,
i,
n = data.length;
for (i = 0; i < n; ++i) {
point = [+fx.call(this, d = data[i], i), +fy.call(this, d, i)];
point.data = d;
points.push(point);
}
return d3.geom.delaunay(points).map(function(triangle) {
return triangle.map(function(vertex) {
return vertex.data;
});
});
};
return voronoi;
};
var d3_geom_voronoiOpposite = {l: "r", r: "l"};

Просмотреть файл

@ -11,4 +11,3 @@ import "pack";
import "cluster";
import "tree";
import "treemap";
import "voronoi";

Просмотреть файл

@ -1,97 +0,0 @@
import "../core/functor";
import "../geom/delaunay";
import "../geom/polygon";
import "../geom/voronoi";
import "../svg/line";
import "layout";
d3.layout.voronoi = function() {
var size = null,
x = d3_svg_lineX,
y = d3_svg_lineY,
clip;
function voronoi(data) {
var points = [],
cells,
fx = d3_functor(x),
fy = d3_functor(y),
d,
i,
n = data.length;
for (i = 0; i < n; ++i) points.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]);
cells = d3.geom.voronoi(points);
for (i = 0; i < n; ++i) cells[i].data = data[i];
if (clip) for (i = 0; i < n; ++i) clip(cells[i]);
return cells;
}
voronoi.x = function(_) {
return arguments.length ? (x = _, voronoi) : x;
};
voronoi.y = function(_) {
return arguments.length ? (y = _, voronoi) : y;
};
voronoi.size = function(_) {
if (!arguments.length) return size;
if (_ == null) {
clip = null;
} else {
var w = +_[0], h = +_[1];
clip = d3.geom.polygon([[0, 0], [0, h], [w, h], [w, 0]]).clip;
}
return voronoi;
};
voronoi.links = function(data) {
var points = [],
graph = [],
links = [],
fx = d3_functor(x),
fy = d3_functor(y),
d,
i,
n = data.length;
for (i = 0; i < n; ++i) {
points.push([+fx.call(this, d = data[i], i), +fy.call(this, d, i)]);
graph.push([]);
}
d3_geom_voronoiTessellate(points, function(e) {
var l = e.region.l.index,
r = e.region.r.index;
if (graph[l][r]) return;
graph[l][r] = graph[r][l] = true;
links.push({source: data[l], target: data[r]});
});
return links;
};
voronoi.triangles = function(data) {
var points = [],
point,
fx = d3_functor(x),
fy = d3_functor(y),
d,
i,
n = data.length;
for (i = 0; i < n; ++i) {
point = [+fx.call(this, d = data[i], i), +fy.call(this, d, i)];
point.data = d;
points.push(point);
}
return d3.geom.delaunay(points).map(function(triangle) {
return triangle.map(function(vertex) {
return vertex.data;
});
});
};
return voronoi;
};

Просмотреть файл

@ -7,37 +7,169 @@ var suite = vows.describe("d3.geom.voronoi");
suite.addBatch({
"voronoi": {
topic: load("geom/voronoi").expression("d3.geom.voronoi"),
"with zero points": {
"returns the empty array": function(voronoi) {
assert.deepEqual(voronoi([]), []);
"the default voronoi layout": {
topic: function(voronoi) {
return voronoi();
},
"has no defined size": function(v) {
assert.isNull(v.size());
},
"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]],
[[-178046.7857142857, 1e6], [179096.07142857145, -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]]);
}
},
"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: [760, 300]},
{source: [500, 250], target: [760, 300]},
{source: [200, 200], target: [500, 250]}
]);
}
},
"triangles": {
"for three points": function(v) {
assert.deepEqual(v.triangles([[200, 200], [500, 250], [760, 300]]), [
[[200, 200], [760, 300], [500, 250]]
]);
}
}
},
"with one point": {
"returns the semi-infinite bounding box": function(voronoi) {
assert.deepEqual(voronoi([[50, 50]], 100, 100), [[[-1000000,-1000000],[-1000000,1000000],[1000000,1000000],[1000000,-1000000]]]);
"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);
}
}
},
"with two points": {
"separated by a line at 90° (vertical)": function(voronoi) {
assert.deepEqual(voronoi([[50, 25], [50, 75]], 100, 100), [[[-1000000,50],[1000000,50],[-1000000,-1000000],[1000000,-1000000]],[[-1000000,50],[1000000,50],[-1000000,1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[50, 75], [50, 25]], 100, 100), [[[-1000000,50],[1000000,50],[-1000000,1000000],[1000000,1000000]],[[-1000000,50],[1000000,50],[-1000000,-1000000],[1000000,-1000000]]]);
"a voronoi layout with size 960x500": {
topic: function(voronoi) {
return voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.size([960, 500]);
},
"separated by a line at 0° (horizontal)": function(voronoi) {
assert.deepEqual(voronoi([[25, 50], [75, 50]], 100, 100), [[[50,1000000],[50,-1000000],[-1000000,-1000000],[-1000000,1000000]],[[50,-1000000],[50,1000000],[1000000,-1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[75, 50], [25, 50]], 100, 100), [[[50,-1000000],[50,1000000],[1000000,-1000000],[1000000,1000000]],[[50,1000000],[50,-1000000],[-1000000,-1000000],[-1000000,1000000]]]);
},
"separated by a line at 45° (diagonal)": function(voronoi) {
assert.deepEqual(voronoi([[25, 25], [75, 75]], 100, 100), [[[-999900,1000000],[1000100,-1000000],[-1000000,-1000000]],[[-999900,1000000],[1000100,-1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[75, 25], [25, 75]], 100, 100), [[[-1000000,-1000000],[1000000,1000000],[1000000,-1000000]],[[-1000000,-1000000],[1000000,1000000],[-1000000,1000000]]]);
},
"separated by an arbitrary diagonal": function(voronoi) {
assert.deepEqual(voronoi([[25, 25], [50, 75]], 100, 100), [[[-1000000,500068.75],[1000000,-499931.25],[-1000000,-1000000],[1000000,-1000000]],[[-1000000,500068.75],[1000000,-499931.25],[-1000000,1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[25, 25], [75, 50]], 100, 100), [[[-499931.25,1000000],[500068.75,-1000000],[-1000000,1000000],[1000000,1000000]], [[-499931.25,1000000],[500068.75,-1000000],[-1000000,-1000000],[1000000,-1000000]]]);
"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]],
[[960, 0], [960, 500], [435.35714285715324, 500], [524.6428571428696, 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;
});
}));
}
}
},
"with three points": {
"collinear": function(voronoi) {
assert.deepEqual(voronoi([[25, 25], [50, 50], [75, 75]], 100, 100), [[[-999925,1000000],[1000075,-1000000],[-1000000,-1000000]],[[-999925,1000000],[-999875,1000000],[1000125,-1000000],[1000075,-1000000]],[[-999875,1000000],[1000125,-1000000],[1000000,1000000]]]);
"the default voronoi layout used directly": {
"with zero points": {
"returns the empty array": function(voronoi) {
assert.deepEqual(voronoi([]), []);
}
},
"with one point": {
"returns the semi-infinite bounding box": function(voronoi) {
assert.deepEqual(voronoi([[50, 50]], 100, 100), [[[-1000000,-1000000],[-1000000,1000000],[1000000,1000000],[1000000,-1000000]]]);
}
},
"with two points": {
"separated by a line at 90° (vertical)": function(voronoi) {
assert.deepEqual(voronoi([[50, 25], [50, 75]], 100, 100), [[[-1000000,50],[1000000,50],[-1000000,-1000000],[1000000,-1000000]],[[-1000000,50],[1000000,50],[-1000000,1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[50, 75], [50, 25]], 100, 100), [[[-1000000,50],[1000000,50],[-1000000,1000000],[1000000,1000000]],[[-1000000,50],[1000000,50],[-1000000,-1000000],[1000000,-1000000]]]);
},
"separated by a line at 0° (horizontal)": function(voronoi) {
assert.deepEqual(voronoi([[25, 50], [75, 50]], 100, 100), [[[50,1000000],[50,-1000000],[-1000000,-1000000],[-1000000,1000000]],[[50,-1000000],[50,1000000],[1000000,-1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[75, 50], [25, 50]], 100, 100), [[[50,-1000000],[50,1000000],[1000000,-1000000],[1000000,1000000]],[[50,1000000],[50,-1000000],[-1000000,-1000000],[-1000000,1000000]]]);
},
"separated by a line at 45° (diagonal)": function(voronoi) {
assert.deepEqual(voronoi([[25, 25], [75, 75]], 100, 100), [[[-999900,1000000],[1000100,-1000000],[-1000000,-1000000]],[[-999900,1000000],[1000100,-1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[75, 25], [25, 75]], 100, 100), [[[-1000000,-1000000],[1000000,1000000],[1000000,-1000000]],[[-1000000,-1000000],[1000000,1000000],[-1000000,1000000]]]);
},
"separated by an arbitrary diagonal": function(voronoi) {
assert.deepEqual(voronoi([[25, 25], [50, 75]], 100, 100), [[[-1000000,500068.75],[1000000,-499931.25],[-1000000,-1000000],[1000000,-1000000]],[[-1000000,500068.75],[1000000,-499931.25],[-1000000,1000000],[1000000,1000000]]]);
assert.deepEqual(voronoi([[25, 25], [75, 50]], 100, 100), [[[-499931.25,1000000],[500068.75,-1000000],[-1000000,1000000],[1000000,1000000]], [[-499931.25,1000000],[500068.75,-1000000],[-1000000,-1000000],[1000000,-1000000]]]);
}
},
"with three points": {
"collinear": function(voronoi) {
assert.deepEqual(voronoi([[25, 25], [50, 50], [75, 75]], 100, 100), [[[-999925,1000000],[1000075,-1000000],[-1000000,-1000000]],[[-999925,1000000],[-999875,1000000],[1000125,-1000000],[1000075,-1000000]],[[-999875,1000000],[1000125,-1000000],[1000000,1000000]]]);
}
}
}
}

Просмотреть файл

@ -1,142 +0,0 @@
var vows = require("vows"),
load = require("../load"),
assert = require("../assert");
var suite = vows.describe("d3.layout.voronoi");
suite.addBatch({
"voronoi": {
topic: load("layout/voronoi").expression("d3.layout.voronoi"),
"the default voronoi layout": {
topic: function(voronoi) {
return voronoi();
},
"has no defined size": function(v) {
assert.isNull(v.size());
},
"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]],
[[-178046.7857142857, 1e6], [179096.07142857145, -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' data points back to the input data": function(cells) {
assert.deepEqual(cells.map(function(cell) { return cell.data; }), [[200, 200], [760, 300]]);
}
},
"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: [760, 300]},
{source: [500, 250], target: [760, 300]},
{source: [200, 200], target: [500, 250]}
]);
}
},
"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);
}
}
},
"a voronoi layout with size 960x500": {
topic: function(voronoi) {
return voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.size([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]],
[[960, 0], [960, 500], [435.35714285715324, 500], [524.6428571428696, 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;
});
}));
}
}
}
}
});
suite.export(module);