Skip to content

Commit

Permalink
Revert piecewise interpolation for transforms.
Browse files Browse the repository at this point in the history
Rather than adopting CSS’s elaborate rules for detecting when the start and end
transform are the same type as using piecewise (i.e., string-based)
interpolation, we always use the consolidated transform transition. If you want
to use string-based interpolation, simply specify d3.interpolateString as the
interpolator. Fixes #746.
  • Loading branch information
mbostock committed Aug 4, 2012
1 parent 3064f0a commit 91630af
Show file tree
Hide file tree
Showing 6 changed files with 23 additions and 181 deletions.
60 changes: 0 additions & 60 deletions d3.v2.js
Expand Up @@ -860,7 +860,6 @@
};
};
d3.interpolateTransform = function(a, b) {
if (n = d3_interpolateTransformSimilar(a, b)) return n;
var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale;
if (ta[0] != tb[0] || ta[1] != tb[1]) {
s.push("translate(", null, ",", null, ")");
Expand Down Expand Up @@ -912,65 +911,6 @@
return s.join("");
};
};
var d3_interpolateTransformTypes = [ "", "", "translate", "scale", "rotate", "skewX", "skewY" ];
var d3_interpolateTransformSimilar = function(a, b) {
var ga = document.createElementNS(d3.ns.prefix.svg, "g"), gb = document.createElementNS(d3.ns.prefix.svg, "g");
return (d3_interpolateTransformSimilar = function(a, b) {
ga.setAttribute("transform", a);
gb.setAttribute("transform", b);
a = ga.transform.baseVal;
b = gb.transform.baseVal;
var sa = [], sb = [], i = -1, n = a.numberOfItems, m = b.numberOfItems, ta, tb, type;
if (m !== n) {
if (!m) b = d3_interpolateTransformIdentity(a); else if (!n) a = d3_interpolateTransformIdentity(b), n = m; else return;
} else if (!m) return;
while (++i < n) {
ta = a.getItem(i);
tb = b.getItem(i);
type = ta.type;
if (type !== tb.type || !type) return;
switch (type) {
case 1:
{
sa.push(new d3_transform(ta.matrix));
sb.push(new d3_transform(tb.matrix));
continue;
}
case 2:
{
ra = ta.matrix.e + "," + ta.matrix.f;
rb = tb.matrix.e + "," + tb.matrix.f;
break;
}
case 3:
{
ra = ta.matrix.a + "," + ta.matrix.d;
rb = tb.matrix.a + "," + tb.matrix.d;
break;
}
default:
{
ra = ta.angle;
rb = tb.angle;
}
}
sa.push(type = d3_interpolateTransformTypes[type], "(", ra, ")");
sb.push(type, "(", rb, ")");
}
return d3.interpolateString(sa.join(""), sb.join(""));
})(a, b);
};
function d3_interpolateTransformIdentity(a) {
return {
getItem: function(i) {
return {
type: a.getItem(i).type,
angle: 0,
matrix: d3_transformIdentity
};
}
};
}
d3.interpolateRgb = function(a, b) {
a = d3.rgb(a);
b = d3.rgb(b);
Expand Down
8 changes: 4 additions & 4 deletions d3.v2.min.js

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions examples/transform/null-matrix.html
Expand Up @@ -2,13 +2,15 @@
<body>
<script src="../../d3.v2.js"></script>
<script>

var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500);

var g = svg.append("g")
.attr("transform", "translate(100,100)")
.append("g");
.append("g")
.attr("transform", "matrix(1 0 0 1 0 0)rotate(0)");

var rect = g.append("rect")
.attr("x", -25)
Expand All @@ -18,5 +20,6 @@

g.transition()
.duration(3000)
.attr("transform", "matrix(1 0 0 1 100 100)rotate(360)");
.attr("transform", "matrix(1 0 0 1 100 100)rotate(360)", d3.interpolateString);

</script>
7 changes: 5 additions & 2 deletions examples/transform/null.html
Expand Up @@ -2,13 +2,15 @@
<body>
<script src="../../d3.v2.js"></script>
<script>

var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500);

var g = svg.append("g")
.attr("transform", "translate(100,100)")
.append("g");
.append("g")
.attr("transform", "translate(0,0)rotate(0)");

var rect = g.append("rect")
.attr("x", -25)
Expand All @@ -18,5 +20,6 @@

g.transition()
.duration(3000)
.attr("transform", "translate(100,100)rotate(360)");
.attr("transform", "translate(100,100)rotate(360)", d3.interpolateString);

