| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>WOO</title> | |
| <style> | |
| body { | |
| font: 10pt/12pt "Helvetica", sans-serif; | |
| } | |
| .axis path { | |
| fill: none; | |
| stroke: #555; | |
| shape-rendering: crispEdges; | |
| } | |
| .line { | |
| fill: none; | |
| stroke-width: 3px; | |
| } | |
| .line.a { | |
| stroke: #333; | |
| } | |
| .line.b { | |
| stroke: #888; | |
| } | |
| .area { | |
| stroke: none; | |
| fill: none; | |
| fill-opacity: 0.25; | |
| } | |
| .area.upper { | |
| fill: #0f0; | |
| } | |
| .area.lower { | |
| fill: #f00; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
| <script type="text/javascript"> | |
| var n = 100, | |
| a = bumpLayer(n, .1), | |
| b = bumpLayer(n, .1); | |
| var margin = { top: 10, right: 10, bottom: 25, left: 50 }, | |
| width = 960 - margin.left - margin.right, | |
| height = 500 - margin.top - margin.bottom; | |
| var svg = d3.select('body') | |
| .append('svg') | |
| .attr('width', width + margin.left + margin.right) | |
| .attr('height', height + margin.top + margin.bottom); | |
| var x = d3.scale.linear() | |
| .domain([0, n]) | |
| .range([0, width]); | |
| var y = d3.scale.linear() | |
| .domain([0, d3.max(a.concat(b), function(d) { return d.y })]) | |
| .range([height, 0]); | |
| var xAxis = d3.svg.axis() | |
| .orient('bottom') | |
| .scale(x); | |
| var xAxisElem = svg.append('g') | |
| .attr('class', 'x axis') | |
| .attr('transform', translate(margin.left, height + margin.top + 5)); | |
| xAxisElem.call(xAxis); | |
| var yAxis = d3.svg.axis() | |
| .orient('left') | |
| .scale(y); | |
| var yAxisElem = svg.append('g') | |
| .attr('class', 'y axis') | |
| .attr('transform', translate(margin.left - 5, margin.top)); | |
| yAxisElem.call(yAxis); | |
| addIntersections(a, b) | |
| var linefn = d3.svg.line() | |
| .x(function(d) { return x(d.x); }) | |
| .y(function(d) { return y(d.y); }); | |
| var upperAreaFn = d3.svg.area() | |
| .x(function(d) { return x(d.a.x) }) | |
| .y0(function(d) { return y(d.b.y) }) | |
| .y1(function(d) { return y(Math.max(d.a.y, d.b.y)) }); | |
| var lowerAreaFn = d3.svg.area() | |
| .x(function(d) { return x(d.a.x) }) | |
| .y0(function(d) { return y(d.b.y) }) | |
| .y1(function(d) { return y(Math.min(d.a.y, d.b.y)) }); | |
| var zipped = zip({ a: a, b: b }); | |
| data = [ zipped ]; | |
| var lines = svg.append('g') | |
| .attr('transform', translate(margin.left, margin.top)) | |
| .selectAll('g.group') | |
| .data(data); | |
| var enterLines = lines.enter() | |
| .append('g') | |
| .attr('class', 'group'); | |
| // add them in backwards on purpose | |
| enterLines.append('path').attr('class', 'lower area') | |
| enterLines.append('path').attr('class', 'upper area') | |
| enterLines.append('path').attr('class', 'b line'); | |
| enterLines.append('path').attr('class', 'a line'); | |
| lines.selectAll('path.a.line') | |
| .attr('d', function(d) { return linefn(d.map(function(d) { return d.a; })) }); | |
| lines.selectAll('path.b.line') | |
| .attr('d', function(d) { return linefn(d.map(function(d) { return d.b; })) }); | |
| lines.selectAll('path.upper.area') | |
| .attr('d', upperAreaFn) | |
| lines.selectAll('path.lower.area') | |
| .attr('d', lowerAreaFn); | |
| // Inspired by Lee Byron's test data generator. | |
| // Borrowed from http://bl.ocks.org/mbostock/3943967 | |
| function bumpLayer(n, o) { | |
| function bump(a) { | |
| var x = 1 / (.1 + Math.random()), | |
| y = 2 * Math.random() - .5, | |
| z = 10 / (.1 + Math.random()); | |
| for (var i = 0; i < n; i++) { | |
| var w = (i / n - y) * z; | |
| a[i] += x * Math.exp(-w * w); | |
| } | |
| } | |
| var a = [], i; | |
| for (i = 0; i < n; ++i) a[i] = o + o * Math.random(); | |
| for (i = 0; i < 5; ++i) bump(a); | |
| return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; }); | |
| } | |
| function translate(x, y) { | |
| return "translate(" + x + ", " + y + ")"; | |
| } | |
| function zip(o) { | |
| var keys = d3.keys(o); | |
| var lengths = d3.values(o).map(function(d) { return d.length }); | |
| var length = lengths[0]; | |
| lengths.forEach(function(d) { | |
| if (d != length) { | |
| throw new TypeError("lengths of values to zip() must be equal") | |
| } | |
| }); | |
| var results = []; | |
| for (var i = 0; i < length; i++) { | |
| var result = {}; | |
| keys.forEach(function(k) { | |
| result[k] = o[k][i]; | |
| }); | |
| results.push(result); | |
| } | |
| return results; | |
| } | |
| function addIntersections(a, b) { | |
| if (a.length != b.length) { | |
| throw new TypeError("a.length != b.length"); | |
| } | |
| for (var i = 0; i < a.length - 1; i++) { | |
| var a1 = a[i], | |
| b1 = b[i], | |
| a2 = a[i + 1], | |
| b2 = b[i + 1]; | |
| if ((a1.y > b1.y && a2.y < b2.y) ^ (a1.y < b1.y && a2.y > b2.y)) { | |
| // calculate line segment intersection | |
| var s = (((b2.x - b1.x) * (a1.y - b1.y)) - ((b2.y - b1.y) * (a1.x - b1.x))) / | |
| (((b2.y - b1.y) * (a2.x - a1.x)) - ((b2.x - b1.x) * (a2.y - a1.y))); | |
| var t = { | |
| x: a1.x + (s * (a2.x - a1.x)), | |
| y: a1.y + (s * (a2.y - a1.y)) | |
| } | |
| var spliceIndex = ++i; | |
| a.splice(spliceIndex, 0, t); | |
| b.splice(spliceIndex, 0, t); | |
| } | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment