Skip to content
Browse files

Add d3.transform and d3.interpolateTransform.

This is based on @jasondavies previous work on interpolating 3D matrices,
simplified to only handle 2D transforms, and using SVG to parse transforms.
  • Loading branch information...
1 parent 6fe70a8 commit e3f6f33b3d6e8845ad47dc069a00b985e9d569e9 @mbostock committed
Showing with 113 additions and 6 deletions.
  1. +1 −0 Makefile
  2. +55 −2 d3.js
  3. +2 −2 d3.min.js
  4. +6 −2 src/core/interpolate.js
  5. +49 −0 src/core/transform.js
View
1 Makefile
@@ -120,6 +120,7 @@ d3.core.js: \
src/core/transition-each.js \
src/core/transition-transition.js \
src/core/timer.js \
+ src/core/transform.js \
src/core/noop.js
d3.scale.js: \
View
57 d3.js
@@ -879,6 +879,10 @@ d3.interpolateString = function(a, b) {
};
};
+d3.interpolateTransform = function(a, b) {
+ return d3.interpolateString(d3.transform(a) + "", d3.transform(b) + "");
+};
+
d3.interpolateRgb = function(a, b) {
a = d3.rgb(a);
b = d3.rgb(b);
@@ -954,8 +958,8 @@ var d3_interpolate_number = /[-+]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][-]?\d+)?/g
d3.interpolators = [
d3.interpolateObject,
function(a, b) { return (b instanceof Array) && d3.interpolateArray(a, b); },
- function(a, b) { return (typeof b === "string") && d3.interpolateString(String(a), b); },
- function(a, b) { return (typeof b === "string" ? b in d3_rgb_names || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Rgb || b instanceof d3_Hsl) && d3.interpolateRgb(String(a), b); },
+ function(a, b) { return (typeof b === "string") && d3.interpolateString(a + "", b); },
+ function(a, b) { return (typeof b === "string" ? b in d3_rgb_names || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Rgb || b instanceof d3_Hsl) && d3.interpolateRgb(a + "", b); },
function(a, b) { return (typeof b === "number") && d3.interpolateNumber(+a, b); }
];
function d3_uninterpolateNumber(a, b) {
@@ -2237,6 +2241,55 @@ var d3_timer_frame = window.requestAnimationFrame
|| window.oRequestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) { setTimeout(callback, 17); };
+d3.transform = function(string) {
+ d3_transformG.setAttribute("transform", string);
+ var m = d3_transformG.transform.baseVal.consolidate().matrix;
+ if (m.a * m.d - m.b * m.c) return new d3_transform(m); // if invertible
+};
+
+// Compute x-scale and normalize the first row.
+// Compute shear and make second row orthogonal to first.
+// Compute y-scale and normalize the second row.
+// Finally, compute the rotation.
+function d3_transform(m) {
+ var r0 = [m.a, m.b],
+ r1 = [m.c, m.d],
+ kx = d3_transformNormalize(r0),
+ kz = d3_transformDot(r0, r1),
+ ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz));
+ this.translate = [m.e, m.f];
+ this.rotate = Math.atan2(m.b, m.a) * d3_transformDegrees;
+ this.scale = [kx, ky];
+ this.skew = kz / ky * d3_transformDegrees;
+};
+
+d3_transform.prototype.toString = function() {
+ return "translate(" + this.translate
+ + ")rotate(" + this.rotate
+ + ")skewX(" + this.skew
+ + ")scale(" + this.scale
+ + ")";
+};
+
+function d3_transformDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1];
+}
+
+function d3_transformNormalize(a) {
+ var k = Math.sqrt(d3_transformDot(a, a));
+ a[0] /= k;
+ a[1] /= k;
+ return k;
+}
+
+function d3_transformCombine(a, b, k) {
+ a[0] += k * b[0];
+ a[1] += k * b[1];
+ return a;
+}
+
+var d3_transformG = document.createElementNS(d3.ns.prefix.svg, "g"),
+ d3_transformDegrees = 180 / Math.PI;
function d3_noop() {}
d3.scale = {};
View
4 d3.min.js
2 additions, 2 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
8 src/core/interpolate.js
@@ -92,6 +92,10 @@ d3.interpolateString = function(a, b) {
};
};
+d3.interpolateTransform = function(a, b) {
+ return d3.interpolateString(d3.transform(a) + "", d3.transform(b) + "");
+};
+
d3.interpolateRgb = function(a, b) {
a = d3.rgb(a);
b = d3.rgb(b);
@@ -167,7 +171,7 @@ var d3_interpolate_number = /[-+]?(?:\d+\.\d+|\d+\.|\.\d+|\d+)(?:[eE][-]?\d+)?/g
d3.interpolators = [
d3.interpolateObject,
function(a, b) { return (b instanceof Array) && d3.interpolateArray(a, b); },
- function(a, b) { return (typeof b === "string") && d3.interpolateString(String(a), b); },
- function(a, b) { return (typeof b === "string" ? b in d3_rgb_names || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Rgb || b instanceof d3_Hsl) && d3.interpolateRgb(String(a), b); },
+ function(a, b) { return (typeof b === "string") && d3.interpolateString(a + "", b); },
+ function(a, b) { return (typeof b === "string" ? b in d3_rgb_names || /^(#|rgb\(|hsl\()/.test(b) : b instanceof d3_Rgb || b instanceof d3_Hsl) && d3.interpolateRgb(a + "", b); },
function(a, b) { return (typeof b === "number") && d3.interpolateNumber(+a, b); }
];
View
49 src/core/transform.js
@@ -0,0 +1,49 @@
+d3.transform = function(string) {
+ d3_transformG.setAttribute("transform", string);
+ var m = d3_transformG.transform.baseVal.consolidate().matrix;
+ if (m.a * m.d - m.b * m.c) return new d3_transform(m); // if invertible
+};
+
+// Compute x-scale and normalize the first row.
+// Compute shear and make second row orthogonal to first.
+// Compute y-scale and normalize the second row.
+// Finally, compute the rotation.
+function d3_transform(m) {
+ var r0 = [m.a, m.b],
+ r1 = [m.c, m.d],
+ kx = d3_transformNormalize(r0),
+ kz = d3_transformDot(r0, r1),
+ ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz));
+ this.translate = [m.e, m.f];
+ this.rotate = Math.atan2(m.b, m.a) * d3_transformDegrees;
+ this.scale = [kx, ky];
+ this.skew = kz / ky * d3_transformDegrees;
+};
+
+d3_transform.prototype.toString = function() {
+ return "translate(" + this.translate
+ + ")rotate(" + this.rotate
+ + ")skewX(" + this.skew
+ + ")scale(" + this.scale
+ + ")";
+};
+
+function d3_transformDot(a, b) {
+ return a[0] * b[0] + a[1] * b[1];
+}
+
+function d3_transformNormalize(a) {
+ var k = Math.sqrt(d3_transformDot(a, a));
+ a[0] /= k;
+ a[1] /= k;
+ return k;
+}
+
+function d3_transformCombine(a, b, k) {
+ a[0] += k * b[0];
+ a[1] += k * b[1];
+ return a;
+}
+
+var d3_transformG = document.createElementNS(d3.ns.prefix.svg, "g"),
+ d3_transformDegrees = 180 / Math.PI;

0 comments on commit e3f6f33

Please sign in to comment.
Something went wrong with that request. Please try again.