Internalize Item#applyMatrix() code in #transform() and add short-cut method to it in Matrix#apply()

This commit is contained in:
Jürg Lehni 2014-03-02 15:50:45 +01:00
parent 6fdb9f60bd
commit fb1420eee0
3 changed files with 74 additions and 55 deletions

View file

@ -137,7 +137,7 @@ var Matrix = Base.extend(/** @lends Matrix# */{
},
/**
* "Resets" the matrix by setting its values to the ones of the identity
* Resets the matrix by setting its values to the ones of the identity
* matrix that results in no transformation.
*/
reset: function(_dontNotify) {
@ -148,6 +148,20 @@ var Matrix = Base.extend(/** @lends Matrix# */{
return this;
},
/**
* Applies the matrix to the item that it belongs to, if possible.
* @return {Boolean} {@true if the matrix was applied}
*/
apply: function() {
var owner = this._owner;
if (owner) {
owner.transform(null, true);
// If the matrix was successfully applied, it will be reset now.
return this.isIdentity();
}
return false;
},
/**
* Concatenates this transform with a translate transformation.
*

View file

@ -1134,9 +1134,13 @@ var Item = Base.extend(Callback, /** @lends Item# */{
setMatrix: function(matrix) {
// Use Matrix#initialize to easily copy over values.
this._matrix.initialize(matrix);
if (this._transformContent)
this.applyMatrix(true);
this._changed(/*#=*/ Change.GEOMETRY);
if (this._transformContent) {
// Directly apply the internal matrix. This will also call
// _changed() for us.
this.transform(null, true);
} else {
this._changed(/*#=*/ Change.GEOMETRY);
}
},
/**
@ -1174,8 +1178,10 @@ var Item = Base.extend(Callback, /** @lends Item# */{
},
setTransformContent: function(transform) {
// Tell #transform() to apply the internal matrix if _transformContent
// can be set to true.
if (this._transformContent = this._canTransformContent && !!transform)
this.applyMatrix();
this.transform(null, true);
},
/**
@ -2760,34 +2766,63 @@ var Item = Base.extend(Callback, /** @lends Item# */{
// 'children', 'fill-gradients', 'fill-patterns', 'stroke-patterns',
// 'lines'. Default: ['objects', 'children']
transform: function(matrix, _applyMatrix) {
// Bail out immediatelly if there is nothing to do
if (matrix.isIdentity())
// If no matrix is provided, or the matrix is the identity, we might
// still have some work to do in case _transformContent is true
if (matrix && matrix.isIdentity())
matrix = null;
var _matrix = this._matrix,
applyMatrix = (_applyMatrix || this._transformContent)
// Don't apply _matrix if the result of concatenating with
// matrix would be identity.
&& (!_matrix.isIdentity() || matrix);
// Bail out if there is nothing to do.
if (!matrix && !applyMatrix)
return this;
// Simply preconcatenate the internal matrix with the passed one:
if (matrix)
_matrix.preConcatenate(matrix);
// Call #_applyMatrix() now, if we need to directly apply the internal
// _matrix transformations to the item's content.
// Application is not possible on Raster, PointText, PlacedSymbol, since
// the matrix is where the actual transformation state is stored.
if (applyMatrix = applyMatrix && this._applyMatrix(_matrix)) {
// When the _matrix could be applied, we also need to transform
// color styles (only gradients so far) and pivot point:
var pivot = this._pivot,
style = this._style,
// pass true for _dontMerge so we don't recursively transform
// styles on groups' children.
fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true);
if (pivot)
pivot.transform(_matrix);
if (fillColor)
fillColor.transform(_matrix);
if (strokeColor)
strokeColor.transform(_matrix);
// Reset the internal matrix to the identity transformation if it
// was possible to apply it.
_matrix.reset(true);
}
// Calling _changed will clear _bounds and _position, but depending
// on matrix we can calculate and set them again.
// on matrix we can calculate and set them again, so preserve them.
var bounds = this._bounds,
position = this._position;
// Simply preconcatenate the internal matrix with the passed one:
this._matrix.preConcatenate(matrix);
// Call applyMatrix if we need to directly apply the accumulated
// transformations to the item's content.
if (this._transformContent || _applyMatrix)
this.applyMatrix(true);
// We always need to call _changed since we're caching bounds on all
// items, including Group.
this._changed(/*#=*/ Change.GEOMETRY);
// Detect matrices that contain only translations and scaling
// and transform the cached _bounds and _position without having to
// fully recalculate each time.
var decomp = bounds && matrix.decompose();
var decomp = bounds && matrix && matrix.decompose();
if (decomp && !decomp.shearing && decomp.rotation % 90 === 0) {
// Transform the old bound by looping through all the cached bounds
// in _bounds and transform each.
for (var key in bounds) {
var rect = bounds[key];
// If these are internal bounds, only transform them if this
// item transforming its content.
if (this._transformContent || !rect._internal)
// item applied its matrix.
if (applyMatrix || !rect._internal)
matrix._transformBounds(rect, rect);
}
// If we have cached bounds, update _position again as its
@ -2798,7 +2833,7 @@ var Item = Base.extend(Callback, /** @lends Item# */{
if (rect)
this._position = rect.getCenter(true);
this._bounds = bounds;
} else if (position) {
} else if (matrix && position) {
// Transform position as well.
this._position = matrix._transformPoint(position, position);
}
@ -2806,47 +2841,15 @@ var Item = Base.extend(Callback, /** @lends Item# */{
return this;
},
_applyMatrix: function(matrix, applyMatrix) {
_applyMatrix: function(matrix) {
var children = this._children;
if (children) {
for (var i = 0, l = children.length; i < l; i++)
children[i].transform(matrix, applyMatrix);
children[i].transform(matrix, true);
return true;
}
},
applyMatrix: function(_dontNotify) {
// Call #_applyMatrix() with the internal _matrix and pass true for
// applyMatrix. Application is not possible on Raster, PointText,
// PlacedSymbol, since the matrix is where the actual location /
// transformation state is stored.
// Pass on the transformation to the content, and apply it there too,
// by passing true for the 2nd hidden parameter.
var matrix = this._matrix;
if (this._applyMatrix(matrix, true)) {
// When the matrix could be applied, we also need to transform
// color styles (only gradients so far) and pivot point:
var pivot = this._pivot,
style = this._style,
// pass true for _dontMerge so we don't recursively transform
// styles on groups' children.
fillColor = style.getFillColor(true),
strokeColor = style.getStrokeColor(true);
if (pivot)
pivot.transform(matrix);
if (fillColor)
fillColor.transform(matrix);
if (strokeColor)
strokeColor.transform(matrix);
// Reset the internal matrix to the identity transformation if it
// was possible to apply it.
matrix.reset(true);
if (!_dontNotify)
this._changed(/*#=*/ Change.GEOMETRY);
}
return this;
},
/**
* Converts the specified point from global project coordinates to local
* coordinates in relation to the the item's own coordinate space.

View file

@ -43,6 +43,9 @@ PathItem.inject(new function() {
* NOTE: Does NOT handle self-intersecting CompoundPaths.
*/
function reorientPath(path) {
// Create a cloned version of the path firsts that we can modify freely,
// with its matrix applied to its geometry.
path = path.clone(false).reduce().transform(null, true);
if (path instanceof CompoundPath) {
var children = path.removeChildren(),
length = children.length,
@ -82,9 +85,8 @@ PathItem.inject(new function() {
// We call reduce() on both cloned paths to simplify compound paths and
// remove empty curves. We also apply matrices to both paths in case
// they were transformed.
var _path1 = reorientPath(path1.clone(false).reduce().applyMatrix());
_path2 = path2 && path1 !== path2
&& reorientPath(path2.clone(false).reduce().applyMatrix());
var _path1 = reorientPath(path1);
_path2 = path2 && path1 !== path2 && reorientPath(path2);
// Do operator specific calculations before we begin
// Make both paths at clockwise orientation, except when subtract = true
// We need both paths at opposite orientation for subtraction.