Skip to content

Commit

Permalink
Condense d3.interpolateTransform slightly.
Browse files Browse the repository at this point in the history
Also, fix the transform rotate test.
  • Loading branch information
mbostock committed Jul 31, 2012
1 parent 14ab1b3 commit ef8672c
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 205 deletions.
188 changes: 95 additions & 93 deletions d3.v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -1011,108 +1011,110 @@ 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,
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, ")");
q.push({i: 1, x: d3.interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3.interpolateNumber(ta[1], tb[1])});
} else if (tb[0] || tb[1]) {
s.push("translate(" + tb + ")");
} else {
s.push("");
}

if (ra != rb) {
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3.interpolateNumber(ra, rb)});
} else if (rb) {
s.push(s.pop() + "rotate(" + rb + ")");
}

if (wa != wb) {
q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3.interpolateNumber(wa, wb)});
} else if (wb) {
s.push(s.pop() + "skewX(" + wb + ")");
}

if (ka[0] != kb[0] || ka[1] != kb[1]) {
n = s.push(s.pop() + "scale(", null, ",", null, ")");
q.push({i: n - 4, x: d3.interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3.interpolateNumber(ka[1], kb[1])});
} else if (kb[0] != 1 || kb[1] != 1) {
s.push(s.pop() + "scale(" + kb + ")");
}

n = q.length;
return function(t) {
var i = -1, o;
while (++i < n) s[(o = q[i]).i] = o.x(t);
return s.join("");
};
};

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.interpolateTransform = function(a, b) {
ga.setAttribute("transform", a);
gb.setAttribute("transform", b);
a = ga.transform.baseVal;
b = gb.transform.baseVal;
function similar(a, b) {
var i = -1,
n = a.numberOfItems,
type;

if (d3_interpolateTransformSameType(a, b)) {
return d3.interpolateString(d3_interpolateTransformListString(a),
d3_interpolateTransformListString(b));
}
if (n !== b.numberOfItems) return false;

a = a.consolidate();
b = b.consolidate();
var s = [], // string constants and placeholders
q = [], // number interpolators
n,
A = new d3_transform(a ? a.matrix : d3_transformIdentity),
B = new d3_transform(b ? b.matrix : d3_transformIdentity),
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, ")");
q.push({i: 1, x: d3.interpolateNumber(ta[0], tb[0])}, {i: 3, x: d3.interpolateNumber(ta[1], tb[1])});
} else if (tb[0] || tb[1]) {
s.push("translate(" + tb + ")");
} else {
s.push("");
while (++i < n) {
if ((type = a.getItem(i).type) !== b.getItem(i).type || type <= 1) {
return false;
}
}

if (ra != rb) {
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; // shortest path
q.push({i: s.push(s.pop() + "rotate(", null, ")") - 2, x: d3.interpolateNumber(ra, rb)});
} else if (rb) {
s.push(s.pop() + "rotate(" + rb + ")");
}
return true;
}

if (wa != wb) {
q.push({i: s.push(s.pop() + "skewX(", null, ")") - 2, x: d3.interpolateNumber(wa, wb)});
} else if (wb) {
s.push(s.pop() + "skewX(" + wb + ")");
}
function format(l) {
var result = [],
i = -1,
n = l.numberOfItems,
m,
s;

if (ka[0] != kb[0] || ka[1] != kb[1]) {
n = s.push(s.pop() + "scale(", null, ",", null, ")");
q.push({i: n - 4, x: d3.interpolateNumber(ka[0], kb[0])}, {i: n - 2, x: d3.interpolateNumber(ka[1], kb[1])});
} else if (kb[0] != 1 || kb[1] != 1) {
s.push(s.pop() + "scale(" + kb + ")");
while (++i < n) {
t = l.getItem(i);
m = t.matrix;
switch (t.type) {
case 1: result.push("matrix(", m.a, " ", m.b, " ", m.c, " ", m.d, " ", m.e, " ", m.f, ")"); break;
case 2: result.push("translate(", m.e, ",", m.f, ")"); break;
case 3: result.push("scale(", m.a, " ", m.d, ")"); break;
case 4: result.push("rotate(", t.angle, ")"); break;
case 5: result.push("skewX(", t.angle, ")"); break;
case 6: result.push("skewY(", t.angle, ")"); break;
}
}

n = q.length;
return function(t) {
var i = -1, o;
while (++i < n) s[(o = q[i]).i] = o.x(t);
return s.join("");
};
})(a, b);
};

function d3_interpolateTransformListString(value) {
var result = [];
for (var i = 0, n = value.numberOfItems; i < n; i++) {
result.push(d3_interpolateTransformString(value.getItem(i)));
return result.join("");
}
return result.join("");
}

