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