Permalink
Browse files

Merge branch 'release'

  • Loading branch information...
2 parents 98ef2d1 + f73cd15 commit 15d75692161434c64f7c956016a4baa1e112b481 @mbostock mbostock committed May 30, 2011
View
@@ -37,6 +37,7 @@ d3.core.js: \
src/core/descending.js \
src/core/min.js \
src/core/max.js \
+ src/core/zip.js \
src/core/bisect.js \
src/core/nest.js \
src/core/keys.js \
@@ -103,6 +104,7 @@ d3.chart.js: \
src/chart/chart.js \
src/chart/box.js \
src/chart/bullet.js \
+ src/chart/horizon.js \
src/chart/qq.js \
src/end.js
@@ -165,8 +167,11 @@ tests: \
tests/test-insert.test \
tests/test-interpolate.test \
tests/test-keys.test \
+ tests/test-max.test \
+ tests/test-min.test \
tests/test-nest.test \
tests/test-permute.test \
+ tests/test-zip.test \
tests/test-remove.test \
tests/test-rgb.test \
tests/test-round.test \
View
@@ -3,10 +3,7 @@
// TODO unbind listener?
d3.behavior.zoom = function() {
- // https://bugs.webkit.org/show_bug.cgi?id=40441
- var bug40441 = /WebKit\/533/.test(navigator.userAgent) ? -1 : 0,
- bug40441Last = 0,
- x = 0,
+ var x = 0,
y = 0,
z = 0,
listeners = [],
@@ -53,6 +50,20 @@ d3.behavior.zoom = function() {
}
}
+ // mousewheel events are totally broken!
+ // https://bugs.webkit.org/show_bug.cgi?id=40441
+ // not only that, but Chrome and Safari differ in re. to acceleration!
+
+ var outer = d3.select("body").append("div")
+ .style("visibility", "hidden")
+ .style("position", "absolute")
+ .style("top", "-3000px")
+ .style("height", 0)
+ .style("overflow-y", "scroll")
+ .append("div")
+ .style("height", "2000px")
+ .node().parentNode;
+
function mousewheel(d, i) {
var e = d3.event;
@@ -72,16 +83,17 @@ d3.behavior.zoom = function() {
if (e.type === "dblclick") {
z = e.shiftKey ? Math.ceil(z - 1) : Math.floor(z + 1);
} else {
- var delta = (e.wheelDelta / 120 || -e.detail) * .1;
-
- /* Detect fast & large wheel events on WebKit. */
- if (bug40441 < 0) {
- var now = Date.now(), since = now - bug40441Last;
- if ((since > 9) && (Math.abs(e.wheelDelta) / since >= 50)) bug40441 = 1;
- bug40441Last = now;
+ var delta = e.wheelDelta || -e.detail;
+ if (delta) {
+ try {
+ outer.scrollTop = 1000;
+ outer.dispatchEvent(e);
+ delta = 1000 - outer.scrollTop;
+ } catch (error) {
+ // Derp! Hope for the best?
+ }
+ delta *= .005;
}
- if (bug40441 === 1) delta *= .03;
-
z += delta;
}
View
Oops, something went wrong.
View
@@ -296,16 +296,9 @@ function d3_chart_boxQuartiles(d) {
return ~~q === q ? (d[q] + d[q + 1]) / 2 : d[Math.round(q)];
});
}
-// ranges (bad, satisfactory, good)
-// measures (actual, forecast)
-// markers (previous, goal)
-
-/*
- * Chart design based on the recommendations of Stephen Few. Implementation
- * based on the work of Clint Ivy, Jamie Love, and Jason Davies.
- * http://projects.instantcognition.com/protovis/bulletchart/
- */
-
+// Chart design based on the recommendations of Stephen Few. Implementation
+// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
+// http://projects.instantcognition.com/protovis/bulletchart/
d3.chart.bullet = function() {
var orient = "left", // TODO top & bottom
reverse = false,
@@ -468,18 +461,21 @@ d3.chart.bullet = function() {
return bullet;
};
+ // ranges (bad, satisfactory, good)
bullet.ranges = function(x) {
if (!arguments.length) return ranges;
ranges = x;
return bullet;
};
+ // markers (previous, goal)
bullet.markers = function(x) {
if (!arguments.length) return markers;
markers = x;
return bullet;
};
+ // measures (actual, forecast)
bullet.measures = function(x) {
if (!arguments.length) return measures;
measures = x;
@@ -537,6 +533,209 @@ function d3_chart_bulletWidth(x) {
return Math.abs(x(d) - x0);
};
}
+// Implements a horizon layout, which is a variation of a single-series
+// area chart where the area is folded into multiple bands. Color is used to
+// encode band, allowing the size of the chart to be reduced significantly
+// without impeding readability. This layout algorithm is based on the work of
+// J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart
+// Size and Layering on the Graphical Perception of Time Series Visualizations",
+// CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf
+d3.chart.horizon = function() {
+ var bands = 1, // between 1 and 5, typically
+ mode = "offset", // or mirror
+ interpolate = "linear", // or basis, monotone, step-before, etc.
+ x = d3_chart_horizonX,
+ y = d3_chart_horizonY,
+ w = 960,
+ h = 40,
+ duration = 0;
+
+ var color = d3.scale.linear()
+ .domain([-1, 0, 1])
+ .range(["#d62728", "#fff", "#1f77b4"]);
+
+ // For each small multiple…
+ function horizon(g) {
+ g.each(function(d, i) {
+ var g = d3.select(this),
+ n = 2 * bands + 1,
+ xMin = Infinity,
+ xMax = -Infinity,
+ yMax = -Infinity,
+ x0, // old x-scale
+ y0, // old y-scale
+ id; // unique id for paths
+
+ // Compute x- and y-values along with extents.
+ var data = d.map(function(d, i) {
+ var xv = x.call(this, d, i),
+ yv = y.call(this, d, i);
+ if (xv < xMin) xMin = xv;
+ if (xv > xMax) xMax = xv;
+ if (-yv > yMax) yMax = -yv;
+ if (yv > yMax) yMax = yv;
+ return [xv, yv];
+ });
+
+ // Compute the new x- and y-scales.
+ var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]),
+ y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]);
+
+ // Retrieve the old scales, if this is an update.
+ if (this.__chart__) {
+ x0 = this.__chart__.x;
+ y0 = this.__chart__.y;
+ id = this.__chart__.id;
+ } else {
+ x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
+ y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
+ id = ++d3_chart_horizonId;
+ }
+
+ // We'll use a defs to store the area path and the clip path.
+ var defs = g.selectAll("defs")
+ .data([data]);
+
+ var defsEnter = defs.enter().append("svg:defs");
+
+ // The clip path is a simple rect.
+ defsEnter.append("svg:clipPath")
+ .attr("id", "d3_chart_horizon_clip" + id)
+ .append("svg:rect")
+ .attr("width", w)
+ .attr("height", h);
+
+ defs.select("rect").transition()
+ .duration(duration)
+ .attr("width", w)
+ .attr("height", h);
+
+ // The area path is rendered with our resuable d3.svg.area.
+ defsEnter.append("svg:path")
+ .attr("id", "d3_chart_horizon_path" + id)
+ .attr("d", d3_chart_horizonArea
+ .interpolate(interpolate)
+ .x(function(d) { return x0(d[0]); })
+ .y0(h * bands)
+ .y1(function(d) { return h * bands - y0(d[1]); }))
+ .transition()
+ .duration(duration)
+ .attr("d", d3_chart_horizonArea
+ .x(function(d) { return x1(d[0]); })
+ .y1(function(d) { return h * bands - y1(d[1]); }));
+
+ defs.select("path").transition()
+ .duration(duration)
+ .attr("d", d3_chart_horizonArea);
+
+ // We'll use a container to clip all horizon layers at once.
+ g.selectAll("g")
+ .data([null])
+ .enter().append("svg:g")
+ .attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")");
+
+ // Define the transform function based on the mode.
+ var transform = mode == "offset"
+ ? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; }
+ : function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; };
+
+ // Instantiate each copy of the path with different transforms.
+ var u = g.select("g").selectAll("use")
+ .data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number);
+
+ // TODO don't fudge the enter transition
+ u.enter().append("svg:use")
+ .attr("xlink:href", "#d3_chart_horizon_path" + id)
+ .attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); })
+ .style("fill", color)
+ .transition()
+ .duration(duration)
+ .attr("transform", transform);
+
+ u.transition()
+ .duration(duration)
+ .attr("transform", transform)
+ .style("fill", color);
+
+ u.exit().transition()
+ .duration(duration)
+ .attr("transform", transform)
+ .remove();
+
+ // Stash the new scales.
+ this.__chart__ = {x: x1, y: y1, id: id};
+ });
+ d3.timer.flush();
+ }
+
+ horizon.duration = function(x) {
+ if (!arguments.length) return duration;
+ duration = +x;
+ return horizon;
+ };
+
+ horizon.bands = function(x) {
+ if (!arguments.length) return bands;
+ bands = +x;
+ color.domain([-bands, 0, bands]);
+ return horizon;
+ };
+
+ horizon.mode = function(x) {
+ if (!arguments.length) return mode;
+ mode = x + "";
+ return horizon;
+ };
+
+ horizon.colors = function(x) {
+ if (!arguments.length) return color.range();
+ color.range(x);
+ return horizon;
+ };
+
+ horizon.interpolate = function(x) {
+ if (!arguments.length) return interpolate;
+ interpolate = x + "";
+ return horizon;
+ };
+
+ horizon.x = function(z) {
+ if (!arguments.length) return x;
+ x = z;
+ return horizon;
+ };
+
+ horizon.y = function(z) {
+ if (!arguments.length) return y;
+ y = z;
+ return horizon;
+ };
+
+ horizon.width = function(x) {
+ if (!arguments.length) return width;
+ w = +x;
+ return horizon;
+ };
+
+ horizon.height = function(x) {
+ if (!arguments.length) return height;
+ h = +x;
+ return horizon;
+ };
+
+ return horizon;
+};
+
+var d3_chart_horizonArea = d3.svg.area(),
+ d3_chart_horizonId = 0;
+
+function d3_chart_horizonX(d) {
+ return d[0];
+}
+
+function d3_chart_horizonY(d) {
+ return d[1];
+}
// Based on http://vis.stanford.edu/protovis/ex/qqplot.html
d3.chart.qq = function() {
var width = 1,
Oops, something went wrong.

0 comments on commit 15d7569

Please sign in to comment.