function d3_interpolateTransformString(t) {
var m = t.matrix;
switch (t.type) {
case SVGTransform.SVG_TRANSFORM_ROTATE: return "rotate(" + t.angle + ")";
case SVGTransform.SVG_TRANSFORM_SCALE: return "scale(" + m.a + " " + m.d + ")";
case SVGTransform.SVG_TRANSFORM_SKEWX: return "skewX(" + t.angle + ")";
case SVGTransform.SVG_TRANSFORM_SKEWY: return "skewY(" + t.angle + ")";
case SVGTransform.SVG_TRANSFORM_TRANSLATE:
return "translate(" + m.e + "," + m.f + ")";
case SVGTransform.SVG_TRANSFORM_MATRIX:
return "matrix(" + m.a + " " + m.b + " " + m.c + " " +
m.d + " " + m.e + " " + m.f + ")";
}
return "";
}

function d3_interpolateTransformSameType(a, b) {
var n = a.numberOfItems;
if (n !== b.numberOfItems) return false;
for (var i = 0, type; i < n; i++) {
if ((type = a.getItem(i).type) !== b.getItem(i).type ||
type === SVGTransform.SVG_TRANSFORM_UNKNOWN ||
type === SVGTransform.SVG_TRANSFORM_MATRIX) return false;
}
return true;
}
return (d3_interpolateTransformSimilar = function(a, b) {
ga.setAttribute("transform", a);
gb.setAttribute("transform", b);
a = ga.transform.baseVal;
b = gb.transform.baseVal;
return similar(a, b) && d3.interpolateString(format(a), format(b));
})(a, b);
};

d3.interpolateRgb = function(a, b) {
a = d3.rgb(a);
Expand Down Expand Up @@ -2557,11 +2559,12 @@ var d3_timer_frame = window.requestAnimationFrame
|| window.msRequestAnimationFrame
|| function(callback) { setTimeout(callback, 17); };
d3.transform = function(string) {
var g = document.createElementNS(d3.ns.prefix.svg, "g");
var g = document.createElementNS(d3.ns.prefix.svg, "g"),
identity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
return (d3.transform = function(string) {
g.setAttribute("transform", string);
var t = g.transform.baseVal.consolidate();
return new d3_transform(t ? t.matrix : d3_transformIdentity);
return new d3_transform(t ? t.matrix : identity);
})(string);
};

Expand Down Expand Up @@ -2614,8 +2617,7 @@ function d3_transformCombine(a, b, k) {
return a;
}

var d3_transformDegrees = 180 / Math.PI,
d3_transformIdentity = {a: 1, b: 0, c: 0, d: 1, e: 0, f: 0};
var d3_transformDegrees = 180 / Math.PI;
d3.mouse = function(container) {
return d3_mousePoint(container, d3_eventSource());
};
Expand Down
8 changes: 4 additions & 4 deletions d3.v2.min.js

Large diffs are not rendered by default.

43 changes: 28 additions & 15 deletions examples/transform/test-rotate.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@
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: 225, expected: [ 170.00, 183.75, 197.50, 211.25, 225.00]},
{start: 225, end: 170, expected: [ 225.00, 211.25, 197.50, 183.75, 170.00]},
{start: -170, end: -225, expected: [ 190.00, 176.25, 162.50, 148.75, 135.00]},
{start: -225, end: -170, expected: [ 135.00, 148.75, 162.50, 176.25, 190.00]},
{start: -170, end: 170, expected: [ 190.00, 185.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: 180, end: -90, expected: [ 180.00, 202.50, 225.00, 247.50, 270.00]}
];

var tr = d3.select("tbody").selectAll("tr")
Expand All @@ -84,34 +84,47 @@
.text(function(d, i) { return format(d.actual); })
.attr("class", function(d) { return Math.abs(d.actual - d.expected) < .01 ? "success" : "fail"; });

tr.append("td").attr("width", 40).append("svg")
var ga = tr.append("td").attr("width", 40).append("svg")
.attr("width", 40)
.attr("height", 20)
.append("g")
.attr("transform", "translate(20,10)")
.append("path")
.attr("d", d3.svg.symbol().type("cross").size(120))
.append("g")
.each(animateExpected);

tr.append("td").attr("width", 40).append("svg")
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("path")
.attr("d", d3.svg.symbol().type("cross").size(120))
.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", rotateTween)
.each("end", animateExpected);

function rotateTween(d) {
if (d.start - d.end > 180) d.end += 360;
else if (d.end - d.start > 180) d.start += 360;
return d3.interpolateString("rotate(" + d.start + ")", "rotate(" + d.end + ")");
var a = d.start, b = d.end;
if (a - b > 180) b += 360;
else if (b - a > 180) a += 360;
return d3.interpolateString("rotate(" + a + ")", "rotate(" + b + ")");
}
}

Expand Down
Loading

0 comments on commit ef8672c

Please sign in to comment.