</script>
14 changes: 9 additions & 5 deletions examples/transform/test-rotate.html
Expand Up @@ -51,14 +51,14 @@
{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, -85.00, 0.00, 85.00, 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, -112.50, -45.00, 22.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: 180, end: -90, expected: [ 180.00, 112.50, 45.00, -22.50, -90.00]},
{start: 780, end: -90, expected: [ 60.00, -157.50, -15.00, 127.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")
Expand Down Expand Up @@ -118,7 +118,11 @@
function animateExpected(d) {
d3.select(this).transition()
.duration(2500)
.attrTween("transform", function(d) { return d3.interpolateString("rotate(" + d.start + ")", "rotate(" + d.end + ")"); })
.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);
}

Expand Down
108 changes: 0 additions & 108 deletions src/core/interpolate.js
Expand Up @@ -93,8 +93,6 @@ d3.interpolateString = function(a, b) {
};

d3.interpolateTransform = function(a, b) {
if ((n = d3_interpolateTransformSimilar(a, b))) return n;

var s = [], // string constants and placeholders
q = [], // number interpolators
n,
Expand Down Expand Up @@ -146,112 +144,6 @@ d3.interpolateTransform = function(a, b) {
};
};

var d3_interpolateTransformTypes = [
"",
"",
"translate",
"scale",
"rotate",
"skewX",
"skewY"
];

// If both the ‘from’ and ‘to’ transforms have the same number of transform
// functions and corresponding functions in each transform list are of the same
// type, each transform function is animated with its corresponding destination
// function in isolation using the rules described above. The individual values
// are then applied as a list to produce resulting transform value.
var d3_interpolateTransformSimilar = function(a, b) {
var ga = document.createElementNS(d3.ns.prefix.svg, "g"),
gb = document.createElementNS(d3.ns.prefix.svg, "g");
return (d3_interpolateTransformSimilar = function(a, b) {
ga.setAttribute("transform", a);
gb.setAttribute("transform", b);
a = ga.transform.baseVal;
b = gb.transform.baseVal;

var sa = [],
sb = [],
i = -1,
n = a.numberOfItems,
m = b.numberOfItems,
ta,
tb,
type;

// If one of the ‘from’ or ‘to’ transforms is "none", the ‘none’ is replaced
// by an equivalent identity function list for the corresponding transform
// function list. Otherwise, if the transform function lists do not have the
// same number of items, the transforms are each converted into the
// equivalent matrix value and animation proceeds using the rule for a
// single function above.
if (m !== n) {
if (!m) b = d3_interpolateTransformIdentity(a);
else if (!n) a = d3_interpolateTransformIdentity(b), n = m;
else return;
}

// If both the ‘from’ and ‘to’ transforms are "none", there is no
// interpolation necessary.
else if (!m) return;

while (++i < n) {
ta = a.getItem(i);
tb = b.getItem(i);
type = ta.type;

// If the transform functions are not the same type, or the type is
// unknown, fallback to the decomposed transform transition.
if (type !== tb.type || !type) return; // unknown
switch (type) {

// For matrix, the matrix is decomposed using the method described by
// unmatrix into separate translation, scale, rotation and skew
// matrices, then each decomposed matrix is interpolated numerically,
// and finally combined in order to produce a resulting 3x2 matrix.
case 1: { // matrix
sa.push(new d3_transform(ta.matrix));
sb.push(new d3_transform(tb.matrix));
continue;
}

// For translate, scale, rotate and skew functions the individual
// components of the function are interpolated numerically.
case 2: { // translate
ra = ta.matrix.e + "," + ta.matrix.f;
rb = tb.matrix.e + "," + tb.matrix.f;
break;
}
case 3: { // scale
ra = ta.matrix.a + "," + ta.matrix.d;
rb = tb.matrix.a + "," + tb.matrix.d;
break;
}
default: { // rotate, skew
ra = ta.angle;
rb = tb.angle;
}
}
sa.push(type = d3_interpolateTransformTypes[type], "(", ra, ")");
sb.push(type, "(", rb, ")");
}

return d3.interpolateString(sa.join(""), sb.join(""));
})(a, b);
};

function d3_interpolateTransformIdentity(a) {
return {
getItem: function(i) {
return {
type: a.getItem(i).type,
angle: 0,
matrix: d3_transformIdentity
};
}
};
}

d3.interpolateRgb = function(a, b) {
a = d3.rgb(a);
b = d3.rgb(b);
Expand Down

0 comments on commit 91630af

Please sign in to comment.