Give view proper Matrix transformation functions, just like on Item.

Relates to #832
This commit is contained in:
Jürg Lehni 2016-01-16 15:10:19 +01:00
parent b3e81af9b6
commit 3a3d46692b
6 changed files with 158 additions and 59 deletions

View file

@ -220,8 +220,6 @@ var Matrix = Base.extend(/** @lends Matrix# */{
* @return {Matrix} this affine transform * @return {Matrix} this affine transform
*/ */
scale: function(/* scale, center */) { scale: function(/* scale, center */) {
// Do not modify scale, center, since that would arguments of which
// we're reading from!
var scale = Point.read(arguments), var scale = Point.read(arguments),
center = Point.read(arguments, 0, { readNull: true }); center = Point.read(arguments, 0, { readNull: true });
if (center) if (center)
@ -761,10 +759,10 @@ var Matrix = Base.extend(/** @lends Matrix# */{
this._tx, this._ty); this._tx, this._ty);
} }
} }
}, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(name) { }, Base.each(['a', 'c', 'b', 'd', 'tx', 'ty'], function(key) {
// Create getters and setters for all internal attributes. // Create getters and setters for all internal attributes.
var part = Base.capitalize(name), var part = Base.capitalize(key),
prop = '_' + name; prop = '_' + key;
this['get' + part] = function() { this['get' + part] = function() {
return this[prop]; return this[prop];
}; };

View file

@ -947,10 +947,10 @@ var Point = Base.extend(/** @lends Point# */{
* /*#=*/Numerical.TRIGONOMETRIC_EPSILON; * /*#=*/Numerical.TRIGONOMETRIC_EPSILON;
} }
} }
}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { }, Base.each(['round', 'ceil', 'floor', 'abs'], function(key) {
// Inject round, ceil, floor, abs: // Inject round, ceil, floor, abs:
var op = Math[name]; var op = Math[key];
this[name] = function() { this[key] = function() {
return new Point(op(this.x), op(this.y)); return new Point(op(this.x), op(this.y));
}; };
}, {})); }, {}));

View file

@ -524,10 +524,10 @@ var Size = Base.extend(/** @lends Size# */{
return new Size(Math.random(), Math.random()); return new Size(Math.random(), Math.random());
} }
} }
}, Base.each(['round', 'ceil', 'floor', 'abs'], function(name) { }, Base.each(['round', 'ceil', 'floor', 'abs'], function(key) {
// Inject round, ceil, floor, abs: // Inject round, ceil, floor, abs:
var op = Math[name]; var op = Math[key];
this[name] = function() { this[key] = function() {
return new Size(op(this.width), op(this.height)); return new Size(op(this.width), op(this.height));
}; };
}, {})); }, {}));

View file

@ -35,7 +35,9 @@ var ChangeFlag = {
// Raster pixels // Raster pixels
PIXELS: 0x200, PIXELS: 0x200,
// Clipping in one of the child items // Clipping in one of the child items
CLIPPING: 0x400 CLIPPING: 0x400,
// The view has been transformed
VIEW: 0x800
}; };
// Shortcuts to often used ChangeFlag values including APPEARANCE // Shortcuts to often used ChangeFlag values including APPEARANCE
@ -51,5 +53,6 @@ var Change = {
STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE, STYLE: ChangeFlag.STYLE | ChangeFlag.APPEARANCE,
ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE, ATTRIBUTE: ChangeFlag.ATTRIBUTE | ChangeFlag.APPEARANCE,
CONTENT: ChangeFlag.CONTENT | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, CONTENT: ChangeFlag.CONTENT | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE PIXELS: ChangeFlag.PIXELS | ChangeFlag.APPEARANCE,
VIEW: ChangeFlag.VIEW | ChangeFlag.APPEARANCE
}; };

View file

