mbostock/d3 — Issue #344: Munging of transform attributes breaks interpolation.
Author: mbostock
Created: 2011-10-19 21:58:52+00:00
State: open
Labels: ff, ie

I've noticed that Firefox and IE9 silently convert "transform(*x*,0)" to "transform(*x*)". Which is to say, if the *y*-coordinate is zero, it is silently dropped from the attribute. This causes problems when interpolating strings, because instead of interpolating from "translate(10,0)" to "translate(10,20)", we're interpolating from "translate(10)" to "translate(10,20)". In the latter case, the initial value of the second number (20) is undefined, so D3 cannot interpolate and just jumps directly to *y*=20.

One possible fix is to change the core interpolator, such that any new number for which there is no existing number is interpolated from zero rather than undefined:

```diff
diff --git a/src/core/interpolate.js b/src/core/interpolate.js
index 08e9312..ed842b1 100644
--- a/src/core/interpolate.js
+++ b/src/core/interpolate.js
@@ -68,16 +68,10 @@ d3.interpolateString = function(a, b) {
}
}

- // Remove any numbers in b not found in a.
- while (i < n) {
- o = q.pop();
- if (s[o.i + 1] == null) { // This match is followed by another number.
- s[o.i] = o.x;
- } else { // This match is followed by a string, so coallesce twice.
- s[o.i] = o.x + s[o.i + 1];
- s.splice(o.i + 1, 1);
- }
- n--;
+ // For any numbers in b not found in a, assume a is 0 and interpolate.
+ for (; i < n; ++i) {
+ o = q[i];
+ o.x = d3.interpolateNumber(0, parseFloat(o.x));
}

// Special optimization for only a single match.
```

However, this has potentially bad side-effects. For example, the d3.svg.axis component enters new ticks, and then uses a single transition on the entering and updating ticks to set the tick size. Previously, the entering new ticks would have an initial tick size of undefined, so when the transition starts they would immediately be set to the proper tick size; under the above change, they would transition from 0 to the tick size as they enter. This looks bad. We could fix the axis component to not do this, but there are potentially other places where we don't want to assume a missing number has the initial value zero.

On the other hand, the only alternative I can think of is to set the transform as "translate(*x*,.0000001)", which is stupid. Or to write an interpolator which is transform aware… Which is certainly a possibility since I think @jasondavies has already implemented transform transitions using matrix decomposition. But we'd have to register than in d3_interpolateByName which means the special-case behavior might sometimes not be available (e.g., direct d3.interpolate). And also that code is quite large to solve this one small problem.

Argh! You can see this bug in the bar-hierarchy example.