block by zachmargolis 4940182928ee60aa7099

Line Comparison

Full Screen

index.html

<!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 //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>