Skip to content

Commit 8598dc4

Browse files
committed
More robust fix to d3.interpolateTransform.
1 parent e4f4a31 commit 8598dc4

5 files changed

Lines changed: 134 additions & 7 deletions

File tree

d3.v2.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1035,7 +1035,8 @@ d3.interpolateTransform = function(a, b) {
10351035
}
10361036

10371037
if (ra != rb) {
1038-
q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3.interpolateNumber(ra, Math.abs(ra - rb) > 180 ? rb + 360 : rb)});
1038+
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
1039+
q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3.interpolateNumber(ra, rb)});
10391040
} else if (rb) {
10401041
s.push(s.pop() + "rotate(" + rb + ")");
10411042
}

d3.v2.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<!DOCTYPE html>
2+
<meta charset="utf-8">
3+
<style>
4+
5+
table {
6+
width: 960px;
7+
border-spacing: 0;
8+
border-collapse: collapse;
9+
}
10+
11+
th, td {
12+
padding: 4px;
13+
}
14+
15+
th {
16+
text-align: left;
17+
}
18+
19+
td {
20+
border: solid 1px #ccc;
21+
text-align: right;
22+
}
23+
24+
td.fail {
25+
background: lightcoral;
26+
}
27+
28+
td.success {
29+
background: lightgreen;
30+
}
31+
32+
</style>
33+
<table>
34+
<thead>
35+
<th>start</th>
36+
<th>end</th>
37+
<th colspan=5>actual intermediate values</th>
38+
<th>exp.</th>
39+
<th>act.</th>
40+
</thead>
41+
<tbody>
42+
</tbody>
43+
</table>
44+
<script src="../../d3.v2.js"></script>
45+
<script>
46+
47+
var format = d3.format(",.2f");
48+
49+
var tests = [
50+
{start: 170, end: 225, expected: [ 170.00, -176.25, -162.50, -148.75, -135.00]},
51+
{start: 225, end: 170, expected: [-135.00, -148.75, -162.50, -176.25, 170.00]},
52+
{start: -170, end: -225, expected: [-170.00, 176.25, 162.50, 148.75, 135.00]},
53+
{start: -225, end: -170, expected: [ 135.00, 148.75, 162.50, 176.25, -170.00]},
54+
{start: -170, end: 170, expected: [-170.00, -175.00, 180.00, 175.00, 170.00]},
55+
{start: -170, end: 0, expected: [-170.00, -127.50, -85.00, -42.50, 0.00]},
56+
{start: 170, end: 0, expected: [ 170.00, 127.50, 85.00, 42.50, 0.00]},
57+
{start: -180, end: 90, expected: [ 180.00, 157.50, 135.00, 112.50, 90.00]},
58+
{start: 180, end: 90, expected: [ 180.00, 157.50, 135.00, 112.50, 90.00]},
59+
{start: -180, end: -90, expected: [-180.00, -157.50, -135.00, -112.50, -90.00]},
60+
{start: 180, end: -90, expected: [ 180.00, -157.50, -135.00, -112.50, -90.00]}
61+
];
62+
63+
var tr = d3.select("tbody").selectAll("tr")
64+
.data(tests)
65+
.enter().append("tr");
66+
67+
tr.append("td")
68+
.text(function(d) { return format(d.start); });
69+
70+
tr.append("td")
71+
.text(function(d) { return format(d.end); });
72+
73+
tr.selectAll(".actual")
74+
.data(function(d) {
75+
var interpolate = d3.interpolateTransform("rotate(" + d.start + ")", "rotate(" + d.end + ")");
76+
return d.expected.map(function(expected, i) {
77+
return {
78+
expected: expected,
79+
actual: d3.transform(interpolate(i / 4)).rotate
80+
};
81+
});
82+
})
83+
.enter().append("td")
84+
.text(function(d, i) { return format(d.actual); })
85+
.attr("class", function(d) { return Math.abs(d.actual - d.expected) < .01 ? "success" : "fail"; });
86+
87+
tr.append("td").attr("width", 40).append("svg")
88+
.attr("width", 40)
89+
.attr("height", 20)
90+
.append("g")
91+
.attr("transform", "translate(20,10)")
92+
.append("path")
93+
.attr("d", d3.svg.symbol().type("cross").size(120))
94+
.each(animateExpected);
95+
96+
tr.append("td").attr("width", 40).append("svg")
97+
.attr("width", 40)
98+
.attr("height", 20)
99+
.append("g")
100+
.attr("transform", "translate(20,10)")
101+
.append("path")
102+
.attr("d", d3.svg.symbol().type("cross").size(120))
103+
.each(animateActual);
104+
105+
function animateExpected(d) {
106+
d3.select(this).transition()
107+
.duration(2500)
108+
.attrTween("transform", rotateTween)
109+
.each("end", animateExpected);
110+
111+
function rotateTween(d) {
112+
if (d.start - d.end > 180) d.end += 360;
113+
else if (d.end - d.start > 180) d.start += 360;
114+
return d3.interpolateString("rotate(" + d.start + ")", "rotate(" + d.end + ")");
115+
}
116+
}
117+
118+
function animateActual(d) {
119+
d3.select(this)
120+
.attr("transform", "rotate(" + d.start + ")")
121+
.transition()
122+
.duration(2500)
123+
.attr("transform", "rotate(" + d.end + ")")
124+
.each("end", animateActual);
125+
}
126+
127+
</script>

examples/transform/test.html

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@
5757
}
5858
}
5959

60-
if (d3.interpolateTransform("rotate(170)", "rotate(225)")(.5) != "rotate(197.5)") ++failures;
61-
6260
outcome.text(failures ? failures + " failures" : "Success!");
6361

6462
function matrix(el) {

src/core/interpolate.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ d3.interpolateTransform = function(a, b) {
117117
}
118118

119119
if (ra != rb) {
120-
q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3.interpolateNumber(ra, Math.abs(ra - rb) > 180 ? rb + 360 : rb)});
120+
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
121+
q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3.interpolateNumber(ra, rb)});
121122
} else if (rb) {
122123
s.push(s.pop() + "rotate(" + rb + ")");
123124
}

0 commit comments

Comments
 (0)