<!DOCTYPE html> <meta charset=“utf-8”> <style>

table {

width: 960px;
border-spacing: 0;
border-collapse: collapse;

}

th, td {

padding: 4px;

}

th {

text-align: left;

}

td {

border: solid 1px #ccc;
text-align: right;

}

td.fail {

background: lightcoral;

}

td.success {

background: lightgreen;

}

</style> <table>

<thead>
  <th>start</th>
  <th>end</th>
  <th colspan=5>actual intermediate values</th>
  <th>exp.</th>
  <th>act.</th>
</thead>
<tbody>
</tbody>

</table> <script src=“../../d3.js”></script> <script>

var format = d3.format(“,.2f”);

var tests = [

{start:  170, end:  225, expected: [ 170.00, -176.25, -162.50, -148.75, -135.00]},
{start:  225, end:  170, expected: [-135.00, -148.75, -162.50, -176.25,  170.00]},
{start: -170, end: -225, expected: [-170.00,  176.25,  162.50,  148.75,  135.00]},
{start: -225, end: -170, expected: [ 135.00,  148.75,  162.50,  176.25, -170.00]},
{start: -170, end:  170, expected: [-170.00, -175.00,  180.00,  175.00,  170.00]},
{start: -170, end:    0, expected: [-170.00, -127.50,  -85.00,  -42.50,    0.00]},
{start:  170, end:    0, expected: [ 170.00,  127.50,   85.00,   42.50,    0.00]},
{start: -180, end:   90, expected: [ 180.00,  157.50,  135.00,  112.50,   90.00]},
{start:  180, end:   90, expected: [ 180.00,  157.50,  135.00,  112.50,   90.00]},
{start: -180, end:  -90, expected: [-180.00, -157.50, -135.00, -112.50,  -90.00]},
{start:  180, end:  -90, expected: [ 180.00, -157.50, -135.00, -112.50,  -90.00]},
{start:  780, end:  -90, expected: [  60.00,  22.50,  -15.00,   -52.50,  -90.00]}

];

var tr = d3.select(“tbody”).selectAll(“tr”)

  .data(tests)
.enter().append("tr");

tr.append(“td”)

.text(function(d) { return format(d.start); });

tr.append(“td”)

.text(function(d) { return format(d.end); });

tr.selectAll(“.actual”)

  .data(function(d) {
    var interpolate = d3.interpolateTransform("rotate(" + d.start + ")", "rotate(" + d.end + ")");
    return d.expected.map(function(expected, i) {
      return {
        expected: expected,
        actual: d3.transform(interpolate(i / 4)).rotate
      };
    });
  })
.enter().append("td")
  .text(function(d, i) { return format(d.actual); })
  .attr("class", function(d) { return Math.abs(d.actual - d.expected) < .01 ? "success" : "fail"; });

var ga = tr.append(“td”).attr(“width”, 40).append(“svg”)

  .attr("width", 40)
  .attr("height", 20)
.append("g")
  .attr("transform", "translate(20,10)")
.append("g")
  .each(animateExpected);

ga.append(“path”)

.attr("d", d3.svg.symbol().type("cross").size(120));

ga.append(“circle”)

.attr("cx", 8)
.attr("r", 4);

var gb = tr.append(“td”).attr(“width”, 40).append(“svg”)

  .attr("width", 40)
  .attr("height", 20)
.append("g")
  .attr("transform", "translate(20,10)")
.append("g")
  .each(animateActual);

gb.append(“path”)

.attr("d", d3.svg.symbol().type("cross").size(120));

gb.append(“circle”)

.attr("cx", 8)
.attr("r", 4);

function animateExpected(d) {

d3.select(this).transition()
    .duration(2500)
    .attrTween("transform", function(d) {
      var a = d.start % 360, b = d.end % 360;
      if (a - b > 180) b += 360; else if (b - a > 180) a += 360; // shortest path
      return d3.interpolateString("rotate(" + a + ")", "rotate(" + b + ")");
    })
    .each("end", animateExpected);

}

function animateActual(d) {

d3.select(this)
    .attr("transform", "rotate(" + d.start + ")")
  .transition()
    .duration(2500)
    .attr("transform", "rotate(" + d.end + ")")
    .each("end", animateActual);

}

</script>