@ -2936,11 +2936,19 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
* @property * @property
* @type Color * @type Color
*/ */
}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {
var rotate = key === 'rotate';
this[key] = function(/* value, center */) {
var value = (rotate ? Base : Point).read(arguments),
center = Point.read(arguments, 0, { readNull: true });
return this.transform(new Matrix()[key](value,
center || this.getPosition(true)));
};
}, /** @lends Item# */{
/** /**
* {@grouptitle Transform Functions} * {@grouptitle Transform Functions}
* *
* Translates (moves) the item by the given offset point. * Translates (moves) the item by the given offset views.
* *
* @param {Point} delta the offset to translate the item by * @param {Point} delta the offset to translate the item by
*/ */
@ -2950,13 +2958,15 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
}, },
/** /**
* Rotates the item by a given angle around the given point. * Rotates the item by a given angle around the given center point.
* *
* Angles are oriented clockwise and measured in degrees. * Angles are oriented clockwise and measured in degrees.
* *
* @name Item#rotate
* @function
* @param {Number} angle the rotation angle * @param {Number} angle the rotation angle
* @param {Point} [center={@link Item#position}] * @param {Point} [center={@link Item#position}]
* @see Matrix#rotate * @see Matrix#rotate(angle[, center])
* *
* @example {@paperscript} * @example {@paperscript}
* // Rotating an item: * // Rotating an item:
@ -2993,20 +3003,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
* path.rotate(3, view.center); * path.rotate(3, view.center);
* } * }
*/ */
rotate: function(angle /*, center */) {
return this.transform(new Matrix().rotate(angle,
Point.read(arguments, 1, { readNull: true })
|| this.getPosition(true)));
}
}, Base.each(['scale', 'shear', 'skew'], function(name) {
this[name] = function() {
// See Matrix#scale for explanation of this:
var point = Point.read(arguments),
center = Point.read(arguments, 0, { readNull: true });
return this.transform(new Matrix()[name](point,
center || this.getPosition(true)));
};
}, /** @lends Item# */{
/** /**
* Scales the item by the given value from its center point, or optionally * Scales the item by the given value from its center point, or optionally
* from a supplied point. * from a supplied point.
@ -3078,7 +3075,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
* @function * @function
* @param {Point} shear the horziontal and vertical shear factors as a point * @param {Point} shear the horziontal and vertical shear factors as a point
* @param {Point} [center={@link Item#position}] * @param {Point} [center={@link Item#position}]
* @see Matrix#shear * @see Matrix#shear(shear[, center])
*/ */
/** /**
* Shears the item by the given values from its center point, or optionally * Shears the item by the given values from its center point, or optionally
@ -3089,7 +3086,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
* @param {Number} hor the horizontal shear factor * @param {Number} hor the horizontal shear factor
* @param {Number} ver the vertical shear factor * @param {Number} ver the vertical shear factor
* @param {Point} [center={@link Item#position}] * @param {Point} [center={@link Item#position}]
* @see Matrix#shear * @see Matrix#shear(hor, ver[, center])
*/ */
/** /**
@ -3100,7 +3097,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
* @function * @function
* @param {Point} skew the horziontal and vertical skew angles in degrees * @param {Point} skew the horziontal and vertical skew angles in degrees
* @param {Point} [center={@link Item#position}] * @param {Point} [center={@link Item#position}]
* @see Matrix#shear * @see Matrix#shear(skew[, center])
*/ */
/** /**
* Skews the item by the given angles from its center point, or optionally * Skews the item by the given angles from its center point, or optionally
@ -3111,9 +3108,9 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
* @param {Number} hor the horizontal skew angle in degrees * @param {Number} hor the horizontal skew angle in degrees
* @param {Number} ver the vertical sskew angle in degrees * @param {Number} ver the vertical sskew angle in degrees
* @param {Point} [center={@link Item#position}] * @param {Point} [center={@link Item#position}]
* @see Matrix#shear * @see Matrix#shear(hor, ver[, center])
*/ */
}), /** @lends Item# */{
/** /**
* Transform the item. * Transform the item.
* *
@ -3331,7 +3328,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
*/ */
fitBounds: function(rectangle, fill) { fitBounds: function(rectangle, fill) {
// TODO: Think about passing options with various ways of defining // TODO: Think about passing options with various ways of defining
// fitting. // fitting. Compare with InDesign fitting to see possible options.
rectangle = Rectangle.read(arguments); rectangle = Rectangle.read(arguments);
var bounds = this.getBounds(), var bounds = this.getBounds(),
itemRatio = bounds.height / bounds.width, itemRatio = bounds.height / bounds.width,
@ -3343,8 +3340,8 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
new Size(bounds.width * scale, bounds.height * scale)); new Size(bounds.width * scale, bounds.height * scale));
newBounds.setCenter(rectangle.getCenter()); newBounds.setCenter(rectangle.getCenter());
this.setBounds(newBounds); this.setBounds(newBounds);
}, }
}), /** @lends Item# */{
/** /**
* {@grouptitle Event Handlers} * {@grouptitle Event Handlers}
* *
@ -4070,10 +4067,10 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
_canComposite: function() { _canComposite: function() {
return false; return false;
} }
}, Base.each(['down', 'drag', 'up', 'move'], function(name) { }, Base.each(['down', 'drag', 'up', 'move'], function(key) {
this['removeOn' + Base.capitalize(name)] = function() { this['removeOn' + Base.capitalize(key)] = function() {
var hash = {}; var hash = {};
hash[name] = true; hash[key] = true;
return this.removeOn(hash); return this.removeOn(hash);
}; };
}, /** @lends Item# */{ }, /** @lends Item# */{

View file

@ -261,18 +261,13 @@ var View = Base.extend(Emitter, /** @lends View# */{
/** /**
* Private notifier that is called whenever a change occurs in this view. * Private notifier that is called whenever a change occurs in this view.
* Used only by Matrix for now. * Used only by Matrix for now.
*
* @param {ChangeFlag} flags describes what exactly has changed
*/ */
_changed: function(flags) { _changed: function() {
this._project._changed(flags); // The only one calling View._changed() is Matrix, so it can only mean
}, // one thing:
this._project._changed(/*#=*/Change.VIEW);
_transform: function(matrix) {
this._matrix.concatenate(matrix);
// Force recalculation of these values next time they are requested. // Force recalculation of these values next time they are requested.
this._bounds = null; this._bounds = null;
this._update();
}, },
/** /**
@ -329,13 +324,13 @@ var View = Base.extend(Emitter, /** @lends View# */{
return; return;
this._viewSize.set(size.width, size.height); this._viewSize.set(size.width, size.height);
this._setViewSize(size); this._setViewSize(size);
this._bounds = null; // Force recalculation
// Call onResize handler on any size change // Call onResize handler on any size change
this.emit('resize', { this.emit('resize', {
size: size, size: size,
delta: delta delta: delta
}); });
this._update(); this._changed();
this.update();
}, },
/** /**
@ -382,7 +377,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
setCenter: function(/* center */) { setCenter: function(/* center */) {
var center = Point.read(arguments); var center = Point.read(arguments);
this.scrollBy(center.subtract(this.getCenter())); this.translate(this.getCenter().subtract(center));
}, },
/** /**
@ -396,8 +391,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
}, },
setZoom: function(zoom) { setZoom: function(zoom) {
// TODO: Clamp the view between 1/32 and 64, just like Illustrator? this.transform(new Matrix().scale(zoom / this._zoom,
this._transform(new Matrix().scale(zoom / this._zoom,
this.getCenter())); this.getCenter()));
this._zoom = zoom; this._zoom = zoom;
}, },
@ -438,17 +432,124 @@ var View = Base.extend(Emitter, /** @lends View# */{
*/ */
isInserted: function() { isInserted: function() {
return DomElement.isInserted(this._element); return DomElement.isInserted(this._element);
}
}, Base.each(['rotate', 'scale', 'shear', 'skew'], function(key) {
var rotate = key === 'rotate';
this[key] = function(/* value, center */) {
var value = (rotate ? Base : Point).read(arguments),
center = Point.read(arguments, 0, { readNull: true });
return this.transform(new Matrix()[key](value,
center || this.getCenter(true)));
};
}, /** @lends View# */{
/**
* {@grouptitle Transform Functions}
*
* Translates (scrolls) the view by the given offset vector.
*
* @param {Point} delta the offset to translate the view by
*/
translate: function(/* delta */) {
var mx = new Matrix();
return this.transform(mx.translate.apply(mx, arguments));
},
/**
* Rotates the view by a given angle around the given center point.
*
* Angles are oriented clockwise and measured in degrees.
*
* @name View#rotate
* @function
* @param {Number} angle the rotation angle
* @param {Point} [center={@link View#getCenter()}]
* @see Matrix#rotate(angle[, center])
*/
/**
* Scales the view by the given value from its center point, or optionally
* from a supplied point.
*
* @name View#scale
* @function
* @param {Number} scale the scale factor
* @param {Point} [center={@link View#getCenter()}]
*/
/**
* Scales the view by the given values from its center point, or optionally
* from a supplied point.
*
* @name View#scale
* @function
* @param {Number} hor the horizontal scale factor
* @param {Number} ver the vertical scale factor
* @param {Point} [center={@link View#getCenter()}]
*/
/**
* Shears the view by the given value from its center point, or optionally
* by a supplied point.
*
* @name View#shear
* @function
* @param {Point} shear the horziontal and vertical shear factors as a point
* @param {Point} [center={@link View#getCenter()}]
* @see Matrix#shear(shear[, center])
*/
/**
* Shears the view by the given values from its center point, or optionally
* by a supplied point.
*
* @name View#shear
* @function
* @param {Number} hor the horizontal shear factor
* @param {Number} ver the vertical shear factor
* @param {Point} [center={@link View#getCenter()}]
* @see Matrix#shear(hor, ver[, center])
*/
/**
* Skews the view by the given angles from its center point, or optionally
* by a supplied point.
*
* @name View#skew
* @function
* @param {Point} skew the horziontal and vertical skew angles in degrees
* @param {Point} [center={@link View#getCenter()}]
* @see Matrix#shear(skew[, center])
*/
/**
* Skews the view by the given angles from its center point, or optionally
* by a supplied point.
*
* @name View#skew
* @function
* @param {Number} hor the horizontal skew angle in degrees
* @param {Number} ver the vertical sskew angle in degrees
* @param {Point} [center={@link View#getCenter()}]
* @see Matrix#shear(hor, ver[, center])
*/
/**
* Transform the view.
*
* @param {Matrix} matrix the matrix by which the view shall be transformed
*/
transform: function(matrix) {
this._matrix.concatenate(matrix);
}, },
/** /**
* Scrolls the view by the given vector. * Scrolls the view by the given vector.
* *
* @param {Point} point * @param {Point} point
* @deprecated use {@link #translate(delta)} instead (using opposite
* direction).
*/ */
scrollBy: function(/* point */) { scrollBy: function(/* point */) {
this._transform(new Matrix().translate(Point.read(arguments).negate())); this.translate(Point.read(arguments).negate());
}, }
}), /** @lends View# */{
/** /**
* Makes all animation play by adding the view to the request animation * Makes all animation play by adding the view to the request animation
* loop. * loop.