I am using this lib for a web page that is drawing SVGs that can have flipped x or y coordinates (the g transform attr is set to scale(1, -1) for example).
I was able to in a not terrible way, add support for this in a local copy of the non minified code - the diff is below. Basically, k represents scale always, but a kxy param maps k to the scaling in x and y. The change means that any new Transform has to carry the kxy param, and the toString() function needs to take kxy into account, the rest basically just works. I wanted to share but am not sure what is needed to make this into a mergeable change - does this look like something that can be mergeable one day?
diff --git a/d3-zoom.v1.js b/d3-zoom.v1.js
index eb97b7d..6128cba 100644
--- a/d3-zoom.v1.js
+++ b/d3-zoom.v1.js
@@ -17,19 +17,21 @@ function ZoomEvent(target, type, transform) {
this.transform = transform;
}
-function Transform(k, x, y) {
+function Transform(k, x, y, kxy) {
this.k = k;
this.x = x;
this.y = y;
+ //This can be undfined, it's handled later
+ this.kxy = kxy;
}
Transform.prototype = {
constructor: Transform,
scale: function(k) {
- return k === 1 ? this : new Transform(this.k * k, this.x, this.y);
+ return k === 1 ? this : new Transform(this.k * k, this.x, this.y, this.kxy);
},
translate: function(x, y) {
- return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y);
+ return x === 0 & y === 0 ? this : new Transform(this.k, this.x + this.k * x, this.y + this.k * y, this.kxy);
},
apply: function(point) {
return [point[0] * this.k + this.x, point[1] * this.k + this.y];
@@ -56,12 +58,51 @@ Transform.prototype = {
return y.copy().domain(y.range().map(this.invertY, this).map(y.invert, y));
},
toString: function() {
- return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")";
+ var scale = "scale(";
+ if (this.kxy) {
+ //TODO: Matrix?
+ scale += this.kxy[0]*this.k+","+this.kxy[1]*this.k;
+ } else {
+ scale += this.k;
+ }
+ scale += ")";
+ return "translate(" + this.x + "," + this.y + ") " + scale;
}
};
var identity = new Transform(1, 0, 0);
+var parse = function(a){
+ //https://stackoverflow.com/a/17838403/557406
+ var b = {};
+ for (var i in a = a.match(/(\w+\((\-?\d+\.?\d*e?\-?\d*,?)+\))+/g)){
+ var c = a[i].match(/[\w\.\-]+/g);
+ b[c.shift()] = c.map(parseFloat);
+ }
+ return b;
+}
+
+var parseTransform = function (transformString){
+ if (!transformString) {
+ return identity;
+ }
+ var params = parse(transformString);
+ if (!params.translate) {
+ params.translate = [0,0];
+ }
+ if (!params.scale) {
+ params.scale = [1];
+ }
+ var k = Math.abs(params.scale[0]);
+ //NOTE: Parse will not have attr empty strings, instead it's just undefined
+ //We only use a pure k if scale is not flipped in x or y
+ if (k === params.scale[0] && (params.scale.length <2 || params.scale[1] === k)){
+ return new Transform(k, params.translate[0], params.translate[1]);
+ }
+ var kxy = [params.scale[0] / k, params.scale[1] / k];
+ return new Transform(k, params.translate[0], params.translate[1], kxy);
+}
+
transform.prototype = Transform.prototype;
function transform(node) {
@@ -136,9 +177,9 @@ var zoom = function() {
wheelDelay = 150,
clickDistance2 = 0;
- function zoom(selection) {
+ function zoom(selection, parsedTransform) {
selection
- .property("__zoom", defaultTransform)
+ .property("__zoom", parsedTransform || defaultTransform)
.on("wheel.zoom", wheeled)
.on("mousedown.zoom", mousedowned)
.on("dblclick.zoom", dblclicked)
@@ -207,12 +248,12 @@ var zoom = function() {
function scale(transform$$1, k) {
k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
- return k === transform$$1.k ? transform$$1 : new Transform(k, transform$$1.x, transform$$1.y);
+ return k === transform$$1.k ? transform$$1 : new Transform(k, transform$$1.x, transform$$1.y, transform$$1.kxy);
}
function translate(transform$$1, p0, p1) {
var x = p0[0] - p1[0] * transform$$1.k, y = p0[1] - p1[1] * transform$$1.k;
- return x === transform$$1.x && y === transform$$1.y ? transform$$1 : new Transform(transform$$1.k, x, y);
+ return x === transform$$1.x && y === transform$$1.y ? transform$$1 : new Transform(transform$$1.k, x, y, transform$$1.kxy);
}
function centroid(extent) {
@@ -235,7 +276,7 @@ var zoom = function() {
i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
return function(t) {
if (t === 1) t = b; // Avoid rounding error on end.
- else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
+ else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k, a.kxy); }
g.zoom(null, t);
};
});
@@ -283,15 +324,17 @@ var zoom = function() {
return this;
},
emit: function(type) {
+ //TODO: Fix array if both elements are the same
d3Selection.customEvent(new ZoomEvent(zoom, type, this.that.__zoom), listeners.apply, listeners, [type, this.that, this.args]);
}
};
function wheeled() {
if (!filter.apply(this, arguments)) return;
+ //Make scale an array for everything here
var g = gesture(this, arguments),
t = this.__zoom,
- k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
+ k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
p = d3Selection.mouse(this);
// If the mouse is in the same location as before, reuse it.
@@ -303,7 +346,7 @@ var zoom = function() {
clearTimeout(g.wheel);
}
- // If this wheel event won’t trigger a transform change, ignore it.
+ // If this wheel event won't trigger a transform change, ignore it.
else if (t.k === k) return;
// Otherwise, capture the mouse point and location at the start.
@@ -496,6 +539,7 @@ var zoom = function() {
exports.zoom = zoom;
exports.zoomTransform = transform;
exports.zoomIdentity = identity;
+exports.parseTransform = parseTransform;
Object.defineProperty(exports, '__esModule', { value: true });
I am using this lib for a web page that is drawing SVGs that can have flipped x or y coordinates (the g transform attr is set to
scale(1, -1)
for example).I was able to in a not terrible way, add support for this in a local copy of the non minified code - the diff is below. Basically, k represents scale always, but a kxy param maps k to the scaling in x and y. The change means that any new Transform has to carry the kxy param, and the
toString()
function needs to take kxy into account, the rest basically just works. I wanted to share but am not sure what is needed to make this into a mergeable change - does this look like something that can be mergeable one day?