Do not keep the view's matrix baked into _globalMatrix, since we might allow multiple views soon.

This commit is contained in:
Jürg Lehni 2014-04-07 17:35:17 +02:00
parent 66b1087d33
commit 60f6eca6c4
4 changed files with 49 additions and 44 deletions

View file

@ -1152,25 +1152,20 @@ var Item = Base.extend(Callback, /** @lends Item# */{
* @type Matrix * @type Matrix
* @bean * @bean
*/ */
getGlobalMatrix: function(_internal) { getGlobalMatrix: function(_dontClone) {
var matrix = this._globalMatrix, var matrix = this._globalMatrix,
updateVersion = this._project._updateVersion, updateVersion = this._project._updateVersion;
viewMatrix = this.getView()._matrix;
// Internally we actually do factor in the view's transformations as
// well, but these are removed again in the return statement below.
// This way it is easier to draw selections and handle non-direct
// blitting, see Item#draw().
// If #_globalMatrix is out of sync, recalculate it now. // If #_globalMatrix is out of sync, recalculate it now.
if (matrix && matrix._updateVersion !== updateVersion) if (matrix && matrix._updateVersion !== updateVersion)
matrix = null; matrix = null;
if (!matrix) { if (!matrix) {
matrix = this._globalMatrix = this._matrix.clone(); matrix = this._globalMatrix = this._matrix.clone();
matrix.preConcatenate(this._parent var parent = this._parent;
? this._parent.getGlobalMatrix(true) if (parent)
: viewMatrix); matrix.preConcatenate(parent.getGlobalMatrix(true));
matrix._updateVersion = updateVersion; matrix._updateVersion = updateVersion;
} }
return _internal ? matrix : viewMatrix.inverted().concatenate(matrix); return _dontClone ? matrix : matrix.clone();
}, },
/** /**
@ -1591,7 +1586,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
ctx.save(); ctx.save();
matrix.applyToContext(ctx); matrix.applyToContext(ctx);
// See Project#draw() for an explanation of new Base() // See Project#draw() for an explanation of new Base()
this.draw(ctx, new Base({ transforms: [matrix] })); this.draw(ctx, new Base({ matrices: [matrix] }));
ctx.restore(); ctx.restore();
var raster = new Raster(Item.NO_INSERT); var raster = new Raster(Item.NO_INSERT);
raster.setCanvas(canvas); raster.setCanvas(canvas);
@ -1706,8 +1701,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
? parentTotalMatrix.clone().concatenate(matrix) ? parentTotalMatrix.clone().concatenate(matrix)
// If this is the first one in the recursion, factor in the // If this is the first one in the recursion, factor in the
// zoom of the view and the globalMatrix of the item. // zoom of the view and the globalMatrix of the item.
: this.getGlobalMatrix().clone().preConcatenate( : this.getGlobalMatrix().preConcatenate(view._matrix),
view._matrix),
// Calculate the transformed padding as 2D size that describes the // Calculate the transformed padding as 2D size that describes the
// transformed tolerance circle / ellipse. Make sure it's never 0 // transformed tolerance circle / ellipse. Make sure it's never 0
// since we're using it for division. // since we're using it for division.
@ -2911,8 +2905,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
* @return {Point} the transformed point as a new instance * @return {Point} the transformed point as a new instance
*/ */
globalToLocal: function(/* point */) { globalToLocal: function(/* point */) {
var matrix = this.getGlobalMatrix(); return this.getGlobalMatrix(true)._inverseTransform(
return matrix && matrix._inverseTransform(Point.read(arguments)); Point.read(arguments));
}, },
/** /**
@ -2923,8 +2917,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
* @return {Point} the transformed point as a new instance * @return {Point} the transformed point as a new instance
*/ */
localToGlobal: function(/* point */) { localToGlobal: function(/* point */) {
var matrix = this.getGlobalMatrix(); return this.getGlobalMatrix(true)._transformPoint(
return matrix && matrix._transformPoint(Point.read(arguments)); Point.read(arguments));
}, },
/** /**
@ -3520,10 +3514,10 @@ var Item = Base.extend(Callback, /** @lends Item# */{
var updateVersion = this._updateVersion = this._project._updateVersion; var updateVersion = this._updateVersion = this._project._updateVersion;
// Keep calculating the current global matrix, by keeping a history // Keep calculating the current global matrix, by keeping a history
// and pushing / popping as we go along. // and pushing / popping as we go along.
var trackTransforms = param.trackTransforms, var matrices = param.matrices,
transforms = param.transforms, parentMatrix = matrices[matrices.length - 1],
viewMatrix = param.viewMatrix,
matrix = this._matrix, matrix = this._matrix,
parentMatrix = transforms[transforms.length - 1],
globalMatrix = parentMatrix.clone().concatenate(matrix); globalMatrix = parentMatrix.clone().concatenate(matrix);
// If this item is not invertible, do not draw it, since it would cause // If this item is not invertible, do not draw it, since it would cause
// empty ctx.currentPath and mess up caching. It appears to also be a // empty ctx.currentPath and mess up caching. It appears to also be a
@ -3531,11 +3525,21 @@ var Item = Base.extend(Callback, /** @lends Item# */{
// handles it the same way. // handles it the same way.
if (!globalMatrix.isInvertible()) if (!globalMatrix.isInvertible())
return; return;
// Since globalMatrix does not take the view's matrix into account (we
// could have multiple views with different zooms), we may have to
// pre-concatenate the view's matrix.
// Note that it's only provided if it isn't the identity matrix.
function getViewMatrix(matrix) {
return viewMatrix ? viewMatrix.clone().concatenate(matrix) : matrix;
}
// Only keep track of transformation if told so. See Project#draw() // Only keep track of transformation if told so. See Project#draw()
if (trackTransforms) { matrices.push(globalMatrix);
transforms.push(this._globalMatrix = globalMatrix); if (param.updateMatrix) {
// We also keep the cached _globalMatrix versioned. // Update the cached _globalMatrix and keep it versioned.
globalMatrix._updateVersion = updateVersion; globalMatrix._updateVersion = updateVersion;
this._globalMatrix = globalMatrix;
} }
// If the item has a blendMode or is defining an opacity, draw it on // If the item has a blendMode or is defining an opacity, draw it on
@ -3561,7 +3565,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
if (!direct) { if (!direct) {
// Apply the parent's global matrix to the calculation of correct // Apply the parent's global matrix to the calculation of correct
// bounds. // bounds.
var bounds = this.getStrokeBounds(parentMatrix); var bounds = this.getStrokeBounds(getViewMatrix(parentMatrix));
if (!bounds.width || !bounds.height) if (!bounds.width || !bounds.height)
return; return;
// Store previous offset and save the main context, so we can // Store previous offset and save the main context, so we can
@ -3590,15 +3594,14 @@ var Item = Base.extend(Callback, /** @lends Item# */{
ctx.translate(-itemOffset.x, -itemOffset.y); ctx.translate(-itemOffset.x, -itemOffset.y);
} }
// Apply globalMatrix when drawing into temporary canvas. // Apply globalMatrix when drawing into temporary canvas.
(direct ? matrix : globalMatrix).applyToContext(ctx); (direct ? matrix : getViewMatrix(globalMatrix)).applyToContext(ctx);
// If we're drawing into a separate canvas and a clipItem is defined for // If we're drawing into a separate canvas and a clipItem is defined for
// the current rendering loop, we need to draw the clip item again. // the current rendering loop, we need to draw the clip item again.
if (!direct && param.clipItem) if (!direct && param.clipItem)
param.clipItem.draw(ctx, param.extend({ clip: true })); param.clipItem.draw(ctx, param.extend({ clip: true }));
this._draw(ctx, param); this._draw(ctx, param);
ctx.restore(); ctx.restore();
if (trackTransforms) matrices.pop();
transforms.pop();
if (param.clip && !param.dontFinish) if (param.clip && !param.dontFinish)
ctx.clip(); ctx.clip();
// If a temporary canvas was created, composite it onto the main canvas: // If a temporary canvas was created, composite it onto the main canvas:

View file

@ -489,7 +489,7 @@ var Raster = Item.extend(/** @lends Raster# */{
// If a path was passed, draw it as a clipping mask: // If a path was passed, draw it as a clipping mask:
// See Project#draw() for an explanation of new Base() // See Project#draw() for an explanation of new Base()
if (path) if (path)
path.draw(ctx, new Base({ clip: true, transforms: [matrix] })); path.draw(ctx, new Base({ clip: true, matrices: [matrix] }));
// Now draw the image clipped into it. // Now draw the image clipped into it.
this._matrix.applyToContext(ctx); this._matrix.applyToContext(ctx);
ctx.drawImage(this.getElement(), ctx.drawImage(this.getElement(),

View file

@ -150,6 +150,8 @@ PathItem.inject(new function() {
// See if the CompoundPath can be reduced to just a simple Path. // See if the CompoundPath can be reduced to just a simple Path.
result = result.reduce(); result = result.reduce();
// Copy over the left-hand item's style and we're done. // Copy over the left-hand item's style and we're done.
// TODO: Consider using Item#_clone() for this, but find a way to not
// clone children / name (content).
result.setStyle(path1._style); result.setStyle(path1._style);
return result; return result;
} }

View file

@ -426,12 +426,12 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
var param = new Base({ var param = new Base({
offset: new Point(0, 0), offset: new Point(0, 0),
pixelRatio: pixelRatio, pixelRatio: pixelRatio,
// Tell the drawing routine that we want to track nested matrices viewMatrix: matrix.isIdentity() ? null : matrix,
// in param.transforms, and that we want it to set _globalMatrix matrices: [new Matrix()], // Start with the identity matrix.
// as used below. Item#rasterize() and Raster#getAverageColor() do // Tell the drawing routine that we want to keep _globalMatrix up to
// not need to set this. // date. Item#rasterize() and Raster#getAverageColor() should not
trackTransforms: true, // set this.
transforms: [matrix] updateMatrix: true
}); });
for (var i = 0, l = this.layers.length; i < l; i++) for (var i = 0, l = this.layers.length; i < l; i++)
this.layers[i].draw(ctx, param); this.layers[i].draw(ctx, param);
@ -441,24 +441,24 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
if (this._selectedItemCount > 0) { if (this._selectedItemCount > 0) {
ctx.save(); ctx.save();
ctx.strokeWidth = 1; ctx.strokeWidth = 1;
for (var id in this._selectedItems) { var size = this._scope.settings.handleSize,
var item = this._selectedItems[id],
globalMatrix = item._globalMatrix,
size = this._scope.settings.handleSize,
half = size / 2; half = size / 2;
for (var id in this._selectedItems) {
var item = this._selectedItems[id];
if (item._updateVersion === this._updateVersion if (item._updateVersion === this._updateVersion
&& (item._drawSelected || item._boundsSelected) && (item._drawSelected || item._boundsSelected)) {
&& globalMatrix) {
// Allow definition of selected color on a per item and per // Allow definition of selected color on a per item and per
// layer level, with a fallback to #009dec // layer level, with a fallback to #009dec
var color = item.getSelectedColor() var color = item.getSelectedColor()
|| item.getLayer().getSelectedColor(); || item.getLayer().getSelectedColor(),
mx = matrix.clone().concatenate(
item.getGlobalMatrix(true));
ctx.strokeStyle = ctx.fillStyle = color ctx.strokeStyle = ctx.fillStyle = color
? color.toCanvasStyle(ctx) : '#009dec'; ? color.toCanvasStyle(ctx) : '#009dec';
if (item._drawSelected) if (item._drawSelected)
item._drawSelected(ctx, globalMatrix); item._drawSelected(ctx, mx);
if (item._boundsSelected) { if (item._boundsSelected) {
var coords = globalMatrix._transformCorners( var coords = mx._transformCorners(
item.getInternalBounds()); item.getInternalBounds());
// Now draw a rectangle that connects the transformed // Now draw a rectangle that connects the transformed
// bounds corners, and draw the corners. // bounds corners, and draw the corners.