Reflect View#zoom through matrix decomposition, and implement additional decomposed properties.

Closes #1107
This commit is contained in:
Jürg Lehni 2016-07-20 00:04:24 +02:00
parent 4d9c0c9f05
commit f874b927bf
2 changed files with 112 additions and 62 deletions

View file

@ -779,13 +779,11 @@ new function() { // Injection scope for various item event handlers
* @type Point
* @default null
*/
getPivot: function(_dontLink) {
getPivot: function() {
var pivot = this._pivot;
if (pivot) {
var ctor = _dontLink ? Point : LinkedPoint;
pivot = new ctor(pivot.x, pivot.y, this, 'setPivot');
}
return pivot;
return pivot
? new LinkedPoint(pivot.x, pivot.y, this, 'setPivot')
: null;
},
setPivot: function(/* point */) {
@ -1088,11 +1086,12 @@ new function() { // Injection scope for various item event handlers
* @bean
* @type Point
*/
getScaling: function(_dontLink) {
getScaling: function() {
var decomposed = this._decompose(),
scaling = decomposed && decomposed.scaling,
ctor = _dontLink ? Point : LinkedPoint;
return scaling && new ctor(scaling.x, scaling.y, this, 'setScaling');
scaling = decomposed && decomposed.scaling;
return scaling
? new LinkedPoint(scaling.x, scaling.y, this, 'setScaling')
: undefined;
},
setScaling: function(/* scaling */) {

View file

@ -107,7 +107,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
// Link this id to our view
View._viewsById[this._id] = this;
(this._matrix = new Matrix())._owner = this;
this._zoom = 1;
// Make sure the first view is focused for keyboard input straight away
if (!View._focused)
View._focused = this;
@ -331,7 +330,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
// one thing:
this._project._changed(/*#=*/Change.VIEW);
// Force recalculation of these values next time they are requested.
this._bounds = null;
this._bounds = this._decomposed = undefined;
},
/**
@ -436,56 +435,6 @@ var View = Base.extend(Emitter, /** @lends View# */{
return this.getBounds().getSize();
},
/**
* The center of the visible area in project coordinates.
*
* @bean
* @type Point
*/
getCenter: function() {
return this.getBounds().getCenter();
},
setCenter: function(/* center */) {
var center = Point.read(arguments);
this.translate(this.getCenter().subtract(center));
},
/**
* The zoom factor by which the project coordinates are magnified.
*
* @bean
* @type Number
*/
getZoom: function() {
return this._zoom;
},
setZoom: function(zoom) {
this.transform(new Matrix().scale(zoom / this._zoom,
this.getCenter()));
this._zoom = zoom;
},
/**
* The view's transformation matrix, defining the view onto the project's
* contents (position, zoom level, rotation, etc).
*
* @bean
* @type Matrix
*/
getMatrix: function() {
return this._matrix;
},
setMatrix: function() {
// Use Matrix#initialize to easily copy over values.
// NOTE: calling initialize() also calls #_changed() for us, through its
// call to #set() / #reset(), and this also handles _applyMatrix for us.
var matrix = this._matrix;
matrix.initialize.apply(matrix, arguments);
},
/**
* Checks whether the view is currently visible within the current browser
* viewport.
@ -539,6 +488,10 @@ var View = Base.extend(Emitter, /** @lends View# */{
center || this.getCenter(true)));
};
}, /** @lends View# */{
_decompose: function() {
return this._decomposed || (this._decomposed = this._matrix.decompose());
},
/**
* {@grouptitle Transform Functions}
*
@ -551,6 +504,104 @@ var View = Base.extend(Emitter, /** @lends View# */{
return this.transform(mx.translate.apply(mx, arguments));
},
/**
* The center of the visible area in project coordinates.
*
* @bean
* @type Point
*/
getCenter: function() {
return this.getBounds().getCenter();
},
setCenter: function(/* center */) {
var center = Point.read(arguments);
this.translate(this.getCenter().subtract(center));
},
/**
* The view's zoom factor by which the project coordinates are magnified.
*
* @bean
* @type Number
* @see #getScaling()
*/
getZoom: function() {
var decomposed = this._decompose(),
scaling = decomposed && decomposed.scaling;
// Use average since it can be non-uniform, and return 0 when it can't
// be decomposed.
return scaling ? (scaling.x + scaling.y) / 2 : 0;
},
setZoom: function(zoom) {
this.transform(new Matrix().scale(zoom / this.getZoom(),
this.getCenter()));
},
/**
* The current rotation angle of the view, as described by its
* {@link #matrix}.
*
* @bean
* @type Number
*/
getRotation: function() {
var decomposed = this._decompose();
return decomposed && decomposed.rotation;
},
setRotation: function(rotation) {
var current = this.getRotation();
if (current != null && rotation != null) {
this.rotate(rotation - current);
}
},
/**
* The current scale factor of the view, as described by its
* {@link #matrix}.
*
* @bean
* @type Point
* @see #getZoom()
*/
getScaling: function() {
var decomposed = this._decompose(),
scaling = decomposed && decomposed.scaling;
return scaling
? new LinkedPoint(scaling.x, scaling.y, this, 'setScaling')
: undefined;
},
setScaling: function(/* scaling */) {
var current = this.getScaling(),
// Clone existing points since we're caching internally.
scaling = Point.read(arguments, 0, { clone: true, readNull: true });
if (current && scaling) {
this.scale(scaling.x / current.x, scaling.y / current.y);
}
},
/**
* The view's transformation matrix, defining the view onto the project's
* contents (position, zoom level, rotation, etc).
*
* @bean
* @type Matrix
*/
getMatrix: function() {
return this._matrix;
},
setMatrix: function() {
// Use Matrix#initialize to easily copy over values.
// NOTE: calling initialize() also calls #_changed() for us, through its
// call to #set() / #reset(), and this also handles _applyMatrix for us.
var matrix = this._matrix;
matrix.initialize.apply(matrix, arguments);
},
/**
* Rotates the view by a given angle around the given center point.
*