FIXME undefined data points are not represented correctly
Last active
February 25, 2016 13:48
-
-
Save nitaku/633fffc54e45289175d7 to your computer and use it in GitHub Desktop.
Term frequencies (Cassandra)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | |
typeof define === 'function' && define.amd ? define('d3-path', ['exports'], factory) : | |
factory((global.d3_path = {})); | |
}(this, function (exports) { 'use strict'; | |
var pi = Math.PI; | |
var tau = 2 * pi; | |
var epsilon = 1e-6; | |
var tauEpsilon = tau - epsilon; | |
function Path() { | |
this._x0 = this._y0 = // start of current subpath | |
this._x1 = this._y1 = null; // end of current subpath | |
this._ = []; | |
} | |
function path() { | |
return new Path; | |
} | |
Path.prototype = path.prototype = { | |
moveTo: function(x, y) { | |
this._.push("M", this._x0 = this._x1 = +x, ",", this._y0 = this._y1 = +y); | |
}, | |
closePath: function() { | |
if (this._x1 !== null) { | |
this._x1 = this._x0, this._y1 = this._y0; | |
this._.push("Z"); | |
} | |
}, | |
lineTo: function(x, y) { | |
this._.push("L", this._x1 = +x, ",", this._y1 = +y); | |
}, | |
quadraticCurveTo: function(x1, y1, x, y) { | |
this._.push("Q", +x1, ",", +y1, ",", this._x1 = +x, ",", this._y1 = +y); | |
}, | |
bezierCurveTo: function(x1, y1, x2, y2, x, y) { | |
this._.push("C", +x1, ",", +y1, ",", +x2, ",", +y2, ",", this._x1 = +x, ",", this._y1 = +y); | |
}, | |
arcTo: function(x1, y1, x2, y2, r) { | |
x1 = +x1, y1 = +y1, x2 = +x2, y2 = +y2, r = +r; | |
var x0 = this._x1, | |
y0 = this._y1, | |
x21 = x2 - x1, | |
y21 = y2 - y1, | |
x01 = x0 - x1, | |
y01 = y0 - y1, | |
l01_2 = x01 * x01 + y01 * y01; | |
// Is the radius negative? Error. | |
if (r < 0) throw new Error("negative radius: " + r); | |
// Is this path empty? Move to (x1,y1). | |
if (this._x1 === null) { | |
this._.push( | |
"M", this._x1 = x1, ",", this._y1 = y1 | |
); | |
} | |
// Or, is (x1,y1) coincident with (x0,y0)? Do nothing. | |
else if (!(l01_2 > epsilon)); | |
// Or, are (x0,y0), (x1,y1) and (x2,y2) collinear? | |
// Equivalently, is (x1,y1) coincident with (x2,y2)? | |
// Or, is the radius zero? Line to (x1,y1). | |
else if (!(Math.abs(y01 * x21 - y21 * x01) > epsilon) || !r) { | |
this._.push( | |
"L", this._x1 = x1, ",", this._y1 = y1 | |
); | |
} | |
// Otherwise, draw an arc! | |
else { | |
var x20 = x2 - x0, | |
y20 = y2 - y0, | |
l21_2 = x21 * x21 + y21 * y21, | |
l20_2 = x20 * x20 + y20 * y20, | |
l21 = Math.sqrt(l21_2), | |
l01 = Math.sqrt(l01_2), | |
l = r * Math.tan((pi - Math.acos((l21_2 + l01_2 - l20_2) / (2 * l21 * l01))) / 2), | |
t01 = l / l01, | |
t21 = l / l21; | |
// If the start tangent is not coincident with (x0,y0), line to. | |
if (Math.abs(t01 - 1) > epsilon) { | |
this._.push( | |
"L", x1 + t01 * x01, ",", y1 + t01 * y01 | |
); | |
} | |
this._.push( | |
"A", r, ",", r, ",0,0,", +(y01 * x20 > x01 * y20), ",", this._x1 = x1 + t21 * x21, ",", this._y1 = y1 + t21 * y21 | |
); | |
} | |
}, | |
arc: function(x, y, r, a0, a1, ccw) { | |
x = +x, y = +y, r = +r; | |
var dx = r * Math.cos(a0), | |
dy = r * Math.sin(a0), | |
x0 = x + dx, | |
y0 = y + dy, | |
cw = 1 ^ ccw, | |
da = ccw ? a0 - a1 : a1 - a0; | |
// Is the radius negative? Error. | |
if (r < 0) throw new Error("negative radius: " + r); | |
// Is this path empty? Move to (x0,y0). | |
if (this._x1 === null) { | |
this._.push( | |
"M", x0, ",", y0 | |
); | |
} | |
// Or, is (x0,y0) not coincident with the previous point? Line to (x0,y0). | |
else if (Math.abs(this._x1 - x0) > epsilon || Math.abs(this._y1 - y0) > epsilon) { | |
this._.push( | |
"L", x0, ",", y0 | |
); | |
} | |
// Is this arc empty? We’re done. | |
if (!r) return; | |
// Is this a complete circle? Draw two arcs to complete the circle. | |
if (da > tauEpsilon) { | |
this._.push( | |
"A", r, ",", r, ",0,1,", cw, ",", x - dx, ",", y - dy, | |
"A", r, ",", r, ",0,1,", cw, ",", this._x1 = x0, ",", this._y1 = y0 | |
); | |
} | |
// Otherwise, draw an arc! | |
else { | |
if (da < 0) da = da % tau + tau; | |
this._.push( | |
"A", r, ",", r, ",0,", +(da >= pi), ",", cw, ",", this._x1 = x + r * Math.cos(a1), ",", this._y1 = y + r * Math.sin(a1) | |
); | |
} | |
}, | |
rect: function(x, y, w, h) { | |
this._.push("M", this._x0 = this._x1 = +x, ",", this._y0 = this._y1 = +y, "h", +w, "v", +h, "h", -w, "Z"); | |
}, | |
toString: function() { | |
return this._.join(""); | |
} | |
}; | |
var version = "0.1.2"; | |
exports.version = version; | |
exports.path = path; | |
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-path')) : | |
typeof define === 'function' && define.amd ? define('d3-shape', ['exports', 'd3-path'], factory) : | |
factory((global.d3_shape = {}),global.d3_path); | |
}(this, function (exports,d3Path) { 'use strict'; | |
function constant(x) { | |
return function constant() { | |
return x; | |
}; | |
}; | |
var pi = Math.PI; | |
var halfPi = pi / 2; | |
var tau = 2 * pi; | |
function arcInnerRadius(d) { | |
return d.innerRadius; | |
} | |
function arcOuterRadius(d) { | |
return d.outerRadius; | |
} | |
function arcStartAngle(d) { | |
return d.startAngle; | |
} | |
function arcEndAngle(d) { | |
return d.endAngle; | |
} | |
function arcPadAngle(d) { | |
return d && d.padAngle; // Note: optional! | |
} | |
function asin(x) { | |
return x >= 1 ? halfPi : x <= -1 ? -halfPi : Math.asin(x); | |
} | |
function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { | |
var x10 = x1 - x0, y10 = y1 - y0, | |
x32 = x3 - x2, y32 = y3 - y2, | |
t = (x32 * (y0 - y2) - y32 * (x0 - x2)) / (y32 * x10 - x32 * y10); | |
return [x0 + t * x10, y0 + t * y10]; | |
} | |
// Compute perpendicular offset line of length rc. | |
// http://mathworld.wolfram.com/Circle-LineIntersection.html | |
function cornerTangents(x0, y0, x1, y1, r1, rc, cw) { | |
var x01 = x0 - x1, | |
y01 = y0 - y1, | |
lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), | |
ox = lo * y01, | |
oy = -lo * x01, | |
x11 = x0 + ox, | |
y11 = y0 + oy, | |
x10 = x1 + ox, | |
y10 = y1 + oy, | |
x00 = (x11 + x10) / 2, | |
y00 = (y11 + y10) / 2, | |
dx = x10 - x11, | |
dy = y10 - y11, | |
d2 = dx * dx + dy * dy, | |
r = r1 - rc, | |
D = x11 * y10 - x10 * y11, | |
d = (dy < 0 ? -1 : 1) * Math.sqrt(Math.max(0, r * r * d2 - D * D)), | |
cx0 = (D * dy - dx * d) / d2, | |
cy0 = (-D * dx - dy * d) / d2, | |
cx1 = (D * dy + dx * d) / d2, | |
cy1 = (-D * dx + dy * d) / d2, | |
dx0 = cx0 - x00, | |
dy0 = cy0 - y00, | |
dx1 = cx1 - x00, | |
dy1 = cy1 - y00; | |
// Pick the closer of the two intersection points. | |
// TODO Is there a faster way to determine which intersection to use? | |
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; | |
return { | |
cx: cx0, | |
cy: cy0, | |
x01: -ox, | |
y01: -oy, | |
x11: cx0 * (r1 / r - 1), | |
y11: cy0 * (r1 / r - 1) | |
}; | |
} | |
function arc() { | |
var innerRadius = arcInnerRadius, | |
outerRadius = arcOuterRadius, | |
cornerRadius = constant(0), | |
padRadius = null, | |
startAngle = arcStartAngle, | |
endAngle = arcEndAngle, | |
padAngle = arcPadAngle, | |
context = null, | |
output = null; | |
function arc() { | |
var buffer, | |
r, | |
r0 = +innerRadius.apply(this, arguments), | |
r1 = +outerRadius.apply(this, arguments), | |
a0 = startAngle.apply(this, arguments) - halfPi, | |
a1 = endAngle.apply(this, arguments) - halfPi, | |
da = Math.abs(a1 - a0), | |
cw = a1 > a0; | |
if (!context) context = buffer = d3Path.path(); | |
// Ensure that the outer radius is always larger than the inner radius. | |
if (r1 < r0) r = r1, r1 = r0, r0 = r; | |
// Is it a point? | |
if (!(r1 > 0)) context.moveTo(0, 0); | |
// Or is it a circle or annulus? | |
else if (da >= tau) { | |
context.moveTo(r1 * Math.cos(a0), r1 * Math.sin(a0)); | |
context.arc(0, 0, r1, a0, a1, !cw); | |
if (r0 > 0) { | |
context.moveTo(r0 * Math.cos(a1), r0 * Math.sin(a1)); | |
context.arc(0, 0, r0, a1, a0, cw); | |
} | |
} | |
// Or is it a circular or annular sector? | |
else { | |
var a01 = a0, | |
a11 = a1, | |
a00 = a0, | |
a10 = a1, | |
da0 = da, | |
da1 = da, | |
ap = padAngle.apply(this, arguments) / 2, | |
rp = (ap > 0) && (padRadius ? +padRadius.apply(this, arguments) : Math.sqrt(r0 * r0 + r1 * r1)), | |
rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments)), | |
rc0 = rc, | |
rc1 = rc; | |
// Apply padding? Note that since r1 ≥ r0, da1 ≥ da0. | |
if (rp > 0) { | |
var p0 = asin(rp / r0 * Math.sin(ap)), | |
p1 = asin(rp / r1 * Math.sin(ap)); | |
if ((da0 -= p0 * 2) > 0) p0 *= (cw ? 1 : -1), a00 += p0, a10 -= p0; | |
else da0 = 0, a00 = a10 = (a0 + a1) / 2; | |
if ((da1 -= p1 * 2) > 0) p1 *= (cw ? 1 : -1), a01 += p1, a11 -= p1; | |
else da1 = 0, a01 = a11 = (a0 + a1) / 2; | |
} | |
var x01 = r1 * Math.cos(a01), | |
y01 = r1 * Math.sin(a01), | |
x10 = r0 * Math.cos(a10), | |
y10 = r0 * Math.sin(a10); | |
// Apply rounded corners? | |
if (rc > 0) { | |
var x11 = r1 * Math.cos(a11), | |
y11 = r1 * Math.sin(a11), | |
x00 = r0 * Math.cos(a00), | |
y00 = r0 * Math.sin(a00); | |
// Restrict the corner radius according to the sector angle. | |
if (da < pi) { | |
var oc = da0 > 0 ? intersect(x01, y01, x00, y00, x11, y11, x10, y10) : [x10, y10], | |
ax = x01 - oc[0], | |
ay = y01 - oc[1], | |
bx = x11 - oc[0], | |
by = y11 - oc[1], | |
kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), | |
lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); | |
rc0 = Math.min(rc, (r0 - lc) / (kc - 1)); | |
rc1 = Math.min(rc, (r1 - lc) / (kc + 1)); | |
} | |
} | |
// Is the sector collapsed to a line? | |
if (!(da1 > 0)) context.moveTo(x01, y01); | |
// Does the sector’s outer ring have rounded corners? | |
else if (rc1 > 0) { | |
var t0 = cornerTangents(x00, y00, x01, y01, r1, rc1, cw), | |
t1 = cornerTangents(x11, y11, x10, y10, r1, rc1, cw); | |
context.moveTo(t0.cx + t0.x01, t0.cy + t0.y01); | |
// Have the corners merged? | |
if (rc1 < rc) context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t1.y01, t1.x01), !cw); | |
// Otherwise, draw the two corners and the ring. | |
else { | |
context.arc(t0.cx, t0.cy, rc1, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw); | |
context.arc(0, 0, r1, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), !cw); | |
context.arc(t1.cx, t1.cy, rc1, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw); | |
} | |
} | |
// Or is the outer ring just a circular arc? | |
else context.moveTo(x01, y01), context.arc(0, 0, r1, a01, a11, !cw); | |
// Is there no inner ring, and it’s a circular sector? | |
// Or perhaps it’s an annular sector collapsed due to padding? | |
if (!(r0 > 0) || !(da0 > 0)) context.lineTo(x10, y10); | |
// Does the sector’s inner ring (or point) have rounded corners? | |
else if (rc0 > 0) { | |
var t0 = cornerTangents(x10, y10, x11, y11, r0, -rc0, cw), | |
t1 = cornerTangents(x01, y01, x00, y00, r0, -rc0, cw); | |
context.lineTo(t0.cx + t0.x01, t0.cy + t0.y01); | |
// Have the corners merged? | |
if (rc0 < rc) context.arc(t0.cx, t0.cy, rc0, Math.atan2(t0.y01, t0.x01), Math.atan2(t1.y01, t1.x01), !cw); | |
// Otherwise, draw the two corners and the ring. | |
else { | |
context.arc(t0.cx, t0.cy, rc0, Math.atan2(t0.y01, t0.x01), Math.atan2(t0.y11, t0.x11), !cw); | |
context.arc(0, 0, r0, Math.atan2(t0.cy + t0.y11, t0.cx + t0.x11), Math.atan2(t1.cy + t1.y11, t1.cx + t1.x11), cw); | |
context.arc(t1.cx, t1.cy, rc0, Math.atan2(t1.y11, t1.x11), Math.atan2(t1.y01, t1.x01), !cw); | |
} | |
} | |
// Or is the inner ring just a circular arc? | |
else context.arc(0, 0, r0, a10, a00, cw); | |
} | |
context.closePath(); | |
if (buffer) return context = null, buffer + "" || null; | |
} | |
arc.centroid = function() { | |
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, | |
a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - pi / 2; | |
return [Math.cos(a) * r, Math.sin(a) * r]; | |
}; | |
arc.innerRadius = function(_) { | |
return arguments.length ? (innerRadius = typeof _ === "function" ? _ : constant(+_), arc) : innerRadius; | |
}; | |
arc.outerRadius = function(_) { | |
return arguments.length ? (outerRadius = typeof _ === "function" ? _ : constant(+_), arc) : outerRadius; | |
}; | |
arc.cornerRadius = function(_) { | |
return arguments.length ? (cornerRadius = typeof _ === "function" ? _ : constant(+_), arc) : cornerRadius; | |
}; | |
arc.padRadius = function(_) { | |
return arguments.length ? (padRadius = _ == null ? null : typeof _ === "function" ? _ : constant(+_), arc) : padRadius; | |
}; | |
arc.startAngle = function(_) { | |
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant(+_), arc) : startAngle; | |
}; | |
arc.endAngle = function(_) { | |
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant(+_), arc) : endAngle; | |
}; | |
arc.padAngle = function(_) { | |
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant(+_), arc) : padAngle; | |
}; | |
arc.context = function(_) { | |
return arguments.length ? ((context = output = _ == null ? null : _), arc) : context; | |
}; | |
return arc; | |
}; | |
var slice = Array.prototype.slice; | |
function bind(curve, args) { | |
if (args.length < 2) return curve; | |
args = slice.call(args); | |
args[0] = null; | |
return function(context) { | |
args[0] = context; | |
return curve.apply(null, args); | |
}; | |
}; | |
function Linear(context) { | |
this._context = context; | |
} | |
Linear.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; // proceed | |
default: this._context.lineTo(x, y); break; | |
} | |
} | |
}; | |
function curveLinear(context) { | |
return new Linear(context); | |
}; | |
function x(p) { | |
return p[0]; | |
}; | |
function y(p) { | |
return p[1]; | |
}; | |
function area() { | |
var x0 = x, | |
x1 = null, | |
y0 = constant(0), | |
y1 = y, | |
defined = constant(true), | |
context = null, | |
curve = curveLinear, | |
output = null; | |
function area(data) { | |
var i, | |
j, | |
k, | |
n = data.length, | |
d, | |
defined0 = false, | |
buffer, | |
x0z = new Array(n), | |
y0z = new Array(n); | |
if (!context) output = curve(buffer = d3Path.path()); | |
for (i = 0; i <= n; ++i) { | |
if (!(i < n && defined(d = data[i], i, data)) === defined0) { | |
if (defined0 = !defined0) { | |
j = i; | |
output.areaStart(); | |
output.lineStart(); | |
} else { | |
output.lineEnd(); | |
output.lineStart(); | |
for (k = i - 1; k >= j; --k) { | |
output.point(x0z[k], y0z[k]); | |
} | |
output.lineEnd(); | |
output.areaEnd(); | |
} | |
} | |
if (defined0) { | |
x0z[i] = +x0(d, i, data), y0z[i] = +y0(d, i, data); | |
output.point(x1 ? +x1(d, i, data) : x0z[i], y1 ? +y1(d, i, data) : y0z[i]); | |
} | |
} | |
if (buffer) return output = null, buffer + "" || null; | |
} | |
area.x = function(_) { | |
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant(+_), x1 = null, area) : x0; | |
}; | |
area.x0 = function(_) { | |
return arguments.length ? (x0 = typeof _ === "function" ? _ : constant(+_), area) : x0; | |
}; | |
area.x1 = function(_) { | |
return arguments.length ? (x1 = _ == null ? null : typeof _ === "function" ? _ : constant(+_), area) : x1; | |
}; | |
area.y = function(_) { | |
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant(+_), y1 = null, area) : y0; | |
}; | |
area.y0 = function(_) { | |
return arguments.length ? (y0 = typeof _ === "function" ? _ : constant(+_), area) : y0; | |
}; | |
area.y1 = function(_) { | |
return arguments.length ? (y1 = _ == null ? null : typeof _ === "function" ? _ : constant(+_), area) : y1; | |
}; | |
area.defined = function(_) { | |
return arguments.length ? (defined = typeof _ === "function" ? _ : constant(!!_), area) : defined; | |
}; | |
area.curve = function(_) { | |
return arguments.length ? (curve = bind(_, arguments), context != null && (output = curve(context)), area) : curve; | |
}; | |
area.context = function(_) { | |
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), area) : context; | |
}; | |
return area; | |
}; | |
function noop() {}; | |
function point(that, x, y) { | |
that._context.bezierCurveTo( | |
(2 * that._x0 + that._x1) / 3, | |
(2 * that._y0 + that._y1) / 3, | |
(that._x0 + 2 * that._x1) / 3, | |
(that._y0 + 2 * that._y1) / 3, | |
(that._x0 + 4 * that._x1 + x) / 6, | |
(that._y0 + 4 * that._y1 + y) / 6 | |
); | |
}; | |
function Basis(context) { | |
this._context = context; | |
} | |
Basis.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = | |
this._y0 = this._y1 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 3: point(this, this._x1, this._y1); // proceed | |
case 2: this._context.lineTo(this._x1, this._y1); break; | |
} | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; break; | |
case 2: this._point = 3; this._context.lineTo((5 * this._x0 + this._x1) / 6, (5 * this._y0 + this._y1) / 6); // proceed | |
default: point(this, x, y); break; | |
} | |
this._x0 = this._x1, this._x1 = x; | |
this._y0 = this._y1, this._y1 = y; | |
} | |
}; | |
function basis(context) { | |
return new Basis(context); | |
}; | |
function BasisClosed(context) { | |
this._context = context; | |
} | |
BasisClosed.prototype = { | |
areaStart: noop, | |
areaEnd: noop, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = | |
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 1: { | |
this._context.moveTo(this._x2, this._y2); | |
this._context.closePath(); | |
break; | |
} | |
case 2: { | |
this._context.moveTo((this._x2 + 2 * this._x3) / 3, (this._y2 + 2 * this._y3) / 3); | |
this._context.lineTo((this._x3 + 2 * this._x2) / 3, (this._y3 + 2 * this._y2) / 3); | |
this._context.closePath(); | |
break; | |
} | |
case 3: { | |
this.point(this._x2, this._y2); | |
this.point(this._x3, this._y3); | |
this.point(this._x4, this._y4); | |
break; | |
} | |
} | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._x2 = x, this._y2 = y; break; | |
case 1: this._point = 2; this._x3 = x, this._y3 = y; break; | |
case 2: this._point = 3; this._x4 = x, this._y4 = y; this._context.moveTo((this._x0 + 4 * this._x1 + x) / 6, (this._y0 + 4 * this._y1 + y) / 6); break; | |
default: point(this, x, y); break; | |
} | |
this._x0 = this._x1, this._x1 = x; | |
this._y0 = this._y1, this._y1 = y; | |
} | |
}; | |
function basisClosed(context) { | |
return new BasisClosed(context); | |
}; | |
function BasisOpen(context) { | |
this._context = context; | |
} | |
BasisOpen.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = | |
this._y0 = this._y1 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; break; | |
case 1: this._point = 2; break; | |
case 2: this._point = 3; var x0 = (this._x0 + 4 * this._x1 + x) / 6, y0 = (this._y0 + 4 * this._y1 + y) / 6; this._line ? this._context.lineTo(x0, y0) : this._context.moveTo(x0, y0); break; | |
case 3: this._point = 4; // proceed | |
default: point(this, x, y); break; | |
} | |
this._x0 = this._x1, this._x1 = x; | |
this._y0 = this._y1, this._y1 = y; | |
} | |
}; | |
function basisOpen(context) { | |
return new BasisOpen(context); | |
}; | |
function Bundle(context, beta) { | |
this._basis = basis(context); | |
this._beta = beta; | |
} | |
Bundle.prototype = { | |
lineStart: function() { | |
this._x = []; | |
this._y = []; | |
this._basis.lineStart(); | |
}, | |
lineEnd: function() { | |
var x = this._x, | |
y = this._y, | |
j = x.length - 1; | |
if (j > 0) { | |
var x0 = x[0], | |
y0 = y[0], | |
dx = x[j] - x0, | |
dy = y[j] - y0, | |
i = -1, | |
t; | |
while (++i <= j) { | |
t = i / j; | |
this._basis.point( | |
this._beta * x[i] + (1 - this._beta) * (x0 + t * dx), | |
this._beta * y[i] + (1 - this._beta) * (y0 + t * dy) | |
); | |
} | |
} | |
this._x = this._y = null; | |
this._basis.lineEnd(); | |
}, | |
point: function(x, y) { | |
this._x.push(+x); | |
this._y.push(+y); | |
} | |
}; | |
function bundle(context, beta) { | |
return beta == null ? new Bundle(context, 0.85) | |
: (beta = +beta) === 1 ? basis(context) | |
: new Bundle(context, beta); | |
}; | |
function point$1(that, x, y) { | |
that._context.bezierCurveTo( | |
that._x1 + that._k * (that._x2 - that._x0), | |
that._y1 + that._k * (that._y2 - that._y0), | |
that._x2 + that._k * (that._x1 - x), | |
that._y2 + that._k * (that._y1 - y), | |
that._x2, | |
that._y2 | |
); | |
}; | |
function Cardinal(context, k) { | |
this._context = context; | |
this._k = k; | |
} | |
Cardinal.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = | |
this._y0 = this._y1 = this._y2 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 2: this._context.lineTo(this._x2, this._y2); break; | |
case 3: point$1(this, this._x1, this._y1); break; | |
} | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; this._x1 = x, this._y1 = y; break; | |
case 2: this._point = 3; // proceed | |
default: point$1(this, x, y); break; | |
} | |
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; | |
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; | |
} | |
}; | |
function cardinal(context, tension) { | |
return new Cardinal(context, (tension == null ? 1 : 1 - tension) / 6); | |
}; | |
function CardinalClosed(context, k) { | |
this._context = context; | |
this._k = k; | |
} | |
CardinalClosed.prototype = { | |
areaStart: noop, | |
areaEnd: noop, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = | |
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 1: { | |
this._context.moveTo(this._x3, this._y3); | |
this._context.closePath(); | |
break; | |
} | |
case 2: { | |
this._context.lineTo(this._x3, this._y3); | |
this._context.closePath(); | |
break; | |
} | |
case 3: { | |
this.point(this._x3, this._y3); | |
this.point(this._x4, this._y4); | |
this.point(this._x5, this._y5); | |
break; | |
} | |
} | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._x3 = x, this._y3 = y; break; | |
case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; | |
case 2: this._point = 3; this._x5 = x, this._y5 = y; break; | |
default: point$1(this, x, y); break; | |
} | |
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; | |
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; | |
} | |
}; | |
function cardinalClosed(context, tension) { | |
return new CardinalClosed(context, (tension == null ? 1 : 1 - tension) / 6); | |
}; | |
function CardinalOpen(context, k) { | |
this._context = context; | |
this._k = k; | |
} | |
CardinalOpen.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = | |
this._y0 = this._y1 = this._y2 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; break; | |
case 1: this._point = 2; break; | |
case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; | |
case 3: this._point = 4; // proceed | |
default: point$1(this, x, y); break; | |
} | |
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; | |
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; | |
} | |
}; | |
function cardinalOpen(context, tension) { | |
return new CardinalOpen(context, (tension == null ? 1 : 1 - tension) / 6); | |
}; | |
var epsilon = 1e-6; | |
function point$2(that, x, y) { | |
var x1 = that._x1, | |
y1 = that._y1, | |
x2 = that._x2, | |
y2 = that._y2; | |
if (that._l01_a > epsilon) { | |
var a = 2 * that._l01_2a + 3 * that._l01_a * that._l12_a + that._l12_2a, | |
n = 3 * that._l01_a * (that._l01_a + that._l12_a); | |
x1 = (x1 * a - that._x0 * that._l12_2a + that._x2 * that._l01_2a) / n; | |
y1 = (y1 * a - that._y0 * that._l12_2a + that._y2 * that._l01_2a) / n; | |
} | |
if (that._l23_a > epsilon) { | |
var b = 2 * that._l23_2a + 3 * that._l23_a * that._l12_a + that._l12_2a, | |
m = 3 * that._l23_a * (that._l23_a + that._l12_a); | |
x2 = (x2 * b + that._x1 * that._l23_2a - x * that._l12_2a) / m; | |
y2 = (y2 * b + that._y1 * that._l23_2a - y * that._l12_2a) / m; | |
} | |
that._context.bezierCurveTo(x1, y1, x2, y2, that._x2, that._y2); | |
}; | |
function CatmullRom(context, alpha) { | |
this._context = context; | |
this._alpha = alpha; | |
} | |
CatmullRom.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = | |
this._y0 = this._y1 = this._y2 = NaN; | |
this._l01_a = this._l12_a = this._l23_a = | |
this._l01_2a = this._l12_2a = this._l23_2a = | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 2: this._context.lineTo(this._x2, this._y2); break; | |
case 3: this.point(this, this._x2, this._y2); break; | |
} | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
if (this._point) { | |
var x23 = this._x2 - x, | |
y23 = this._y2 - y; | |
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); | |
} | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; break; | |
case 2: this._point = 3; // proceed | |
default: point$2(this, x, y); break; | |
} | |
this._l01_a = this._l12_a, this._l12_a = this._l23_a; | |
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; | |
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; | |
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; | |
} | |
}; | |
function catmullRom(context, alpha) { | |
return (alpha = alpha == null ? 0.5 : +alpha) | |
? new CatmullRom(context, alpha) | |
: cardinal(context, 0); | |
}; | |
function CatmullRomClosed(context, alpha) { | |
this._context = context; | |
this._alpha = alpha; | |
} | |
CatmullRomClosed.prototype = { | |
areaStart: noop, | |
areaEnd: noop, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = this._x3 = this._x4 = this._x5 = | |
this._y0 = this._y1 = this._y2 = this._y3 = this._y4 = this._y5 = NaN; | |
this._l01_a = this._l12_a = this._l23_a = | |
this._l01_2a = this._l12_2a = this._l23_2a = | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 1: { | |
this._context.moveTo(this._x3, this._y3); | |
this._context.closePath(); | |
break; | |
} | |
case 2: { | |
this._context.lineTo(this._x3, this._y3); | |
this._context.closePath(); | |
break; | |
} | |
case 3: { | |
this.point(this._x3, this._y3); | |
this.point(this._x4, this._y4); | |
this.point(this._x5, this._y5); | |
break; | |
} | |
} | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
if (this._point) { | |
var x23 = this._x2 - x, | |
y23 = this._y2 - y; | |
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); | |
} | |
switch (this._point) { | |
case 0: this._point = 1; this._x3 = x, this._y3 = y; break; | |
case 1: this._point = 2; this._context.moveTo(this._x4 = x, this._y4 = y); break; | |
case 2: this._point = 3; this._x5 = x, this._y5 = y; break; | |
default: point$2(this, x, y); break; | |
} | |
this._l01_a = this._l12_a, this._l12_a = this._l23_a; | |
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; | |
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; | |
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; | |
} | |
}; | |
function catmullRomClosed(context, alpha) { | |
return (alpha = alpha == null ? 0.5 : +alpha) | |
? new CatmullRomClosed(context, alpha) | |
: cardinalClosed(context, 0); | |
}; | |
function CatmullRomOpen(context, alpha) { | |
this._context = context; | |
this._alpha = alpha; | |
} | |
CatmullRomOpen.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = this._x2 = | |
this._y0 = this._y1 = this._y2 = NaN; | |
this._l01_a = this._l12_a = this._l23_a = | |
this._l01_2a = this._l12_2a = this._l23_2a = | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._line || (this._line !== 0 && this._point === 3)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
if (this._point) { | |
var x23 = this._x2 - x, | |
y23 = this._y2 - y; | |
this._l23_a = Math.sqrt(this._l23_2a = Math.pow(x23 * x23 + y23 * y23, this._alpha)); | |
} | |
switch (this._point) { | |
case 0: this._point = 1; break; | |
case 1: this._point = 2; break; | |
case 2: this._point = 3; this._line ? this._context.lineTo(this._x2, this._y2) : this._context.moveTo(this._x2, this._y2); break; | |
case 3: this._point = 4; // proceed | |
default: point$2(this, x, y); break; | |
} | |
this._l01_a = this._l12_a, this._l12_a = this._l23_a; | |
this._l01_2a = this._l12_2a, this._l12_2a = this._l23_2a; | |
this._x0 = this._x1, this._x1 = this._x2, this._x2 = x; | |
this._y0 = this._y1, this._y1 = this._y2, this._y2 = y; | |
} | |
}; | |
function catmullRomOpen(context, alpha) { | |
return (alpha = alpha == null ? 0.5 : +alpha) | |
? new CatmullRomOpen(context, alpha) | |
: cardinalOpen(context, 0); | |
}; | |
var circle = { | |
draw: function(context, size) { | |
var r = Math.sqrt(size / pi); | |
context.moveTo(r, 0); | |
context.arc(0, 0, r, 0, tau); | |
} | |
}; | |
var cross = { | |
draw: function(context, size) { | |
var r = Math.sqrt(size / 5) / 2; | |
context.moveTo(-3 * r, -r); | |
context.lineTo(-r, -r); | |
context.lineTo(-r, -3 * r); | |
context.lineTo(r, -3 * r); | |
context.lineTo(r, -r); | |
context.lineTo(3 * r, -r); | |
context.lineTo(3 * r, r); | |
context.lineTo(r, r); | |
context.lineTo(r, 3 * r); | |
context.lineTo(-r, 3 * r); | |
context.lineTo(-r, r); | |
context.lineTo(-3 * r, r); | |
context.closePath(); | |
} | |
}; | |
var tan30 = Math.sqrt(1 / 3); | |
var tan30_2 = tan30 * 2; | |
var diamond = { | |
draw: function(context, size) { | |
var y = Math.sqrt(size / tan30_2), | |
x = y * tan30; | |
context.moveTo(0, -y); | |
context.lineTo(x, 0); | |
context.lineTo(0, y); | |
context.lineTo(-x, 0); | |
context.closePath(); | |
} | |
}; | |
function LinearClosed(context) { | |
this._context = context; | |
} | |
LinearClosed.prototype = { | |
areaStart: noop, | |
areaEnd: noop, | |
lineStart: function() { | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._point) this._context.closePath(); | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
if (this._point) this._context.lineTo(x, y); | |
else this._point = 1, this._context.moveTo(x, y); | |
} | |
}; | |
function linearClosed(context) { | |
return new LinearClosed(context); | |
}; | |
function line() { | |
var x$$ = x, | |
y$$ = y, | |
defined = constant(true), | |
context = null, | |
curve = curveLinear, | |
output = null; | |
function line(data) { | |
var i, | |
n = data.length, | |
d, | |
defined0 = false, | |
buffer; | |
if (!context) output = curve(buffer = d3Path.path()); | |
for (i = 0; i <= n; ++i) { | |
if (!(i < n && defined(d = data[i], i, data)) === defined0) { | |
if (defined0 = !defined0) output.lineStart(); | |
else output.lineEnd(); | |
} | |
if (defined0) output.point(+x$$(d, i, data), +y$$(d, i, data)); | |
} | |
if (buffer) return output = null, buffer + "" || null; | |
} | |
line.x = function(_) { | |
return arguments.length ? (x$$ = typeof _ === "function" ? _ : constant(+_), line) : x$$; | |
}; | |
line.y = function(_) { | |
return arguments.length ? (y$$ = typeof _ === "function" ? _ : constant(+_), line) : y$$; | |
}; | |
line.defined = function(_) { | |
return arguments.length ? (defined = typeof _ === "function" ? _ : constant(!!_), line) : defined; | |
}; | |
line.curve = function(_) { | |
return arguments.length ? (curve = bind(_, arguments), context != null && (output = curve(context)), line) : curve; | |
}; | |
line.context = function(_) { | |
return arguments.length ? (_ == null ? context = output = null : output = curve(context = _), line) : context; | |
}; | |
return line; | |
}; | |
function sign(x) { | |
return x < 0 ? -1 : 1; | |
} | |
// Calculate the slopes of the tangents (Hermite-type interpolation) based on | |
// the following paper: Steffen, M. 1990. A Simple Method for Monotonic | |
// Interpolation in One Dimension. Astronomy and Astrophysics, Vol. 239, NO. | |
// NOV(II), P. 443, 1990. | |
function slope3(that, x2, y2) { | |
var h0 = that._x1 - that._x0, | |
h1 = x2 - that._x1, | |
s0 = (that._y1 - that._y0) / h0, | |
s1 = (y2 - that._y1) / h1, | |
p = (s0 * h1 + s1 * h0) / (h0 + h1); | |
return (sign(s0) + sign(s1)) * Math.min(Math.abs(s0), Math.abs(s1), 0.5 * Math.abs(p)) || 0; | |
} | |
// Calculate a one-sided slope. | |
function slope2(that, t) { | |
var h = that._x1 - that._x0; | |
return h ? (3 * (that._y1 - that._y0) / h - t) / 2 : t; | |
} | |
// According to https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Representations | |
// "you can express cubic Hermite interpolation in terms of cubic Bézier curves | |
// with respect to the four values p0, p0 + m0 / 3, p1 - m1 / 3, p1". | |
function point$3(that, t0, t1) { | |
var x0 = that._x0, | |
y0 = that._y0, | |
x1 = that._x1, | |
y1 = that._y1, | |
dx = (x1 - x0) / 3; | |
that._context.bezierCurveTo(x0 + dx, y0 + dx * t0, x1 - dx, y1 - dx * t1, x1, y1); | |
} | |
function Monotone(context) { | |
this._context = context; | |
} | |
Monotone.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x0 = this._x1 = | |
this._y0 = this._y1 = | |
this._t0 = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
switch (this._point) { | |
case 2: this._context.lineTo(this._x1, this._y1); break; | |
case 3: point$3(this, this._t0, slope2(this, this._t0)); break; | |
} | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
var t1 = NaN; | |
x = +x, y = +y; | |
if (x === this._x1 && y === this._y1) return; // Ignore coincident points. | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; break; | |
case 2: this._point = 3; point$3(this, slope2(this, t1 = slope3(this, x, y)), t1); break; | |
default: point$3(this, this._t0, t1 = slope3(this, x, y)); break; | |
} | |
this._x0 = this._x1, this._x1 = x; | |
this._y0 = this._y1, this._y1 = y; | |
this._t0 = t1; | |
} | |
} | |
function monotone(context) { | |
return new Monotone(context); | |
}; | |
function Natural(context) { | |
this._context = context; | |
} | |
Natural.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x = []; | |
this._y = []; | |
}, | |
lineEnd: function() { | |
var x = this._x, | |
y = this._y, | |
n = x.length; | |
if (n) { | |
this._line ? this._context.lineTo(x[0], y[0]) : this._context.moveTo(x[0], y[0]); | |
if (n === 2) { | |
this._context.lineTo(x[1], y[1]); | |
} else { | |
var px = controlPoints(x), | |
py = controlPoints(y); | |
for (var i0 = 0, i1 = 1; i1 < n; ++i0, ++i1) { | |
this._context.bezierCurveTo(px[0][i0], py[0][i0], px[1][i0], py[1][i0], x[i1], y[i1]); | |
} | |
} | |
} | |
if (this._line || (this._line !== 0 && n === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
this._x = this._y = null; | |
}, | |
point: function(x, y) { | |
this._x.push(+x); | |
this._y.push(+y); | |
} | |
}; | |
// See https://www.particleincell.com/2012/bezier-splines/ for derivation. | |
function controlPoints(x) { | |
var i, | |
n = x.length - 1, | |
m, | |
a = new Array(n), | |
b = new Array(n), | |
r = new Array(n); | |
a[0] = 0, b[0] = 2, r[0] = x[0] + 2 * x[1]; | |
for (i = 1; i < n - 1; ++i) a[i] = 1, b[i] = 4, r[i] = 4 * x[i] + 2 * x[i + 1]; | |
a[n - 1] = 2, b[n - 1] = 7, r[n - 1] = 8 * x[n - 1] + x[n]; | |
for (i = 1; i < n; ++i) m = a[i] / b[i - 1], b[i] -= m, r[i] -= m * r[i - 1]; | |
a[n - 1] = r[n - 1] / b[n - 1]; | |
for (i = n - 2; i >= 0; --i) a[i] = (r[i] - a[i + 1]) / b[i]; | |
b[n - 1] = (x[n] + a[n - 1]) / 2; | |
for (i = 0; i < n - 1; ++i) b[i] = 2 * x[i + 1] - a[i + 1]; | |
return [a, b]; | |
} | |
function natural(context) { | |
return new Natural(context); | |
}; | |
function descending(a, b) { | |
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; | |
}; | |
function identity(d) { | |
return d; | |
}; | |
function pie() { | |
var value = identity, | |
sortValues = descending, | |
sort = null, | |
startAngle = constant(0), | |
endAngle = constant(tau), | |
padAngle = constant(0); | |
function pie(data) { | |
var n = data.length, | |
sum = 0, | |
index = new Array(n), | |
arcs = new Array(n), | |
a0 = +startAngle.apply(this, arguments), | |
da = Math.min(tau, Math.max(-tau, endAngle.apply(this, arguments) - a0)), | |
a1, | |
p = Math.min(Math.abs(da) / n, padAngle.apply(this, arguments)), | |
pa = p * (da < 0 ? -1 : 1); | |
for (var i = 0, v; i < n; ++i) { | |
if ((v = arcs[index[i] = i] = +value(data[i], i, data)) > 0) { | |
sum += v; | |
} | |
} | |
// Optionally sort the arcs by previously-computed values or by data. | |
if (sortValues != null) index.sort(function(i, j) { return sortValues(arcs[i], arcs[j]); }); | |
else if (sort !== null) index.sort(function(i, j) { return sort(data[i], data[j]); }); | |
// Compute the arcs! They are stored in the original data's order. | |
for (var i = 0, j, k = sum ? (da - n * pa) / sum : 0; i < n; ++i, a0 = a1) { | |
j = index[i], v = arcs[j], a1 = a0 + (v > 0 ? v * k : 0) + pa, arcs[j] = { | |
data: data[j], | |
value: v, | |
startAngle: a0, | |
endAngle: a1, | |
padAngle: p | |
}; | |
} | |
return arcs; | |
} | |
pie.value = function(_) { | |
return arguments.length ? (value = typeof _ === "function" ? _ : constant(+_), pie) : value; | |
}; | |
pie.sortValues = function(_) { | |
return arguments.length ? (sortValues = _, sort = null, pie) : sortValues; | |
}; | |
pie.sort = function(_) { | |
return arguments.length ? (sort = _, sortValues = null, pie) : sort; | |
}; | |
pie.startAngle = function(_) { | |
return arguments.length ? (startAngle = typeof _ === "function" ? _ : constant(+_), pie) : startAngle; | |
}; | |
pie.endAngle = function(_) { | |
return arguments.length ? (endAngle = typeof _ === "function" ? _ : constant(+_), pie) : endAngle; | |
}; | |
pie.padAngle = function(_) { | |
return arguments.length ? (padAngle = typeof _ === "function" ? _ : constant(+_), pie) : padAngle; | |
}; | |
return pie; | |
}; | |
function Radial(curve) { | |
this._curve = curve; | |
} | |
Radial.prototype = { | |
areaStart: function() { | |
this._curve.areaStart(); | |
}, | |
areaEnd: function() { | |
this._curve.areaEnd(); | |
}, | |
lineStart: function() { | |
this._curve.lineStart(); | |
}, | |
lineEnd: function() { | |
this._curve.lineEnd(); | |
}, | |
point: function(a, r) { | |
a -= halfPi, this._curve.point(r * Math.cos(a), r * Math.sin(a)); | |
} | |
}; | |
function curveRadial(curve, args) { | |
curve = bind(curve, args); | |
function radial(context) { | |
return new Radial(curve(context)); | |
} | |
radial._curve = curve; | |
return radial; | |
}; | |
function radialArea() { | |
var a = area(), | |
c = a.curve; | |
a.angle = a.x, delete a.x; | |
a.startAngle = a.x0, delete a.x0; | |
a.endAngle = a.x1, delete a.x1; | |
a.radius = a.y, delete a.y; | |
a.innerRadius = a.y0, delete a.y0; | |
a.outerRadius = a.y1, delete a.y1; | |
a.curve = function(_) { | |
return arguments.length ? c(curveRadial(_, arguments)) : c()._curve; | |
}; | |
return a.curve(curveLinear); | |
}; | |
function radialLine() { | |
var l = line(), | |
c = l.curve; | |
l.angle = l.x, delete l.x; | |
l.radius = l.y, delete l.y; | |
l.curve = function(_) { | |
return arguments.length ? c(curveRadial(_, arguments)) : c()._curve; | |
}; | |
return l.curve(curveLinear); | |
}; | |
var square = { | |
draw: function(context, size) { | |
var w = Math.sqrt(size), | |
x = -w / 2; | |
context.rect(x, x, w, w); | |
} | |
}; | |
var ka = 0.89081309152928522810; | |
var kr = Math.sin(pi / 10) / Math.sin(7 * pi / 10); | |
var kx = Math.sin(tau / 10) * kr; | |
var ky = -Math.cos(tau / 10) * kr; | |
var star = { | |
draw: function(context, size) { | |
var r = Math.sqrt(size * ka), | |
x = kx * r, | |
y = ky * r; | |
context.moveTo(0, -r); | |
context.lineTo(x, y); | |
for (var i = 1; i < 5; ++i) { | |
var a = tau * i / 5, | |
c = Math.cos(a), | |
s = Math.sin(a); | |
context.lineTo(s * r, -c * r); | |
context.lineTo(c * x - s * y, s * x + c * y); | |
} | |
context.closePath(); | |
} | |
}; | |
function StepAfter(context) { | |
this._context = context; | |
} | |
StepAfter.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._y = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; // proceed | |
default: { | |
this._context.lineTo(x, this._y); | |
this._context.lineTo(x, y); | |
break; | |
} | |
} | |
this._y = y; | |
} | |
}; | |
function stepAfter(context) { | |
return new StepAfter(context); | |
}; | |
function StepBefore(context) { | |
this._context = context; | |
} | |
StepBefore.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; // proceed | |
default: { | |
this._context.lineTo(this._x, y); | |
this._context.lineTo(x, y); | |
break; | |
} | |
} | |
this._x = x; | |
} | |
}; | |
function stepBefore(context) { | |
return new StepBefore(context); | |
}; | |
function Step(context) { | |
this._context = context; | |
} | |
Step.prototype = { | |
areaStart: function() { | |
this._line = 0; | |
}, | |
areaEnd: function() { | |
this._line = NaN; | |
}, | |
lineStart: function() { | |
this._x = this._y = NaN; | |
this._point = 0; | |
}, | |
lineEnd: function() { | |
if (this._point === 2) this._context.lineTo(this._x, this._y); | |
if (this._line || (this._line !== 0 && this._point === 1)) this._context.closePath(); | |
this._line = 1 - this._line; | |
}, | |
point: function(x, y) { | |
x = +x, y = +y; | |
switch (this._point) { | |
case 0: this._point = 1; this._line ? this._context.lineTo(x, y) : this._context.moveTo(x, y); break; | |
case 1: this._point = 2; // proceed | |
default: { | |
var x1 = (this._x + x) / 2; | |
this._context.lineTo(x1, this._y); | |
this._context.lineTo(x1, y); | |
break; | |
} | |
} | |
this._x = x, this._y = y; | |
} | |
}; | |
function step(context) { | |
return new Step(context); | |
}; | |
var c = -0.5; | |
var s = Math.sqrt(3) / 2; | |
var k = 1 / Math.sqrt(12); | |
var a = (k / 2 + 1) * 3; | |
var wye = { | |
draw: function(context, size) { | |
var r = Math.sqrt(size / a), | |
x0 = r / 2, | |
y0 = r * k, | |
x1 = x0, | |
y1 = r * k + r, | |
x2 = -x1, | |
y2 = y1; | |
context.moveTo(x0, y0); | |
context.lineTo(x1, y1); | |
context.lineTo(x2, y2); | |
context.lineTo(c * x0 - s * y0, s * x0 + c * y0); | |
context.lineTo(c * x1 - s * y1, s * x1 + c * y1); | |
context.lineTo(c * x2 - s * y2, s * x2 + c * y2); | |
context.lineTo(c * x0 + s * y0, c * y0 - s * x0); | |
context.lineTo(c * x1 + s * y1, c * y1 - s * x1); | |
context.lineTo(c * x2 + s * y2, c * y2 - s * x2); | |
context.closePath(); | |
} | |
}; | |
var sqrt3 = Math.sqrt(3); | |
var triangle = { | |
draw: function(context, size) { | |
var y = -Math.sqrt(size / (sqrt3 * 3)); | |
context.moveTo(0, y * 2); | |
context.lineTo(-sqrt3 * y, -y); | |
context.lineTo(sqrt3 * y, -y); | |
context.closePath(); | |
} | |
}; | |
var symbols = [ | |
circle, | |
cross, | |
diamond, | |
square, | |
star, | |
triangle, | |
wye | |
]; | |
function symbol() { | |
var type = constant(circle), | |
size = constant(64), | |
context = null; | |
function symbol() { | |
var buffer; | |
if (!context) context = buffer = d3Path.path(); | |
type.apply(this, arguments).draw(context, +size.apply(this, arguments)); | |
if (buffer) return context = null, buffer + "" || null; | |
} | |
symbol.type = function(_) { | |
return arguments.length ? (type = typeof _ === "function" ? _ : constant(_), symbol) : type; | |
}; | |
symbol.size = function(_) { | |
return arguments.length ? (size = typeof _ === "function" ? _ : constant(+_), symbol) : size; | |
}; | |
symbol.context = function(_) { | |
return arguments.length ? (context = _ == null ? null : _, symbol) : context; | |
}; | |
return symbol; | |
}; | |
var version = "0.2.0"; | |
exports.version = version; | |
exports.arc = arc; | |
exports.area = area; | |
exports.basisClosed = basisClosed; | |
exports.basisOpen = basisOpen; | |
exports.basis = basis; | |
exports.bundle = bundle; | |
exports.cardinalClosed = cardinalClosed; | |
exports.cardinalOpen = cardinalOpen; | |
exports.cardinal = cardinal; | |
exports.catmullRomClosed = catmullRomClosed; | |
exports.catmullRomOpen = catmullRomOpen; | |
exports.catmullRom = catmullRom; | |
exports.circle = circle; | |
exports.cross = cross; | |
exports.diamond = diamond; | |
exports.linearClosed = linearClosed; | |
exports.linear = curveLinear; | |
exports.line = line; | |
exports.monotone = monotone; | |
exports.natural = natural; | |
exports.pie = pie; | |
exports.radialArea = radialArea; | |
exports.radialLine = radialLine; | |
exports.square = square; | |
exports.star = star; | |
exports.stepAfter = stepAfter; | |
exports.stepBefore = stepBefore; | |
exports.step = step; | |
exports.symbol = symbol; | |
exports.symbols = symbols; | |
exports.triangle = triangle; | |
exports.wye = wye; | |
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
MARGIN = 20 | |
MARGIN_LEFT = 20 | |
MARGIN_BOTTOM = 40 | |
keyword = 'cocaine' | |
forum = 'drugsforum' | |
# FIXME se la API li cambiano, devono restituire i nuovi valori. Se non lo dicono il valore di end dev'essere una tacca più avanti, altrimenti manca l'ultimo valore. | |
#START = 915148800 #1999 | |
START = 946598400 #2000 | |
#START = 1041292800 #2003 | |
END = 1450310400 | |
#FIXME lo step non può essere fisso in secondi, ci vuole un'aggregazione per mesi o settimane veri | |
STEP = 604800 # 1 week | |
#STEP = 2592000 # 30 days | |
svg = d3.select('svg') | |
width = svg.node().getBoundingClientRect().width | |
height = svg.node().getBoundingClientRect().height | |
chart_width = width-2*MARGIN-MARGIN_LEFT | |
chart_height = height-2*MARGIN-MARGIN_BOTTOM | |
chart = svg.append 'g' | |
.attr | |
transform: "translate(#{MARGIN+MARGIN_LEFT} #{MARGIN})" | |
x = d3.time.scale() | |
.range([0, chart_width]) | |
y = d3.scale.linear() | |
.range([chart_height, 0]) | |
line_generator = d3_shape.area() | |
.x (d) -> x(d.date) | |
.y1 (d) -> y(d.value) | |
.y0 (d) -> y(0) | |
.curve d3_shape.stepAfter # step interpolator is the only one that can be applied after wrapping and wrap without error | |
redraw = () -> | |
d3.select('body').classed 'wait', true | |
d3.json "http://wafi.iit.cnr.it/cas-scraper2/api/timeseries/getTermTs.php?db=#{forum}&term=#{keyword}&start=#{START}&end=#{END}&step=#{STEP}", (result) -> | |
d3.select('body').classed 'wait', false | |
start = new Date(START*1000) | |
end = new Date(END*1000+(STEP*1000)) | |
index = {} | |
result.data.forEach (d) -> | |
d.date = 1000*d.date | |
index[d.date] = d | |
data = d3.range(+start, +end, STEP*1000).map (k) -> | |
if k of index | |
return index[k] | |
else | |
return {date: new Date(k), value: 0} | |
data.forEach (d) -> | |
d.date = new Date(d.date) | |
x | |
.domain([start, end]) | |
y | |
.domain([0, d3.max(data, (d) -> d.value)]) | |
# add a fake data point to extend the stepped curve to include the last value | |
data.push {date: end, value: null} | |
# define the x axis | |
xAxis = d3.svg.axis() | |
.orient("bottom") | |
.ticks(d3.time.year, 1) # FIXME non si sa se d3 posiziona i tick secondo l'ora locale o secondo l'ora UTC. E se scrive considerando l'ora giusta o no. | |
.scale(x) | |
# define the y axis | |
yAxis = d3.svg.axis() | |
.orient("left") | |
# .ticks(10) | |
.scale(y) | |
chart.selectAll('*') | |
.remove() | |
# draw x axis with labels and move to the bottom of the chart area | |
chart.append("g") | |
.attr "class", "xaxis" | |
.attr "transform", "translate(0,#{chart_height+0.5})" | |
.call(xAxis) | |
# draw y axis with labels and move to the bottom of the chart area | |
chart.append("g") | |
.attr "class", "yaxis" | |
.call(yAxis) | |
chart.append('path') | |
.datum data | |
.attr | |
class: 'area' | |
d: line_generator | |
fill: 'orange' | |
d3.select("#keyword").node().value = keyword | |
d3.select("#keyword").on 'keyup', () -> | |
if(d3.event.keyCode == 13) | |
keyword = this.value | |
.replace(/ and /gi, "%2BAND%2B") # URL encoding of queries | |
.replace(/ or /gi, "%2BOR%2B") | |
.replace(/-/g, "%2D") | |
.replace(/ /g, "%2B") | |
redraw() | |
d3.select "#forum_ctrl" | |
.on "change", () -> | |
forum = this.options[this.selectedIndex].value | |
redraw() | |
# d3.select "#date_start" | |
# .on "change", () -> | |
# START = +new Date(this.value)/1000 | |
# console.log START | |
# redraw() | |
# d3.select "#date_end" | |
# .on "change", () -> | |
# END = +new Date(this.value)/1000 | |
# console.log END | |
# redraw() | |
redraw() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body, html { | |
padding: 0; | |
margin: 0; | |
width: 100%; | |
height: 100%; | |
font-family: sans-serif; | |
font-size: 12px; | |
} | |
body.wait { | |
cursor: progress; | |
} | |
svg { | |
width: 100%; | |
height: 100%; | |
background: white; | |
} | |
.tick line { | |
stroke: black; | |
stroke-width: 1px; | |
} | |
.xaxis path, .axis line, .yaxis path { | |
stroke-width:1px; | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
input{ | |
padding : 0 2px; | |
margin : 0; | |
width : 240px; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Term frequencies (Cassandra)</title> | |
<link rel="stylesheet" href="index.css"> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="d3-path.js"></script> | |
<script src="d3-shape.js"></script> | |
</head> | |
<body> | |
<div> | |
<label>Select source:</label> | |
<select id="forum_ctrl"> | |
<option value="drugsforum">Drugs-forum</option> | |
<option value="bluelight">Bluelight</option> | |
<option value="tweets_NPS">Tweets</option> | |
</select> | |
</div> | |
<div id="search"> | |
<label>Keyword:</label> | |
<input type="search" id="keyword"> | |
</div> | |
<svg></svg> | |
<script src="index.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Generated by CoffeeScript 1.10.0 | |
(function() { | |
var END, MARGIN, MARGIN_BOTTOM, MARGIN_LEFT, START, STEP, chart, chart_height, chart_width, forum, height, keyword, line_generator, redraw, svg, width, x, y; | |
MARGIN = 20; | |
MARGIN_LEFT = 20; | |
MARGIN_BOTTOM = 40; | |
keyword = 'cocaine'; | |
forum = 'drugsforum'; | |
START = 946598400; | |
END = 1450310400; | |
STEP = 604800; | |
svg = d3.select('svg'); | |
width = svg.node().getBoundingClientRect().width; | |
height = svg.node().getBoundingClientRect().height; | |
chart_width = width - 2 * MARGIN - MARGIN_LEFT; | |
chart_height = height - 2 * MARGIN - MARGIN_BOTTOM; | |
chart = svg.append('g').attr({ | |
transform: "translate(" + (MARGIN + MARGIN_LEFT) + " " + MARGIN + ")" | |
}); | |
x = d3.time.scale().range([0, chart_width]); | |
y = d3.scale.linear().range([chart_height, 0]); | |
line_generator = d3_shape.area().x(function(d) { | |
return x(d.date); | |
}).y1(function(d) { | |
return y(d.value); | |
}).y0(function(d) { | |
return y(0); | |
}).curve(d3_shape.stepAfter); | |
redraw = function() { | |
d3.select('body').classed('wait', true); | |
return d3.json("http://wafi.iit.cnr.it/cas-scraper2/api/timeseries/getTermTs.php?db=" + forum + "&term=" + keyword + "&start=" + START + "&end=" + END + "&step=" + STEP, function(result) { | |
var data, end, index, start, xAxis, yAxis; | |
d3.select('body').classed('wait', false); | |
start = new Date(START * 1000); | |
end = new Date(END * 1000 + (STEP * 1000)); | |
index = {}; | |
result.data.forEach(function(d) { | |
d.date = 1000 * d.date; | |
return index[d.date] = d; | |
}); | |
data = d3.range(+start, +end, STEP * 1000).map(function(k) { | |
if (k in index) { | |
return index[k]; | |
} else { | |
return { | |
date: new Date(k), | |
value: 0 | |
}; | |
} | |
}); | |
data.forEach(function(d) { | |
return d.date = new Date(d.date); | |
}); | |
x.domain([start, end]); | |
y.domain([ | |
0, d3.max(data, function(d) { | |
return d.value; | |
}) | |
]); | |
data.push({ | |
date: end, | |
value: null | |
}); | |
xAxis = d3.svg.axis().orient("bottom").ticks(d3.time.year, 1).scale(x); | |
yAxis = d3.svg.axis().orient("left").scale(y); | |
chart.selectAll('*').remove(); | |
chart.append("g").attr("class", "xaxis").attr("transform", "translate(0," + (chart_height + 0.5) + ")").call(xAxis); | |
chart.append("g").attr("class", "yaxis").call(yAxis); | |
return chart.append('path').datum(data).attr({ | |
"class": 'area', | |
d: line_generator, | |
fill: 'orange' | |
}); | |
}); | |
}; | |
d3.select("#keyword").node().value = keyword; | |
d3.select("#keyword").on('keyup', function() { | |
if (d3.event.keyCode === 13) { | |
keyword = this.value.replace(/ and /gi, "%2BAND%2B").replace(/ or /gi, "%2BOR%2B").replace(/-/g, "%2D").replace(/ /g, "%2B"); | |
return redraw(); | |
} | |
}); | |
d3.select("#forum_ctrl").on("change", function() { | |
forum = this.options[this.selectedIndex].value; | |
return redraw(); | |
}); | |
redraw(); | |
}).call(this); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"data": [ | |
{ | |
"date": 1042156800, | |
"value": 0 | |
}, | |
{ | |
"date": 1044748800, | |
"value": 50 | |
}, | |
{ | |
"date": 1047340800, | |
"value": 100 | |
}, | |
{ | |
"date": 1049932800, | |
"value": 150 | |
}, | |
{ | |
"date": 1052524800, | |
"value": 200 | |
}, | |
{ | |
"date": 1055116800, | |
"value": 250 | |
}, | |
{ | |
"date": 1057708800, | |
"value": 300 | |
}, | |
{ | |
"date": 1060300800, | |
"value": 350 | |
}, | |
{ | |
"date": 1062892800, | |
"value": 375 | |
}, | |
{ | |
"date": 1065484800, | |
"value": 425 | |
}, | |
{ | |
"date": 1068076800, | |
"value": 1000 | |
}, | |
{ | |
"date": 1070668800, | |
"value": 1025 | |
}, | |
{ | |
"date": 1073260800, | |
"value": 750 | |
}, | |
{ | |
"date": 1075852800, | |
"value": 0 | |
}, | |
{ | |
"date": 1078444800, | |
"value": 50 | |
}, | |
{ | |
"date": 1081036800, | |
"value": 675 | |
}, | |
{ | |
"date": 1083628800, | |
"value": 50 | |
}, | |
{ | |
"date": 1086220800, | |
"value": 150 | |
}, | |
{ | |
"date": 1088812800, | |
"value": 50 | |
}], | |
"tot": 896311 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment