Various renaming and introduction of additional methods in Matrix

- Rename Matrix#concatenate() to #append() and #preConcatenate() to #prepend().
- Over #invert() as alternative to #inverted(), directly modifying the matrix.
- Make Matrix#_shiftless() and internal function.
- Introduce versions that return copies instead, named #appended(). (previously #chain()) and #prepended().
- Rename internal Path#_getPenPadding() to #_getStrokePadding().
This commit is contained in:
Jürg Lehni 2016-01-17 19:30:47 +01:00
parent 2ed9fae105
commit d292e08ed2
9 changed files with 122 additions and 89 deletions

View file

@ -346,12 +346,13 @@ var Matrix = Base.extend(/** @lends Matrix# */{
},
/**
* Concatenates the given affine transform to this transform.
* Appends the specified matrix to this matrix. This is the equivalent of
* multiplying `(this matrix) * (specified matrix)`.
*
* @param {Matrix} mx the transform to concatenate
* @return {Matrix} this affine transform
* @param {Matrix} matrix the matrix to append
* @return {Matrix} this matrix, modified
*/
concatenate: function(mx) {
append: function(mx) {
var a1 = this._a,
b1 = this._b,
c1 = this._c,
@ -373,12 +374,25 @@ var Matrix = Base.extend(/** @lends Matrix# */{
},
/**
* Pre-concatenates the given affine transform to this transform.
* Returns a new matrix as the result of appending the specified matrix to
* this matrix. This is the equivalent of multiplying
* `(this matrix) * (specified matrix)`.
*
* @param {Matrix} mx the transform to preconcatenate
* @return {Matrix} this affine transform
* @param {Matrix} matrix the matrix to append
* @return {Matrix} the newly created matrix
*/
preConcatenate: function(mx) {
appended: function(mx) {
return this.clone().append(mx);
},
/**
* Prepends the specified matrix to this matrix. This is the equivalent of
* multiplying `(specified matrix) * (this matrix)`.
*
* @param {Matrix} matrix the matrix to prepend
* @return {Matrix} this matrix, modified
*/
prepend: function(mx) {
var a1 = this._a,
b1 = this._b,
c1 = this._c,
@ -402,32 +416,74 @@ var Matrix = Base.extend(/** @lends Matrix# */{
},
/**
* Returns a new instance of the result of the concatenation of the given
* affine transform with this transform.
* Returns a new matrix as the result of prepending the specified matrix
* to this matrix. This is the equivalent of multiplying
* `(specified matrix) s* (this matrix)`.
*
* @param {Matrix} mx the transform to concatenate
* @return {Matrix} the newly created affine transform
* @param {Matrix} matrix the matrix to prepend
* @return {Matrix} the newly created matrix
*/
chain: function(mx) {
var a1 = this._a,
b1 = this._b,
c1 = this._c,
d1 = this._d,
tx1 = this._tx,
ty1 = this._ty,
a2 = mx._a,
b2 = mx._b,
c2 = mx._c,
d2 = mx._d,
tx2 = mx._tx,
ty2 = mx._ty;
return new Matrix(
a2 * a1 + c2 * b1,
a2 * c1 + c2 * d1,
b2 * a1 + d2 * b1,
b2 * c1 + d2 * d1,
tx1 + tx2 * a1 + ty2 * b1,
ty1 + tx2 * c1 + ty2 * d1);
prepended: function(mx) {
return this.clone().prepend(mx);
},
/**
* Inverts the matrix, causing it to perform the opposite transformation.
* If the matrix is not invertible (in which case {@link #isSingular()}
* returns true), `null` is returned.
*
* @return {Matrix} this matrix, or `null`, if the matrix is singular.
*/
invert: function() {
var det = this._getDeterminant(),
tx = this._tx,
ty = this._ty,
res = null;
if (det) {
this._tx = (this._b * ty - this._d * tx) / det;
this._ty = (this._c * tx - this._a * ty) / det;
this._d /= det;
this._c /= -det;
this._b /= -det;
this._a /= det;
res = this;
}
return res;
},
/**
* Creates a new matrix that is the inversion of this matrix, causing it to
* perform the opposite transformation. If the matrix is not invertible (in
* which case {@link #isSingular()} returns true), `null` is returned.
*
* @return {Matrix} this matrix, or `null`, if the matrix is singular.
*/
inverted: function() {
return this.clone().invert();
},
/**
* @deprecated, use use {@link #append(matrix)} instead.
*/
concatenate: '#append',
/**
* @deprecated, use use {@link #prepend(matrix)} instead.
*/
preConcatenate: '#prepend',
/**
* @deprecated, use use {@link #appended(matrix)} instead.
*/
chain: '#appended',
/**
* A private helper function to create a clone of this matrix, without the
* translation factored in.
*
* @return {Matrix} a clone of this matrix, with {@link #tx} and {@link #ty}
* set to `0`.
*/
_shiftless: function() {
return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
},
/**
@ -725,29 +781,6 @@ var Matrix = Base.extend(/** @lends Matrix# */{
return (this.decompose() || {}).rotation;
},
/**
* Creates the inversion of the transformation of the matrix and returns it
* as a new insteance. If the matrix is not invertible (in which case {@link
* #isSingular()} returns true), `null` is returned.
*
* @return {Matrix} the inverted matrix, or `null`, if the matrix is
* singular
*/
inverted: function() {
var det = this._getDeterminant();
return det && new Matrix(
this._d / det,
-this._c / det,
-this._b / det,
this._a / det,
(this._b * this._ty - this._d * this._tx) / det,
(this._c * this._tx - this._a * this._ty) / det);
},
shiftless: function() {
return new Matrix(this._a, this._c, this._b, this._d, 0, 0);
},
/**
* Applies this matrix to the specified Canvas Context.
*

View file

@ -838,7 +838,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
var child = children[i];
if (child._visible && !child.isEmpty()) {
var rect = child._getCachedBounds(getter,
matrix && matrix.chain(child._matrix), cacheItem);
matrix && matrix.appended(child._matrix), cacheItem);
x1 = Math.min(rect.x, x1);
y1 = Math.min(rect.y, y1);
x2 = Math.max(rect.x + rect.width, x2);
@ -1089,7 +1089,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
matrix = this._globalMatrix = this._matrix.clone();
var parent = this._parent;
if (parent)
matrix.preConcatenate(parent.getGlobalMatrix(true));
matrix.prepend(parent.getGlobalMatrix(true));
matrix._updateVersion = updateVersion;
}
return _dontClone ? matrix : matrix.clone();
@ -1706,20 +1706,19 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
// this item does not have children, since we'd have to travel up the
// chain already to determine the rough bounds.
var matrix = this._matrix,
parentTotalMatrix = options._totalMatrix,
view = this.getView(),
parentViewMatrix = options._viewMatrix,
// Keep the accumulated matrices up to this item in options, so we
// can keep calculating the correct _tolerancePadding values.
totalMatrix = options._totalMatrix = parentTotalMatrix
? parentTotalMatrix.chain(matrix)
viewMatrix = options._viewMatrix = parentViewMatrix
? parentViewMatrix.appended(matrix)
// If this is the first one in the recursion, factor in the
// zoom of the view and the globalMatrix of the item.
: this.getGlobalMatrix().preConcatenate(view._matrix),
: this.getGlobalMatrix().prepend(this.getView()._matrix),
// Calculate the transformed padding as 2D size that describes the
// transformed tolerance circle / ellipse. Make sure it's never 0
// since we're using it for division.
tolerancePadding = options._tolerancePadding = new Size(
Path._getPenPadding(1, totalMatrix.inverted())
Path._getStrokePadding(1, viewMatrix.inverted())
).multiply(
Math.max(options.tolerance, /*#=*/Numerical.TOLERANCE)
);
@ -1778,8 +1777,8 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
// Transform the point back to the outer coordinate system.
if (res && res.point)
res.point = matrix.transform(res.point);
// Restore totalMatrix for next child.
options._totalMatrix = parentTotalMatrix;
// Restore viewMatrix for next child.
options._viewMatrix = parentViewMatrix;
return res;
},
@ -1983,7 +1982,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
matrix = rect && (matrix || new Matrix());
for (var i = 0, l = children && children.length; i < l; i++) {
var child = children[i],
childMatrix = matrix && matrix.chain(child._matrix),
childMatrix = matrix && matrix.appended(child._matrix),
add = true;
if (rect) {
var bounds = child.getBounds(childMatrix);
@ -3137,9 +3136,9 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
// Bail out if there is nothing to do.
if (!matrix && !applyMatrix)
return this;
// Simply preconcatenate the internal matrix with the passed one:
// Simply prepend the internal matrix with the passed one:
if (matrix)
_matrix.preConcatenate(matrix);
_matrix.prepend(matrix);
// Call #_transformContent() 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
@ -3884,7 +3883,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
var matrices = param.matrices,
viewMatrix = param.viewMatrix,
matrix = this._matrix,
globalMatrix = matrices[matrices.length - 1].chain(matrix);
globalMatrix = matrices[matrices.length - 1].appended(matrix);
// If this item is not invertible, do not draw it. It appears to be a
// good idea generally to not draw in such circumstances, e.g. SVG
// handles it the same way.
@ -3893,10 +3892,10 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
// 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.
// prepend the view's matrix.
// Note that it's only provided if it isn't the identity matrix.
function getViewMatrix(matrix) {
return viewMatrix ? viewMatrix.chain(matrix) : matrix;
return viewMatrix ? viewMatrix.appended(matrix) : matrix;
}
// Only keep track of transformation if told so. See Project#draw()
@ -3952,7 +3951,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
ctx.save();
// Get the transformation matrix for non-scaling strokes.
var strokeMatrix = parentStrokeMatrix
? parentStrokeMatrix.chain(matrix)
? parentStrokeMatrix.appended(matrix)
// pass `true` for dontMerge
: this._canScaleStroke && !this.getStrokeScaling(true)
&& getViewMatrix(globalMatrix),
@ -4042,7 +4041,7 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
var layer,
color = this.getSelectedColor(true)
|| (layer = this.getLayer()) && layer.getSelectedColor(true),
mx = matrix.chain(this.getGlobalMatrix(true));
mx = matrix.appended(this.getGlobalMatrix(true));
ctx.strokeStyle = ctx.fillStyle = color
? color.toCanvasStyle(ctx) : '#009dec';
if (this._drawSelected)

View file

@ -112,7 +112,7 @@ var PlacedSymbol = Item.extend(/** @lends PlacedSymbol# */{
var definition = this.symbol._definition;
// Redirect the call to the symbol definition to calculate the bounds
return definition._getCachedBounds(getter,
matrix && matrix.chain(definition._matrix), cacheItem);
matrix && matrix.appended(definition._matrix), cacheItem);
},
_hitTestSelf: function(point, options) {

View file

@ -470,7 +470,7 @@ var Raster = Item.extend(/** @lends Raster# */{
raster = new Raster(Item.NO_INSERT);
raster.setImage(this.getSubCanvas(rect));
raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
raster._matrix.preConcatenate(this._matrix);
raster._matrix.prepend(this._matrix);
raster.insertAbove(this);
return raster;
},

View file

@ -268,7 +268,7 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
var child = children[i],
mx = child._matrix;
paths.push(child.getPathData(_matrix && !mx.isIdentity()
? _matrix.chain(mx) : _matrix, _precision));
? _matrix.appended(mx) : _matrix, _precision));
}
return paths.join(' ');
}
@ -313,7 +313,7 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
mx = child._matrix;
if (!selectedItems[child._id])
child._drawSelected(ctx, mx.isIdentity() ? matrix
: matrix.chain(mx));
: matrix.appended(mx));
}
}
},

View file

@ -1504,9 +1504,9 @@ var Path = PathItem.extend(/** @lends Path# */{
radius: radius,
insert: false
});
// Pass `true` to exclude the matrix, so we can preconcatenate after
// Pass `true` to exclude the matrix, so we can prepend after
shape.copyAttributes(this, true);
shape._matrix.preConcatenate(this._matrix);
shape._matrix.prepend(this._matrix);
// Determine and apply the shape's angle of rotation.
shape.rotate(topCenter.subtract(center).getAngle() + 90);
if (insert === undefined || insert)
@ -2701,7 +2701,7 @@ new function() { // PostScript-style drawing commands
this.join();
}
};
}, { // A dedicated scope for the tricky bounds calculations
}, { // A dedicated scope for the tricky bounds calculations
// We define all the different getBounds functions as static methods on Path
// and have #_getBounds directly access these. All static bounds functions
// below have the same first four parameters: segments, closed, style,
@ -2826,14 +2826,14 @@ statics: {
* stroke adds to the bounding box, by calculating the dimensions of a
* rotated ellipse.
*/
_getPenPadding: function(radius, matrix) {
_getStrokePadding: function(radius, matrix) {
if (!matrix)
return [radius, radius];
// If a matrix is provided, we need to rotate the stroke circle
// and calculate the bounding box of the resulting rotated elipse:
// Get rotated hor and ver vectors, and determine rotation angle
// and elipse values from them:
var mx = matrix.shiftless(),
var mx = matrix._shiftless(),
hor = mx.transform(new Point(radius, 0)),
ver = mx.transform(new Point(0, radius)),
phi = hor.getAngleInRadians(),
@ -2965,8 +2965,9 @@ statics: {
*/
getRoughBounds: function(segments, closed, style, matrix) {
// Delegate to handleBounds, but pass on radius values for stroke and
// joins. Hanlde miter joins specially, by passing the largets radius
// joins. Handle miter joins specially, by passing the largest radius
// possible.
// TODO: Take strokeScaling into account here too!
var strokeRadius = style.hasStroke() ? style.getStrokeWidth() / 2 : 0,
joinRadius = strokeRadius;
if (strokeRadius > 0) {
@ -2976,7 +2977,7 @@ statics: {
joinRadius = Math.max(joinRadius, strokeRadius * Math.sqrt(2));
}
return Path.getHandleBounds(segments, closed, style, matrix,
Path._getPenPadding(strokeRadius, matrix),
Path._getPenPadding(joinRadius, matrix));
Path._getStrokePadding(strokeRadius, matrix),
Path._getStrokePadding(joinRadius, matrix));
}
}});

View file

@ -49,7 +49,7 @@ new function() {
// in rotate(). To do so, SVG requries us to inverse transform the
// translation point by the matrix itself, since they are provided
// in local coordinates.
matrix = matrix.shiftless();
matrix = matrix._shiftless();
var point = matrix._inverseTransform(trans);
attrs[center ? 'cx' : 'x'] = point.x;
attrs[center ? 'cy' : 'y'] = point.y;

View file

@ -338,7 +338,7 @@ new function() {
v[j] = parseFloat(v[j]);
switch (command) {
case 'matrix':
matrix.concatenate(
matrix.append(
new Matrix(v[0], v[1], v[2], v[3], v[4], v[5]));
break;
case 'rotate':
@ -461,7 +461,7 @@ new function() {
// symbol.
var scale = size ? rect.getSize().divide(size) : 1,
matrix = new Matrix().translate(rect.getPoint()).scale(scale);
item.transform(matrix.inverted());
item.transform(matrix.invert());
} else if (item instanceof Symbol) {
// The symbol is wrapping a group. Note that viewBox was already
// applied to the group, and above code was executed for it.

View file

@ -536,7 +536,7 @@ var View = Base.extend(Emitter, /** @lends View# */{
* @param {Matrix} matrix the matrix by which the view shall be transformed
*/
transform: function(matrix) {
this._matrix.concatenate(matrix);
this._matrix.append(matrix);
},
/**