From 7c2e57e1059acbcc1eb3ab5b5c2f07a3772ddda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrg=20Lehni?= Date: Mon, 13 May 2013 18:57:17 -0700 Subject: [PATCH] Remove Item#applyMatrix boolean, go back to previous way of directly applying transformations to children in Group and Layer, and introduce new Clip class for non-transformed nested matrices. --- examples/Games/Paperoids.html | 1 - examples/Paperjs.org/BouncingBalls.html | 24 +++---- examples/Paperjs.org/InteractiveTiger.html | 1 - examples/Paperjs.org/NyanRainbow.html | 1 - examples/Paperjs.org/PathIntersections.html | 4 -- examples/Paperjs.org/Qbertify.html | 1 - examples/Rasters/PhyllotaxisRaster.html | 1 - src/item/Clip.js | 26 ++++++++ src/item/Item.js | 70 ++++++++++----------- src/item/PlacedSymbol.js | 1 + src/item/Raster.js | 1 + src/item/Shape.js | 1 + src/paper.js | 1 + src/path/Path.js | 4 +- src/path/PathItem.js | 3 - src/style/Style.js | 5 +- test/tests/Item_Bounds.js | 2 +- 17 files changed, 83 insertions(+), 64 deletions(-) create mode 100644 src/item/Clip.js diff --git a/examples/Games/Paperoids.html b/examples/Games/Paperoids.html index 6d81acd0..5d5b382e 100644 --- a/examples/Games/Paperoids.html +++ b/examples/Games/Paperoids.html @@ -125,7 +125,6 @@ path.closed = true; var thrust = new Path([-8, -4], [-14, 0], [-8, 4]); var group = new Group(path, thrust); - group.applyMatrix = true; group.position = view.bounds.center; return { item: group, diff --git a/examples/Paperjs.org/BouncingBalls.html b/examples/Paperjs.org/BouncingBalls.html index bd338c9b..b1d9d38e 100644 --- a/examples/Paperjs.org/BouncingBalls.html +++ b/examples/Paperjs.org/BouncingBalls.html @@ -27,21 +27,21 @@ var radius = this.radius = 50 * Math.random() + 30; // Wrap CompoundPath in a Group, since CompoundPaths directly // applies the transformations to the content, just like Path. - this.item = new Group({ + var ball = new CompoundPath({ children: [ - new CompoundPath({ - children: [ - new Path.Circle({ - radius: radius - }), - new Path.Circle({ - center: radius / 8, - radius: radius / 3 - }) - ], - fillColor: new Color(gradient, 0, radius, radius / 8), + new Path.Circle({ + radius: radius + }), + new Path.Circle({ + center: radius / 8, + radius: radius / 3 }) ], + fillColor: new Color(gradient, 0, radius, radius / 8), + }); + + this.item = new Clip({ + children: [ball], position: this.point }); } diff --git a/examples/Paperjs.org/InteractiveTiger.html b/examples/Paperjs.org/InteractiveTiger.html index 5da0690f..05572ad8 100644 --- a/examples/Paperjs.org/InteractiveTiger.html +++ b/examples/Paperjs.org/InteractiveTiger.html @@ -9,7 +9,6 @@ project.importSVG(document.getElementById('svg')); // Resize the tiger to fit within the window: - project.activeLayer.applyMatrix = true; project.activeLayer.fitBounds(view.bounds); var items = project.activeLayer.firstChild.children; diff --git a/examples/Paperjs.org/NyanRainbow.html b/examples/Paperjs.org/NyanRainbow.html index 6a0f69f6..fc7f5775 100644 --- a/examples/Paperjs.org/NyanRainbow.html +++ b/examples/Paperjs.org/NyanRainbow.html @@ -123,7 +123,6 @@ var count = 30; var group = new Group(paths); - group.applyMatrix = true; var headGroup; var eyePosition = new Point(); var eyeFollow = (Point.random() - 0.5); diff --git a/examples/Paperjs.org/PathIntersections.html b/examples/Paperjs.org/PathIntersections.html index 48c76c9f..8a98c6f6 100644 --- a/examples/Paperjs.org/PathIntersections.html +++ b/examples/Paperjs.org/PathIntersections.html @@ -12,10 +12,6 @@ var yesGroup = words.children.yes; var noGroup = words.children.no; - project.activeLayer.applyMatrix = true; - noGroup.applyMatrix = true; - yesGroup.applyMatrix = true; - // Resize the words to fit snugly inside the view: project.activeLayer.fitBounds(view.bounds); project.activeLayer.scale(0.8); diff --git a/examples/Paperjs.org/Qbertify.html b/examples/Paperjs.org/Qbertify.html index 362c202f..fcad642b 100644 --- a/examples/Paperjs.org/Qbertify.html +++ b/examples/Paperjs.org/Qbertify.html @@ -69,7 +69,6 @@ // Transform the raster, so it fills the view: raster.fitBounds(view.bounds, true); group = new Group(); - group.applyMatrix = true; for (var y = 0; y < values.amount; y++) { for (var x = 0; x < values.amount; x++) { var copy = piece.clone(); diff --git a/examples/Rasters/PhyllotaxisRaster.html b/examples/Rasters/PhyllotaxisRaster.html index 568886d3..6fdc638d 100644 --- a/examples/Rasters/PhyllotaxisRaster.html +++ b/examples/Rasters/PhyllotaxisRaster.html @@ -28,7 +28,6 @@ // Create the group of circle shaped paths and scale it up a bit: var group = createPhyllotaxis(values.amount); - group.applyMatrix = true; group.scale(3); function createPhyllotaxis(amount) { diff --git a/src/item/Clip.js b/src/item/Clip.js new file mode 100644 index 00000000..a311f223 --- /dev/null +++ b/src/item/Clip.js @@ -0,0 +1,26 @@ +/* + * Paper.js - The Swiss Army Knife of Vector Graphics Scripting. + * http://paperjs.org/ + * + * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey + * http://lehni.org/ & http://jonathanpuckey.com/ + * + * Distributed under the MIT license. See LICENSE file for details. + * + * All rights reserved. + */ + +/** + * @name Clip + * + * @class A Clip is a collection of items, similar to a Group. But instead of + * automatically passing on transformations to its children by calling + * {@link Item#applyMatrix()}, the transformations are stored in the internal + * matrix. + * + * @extends Group + */ +var Clip = this.Clip = Group.extend(/** @lends Clip# */{ + _class: 'Clip', + _applyMatrix: false +}); diff --git a/src/item/Item.js b/src/item/Item.js index c975b897..4b494781 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -37,6 +37,9 @@ var Item = this.Item = Base.extend(Callback, { } } }, /** @lends Item# */{ + // All items apply their matrix by default. + // Exceptions are Raster, PlacedSymbol, Clip and Shape. + _applyMatrix: true, _boundsSelected: false, // Provide information about fields to be serialized, with their defaults // that can be ommited. @@ -508,16 +511,6 @@ var Item = this.Item = Base.extend(Callback, { */ _guide: false, - /** - * Specifies whether the item directly transforms its contents when - * transformations are applied to it, or wether it simply stores them in - * {@link Item#matrix}. - * - * @type Boolean - * @default false - */ - applyMatrix: false, - /** * Specifies whether an item is selected and will also return {@code true} * if the item is partially selected (groups with some selected or partially @@ -1203,7 +1196,7 @@ var Item = this.Item = Base.extend(Callback, { // TODO: Consider moving this to Base once it's useful in more than one // place var keys = ['_locked', '_visible', '_blendMode', '_opacity', - '_clipMask', '_guide', 'applyMatrix']; + '_clipMask', '_guide']; for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; if (this.hasOwnProperty(key)) @@ -2193,26 +2186,10 @@ var Item = this.Item = Base.extend(Callback, { position = this._position; // Simply preconcatenate the internal matrix with the passed one: this._matrix.preConcatenate(matrix); - if (this._transform) - this._transform(matrix); - // If we need to directly apply the accumulated transformations, call - // #_applyMatrix() with the internal _matrix, and set it to the identity - // transformation if it was possible to apply it. Application is not - // possible on Raster, PointText, PlacedSymbol, since the matrix is - // storing the actual location / transformation state. - if ((this.applyMatrix || arguments[1]) - && this._applyMatrix(this._matrix)) { - // When the matrix could be applied, we also need to transform - // color styles with matrices (only gradients so far): - var style = this._style, - fillColor = style.getFillColor(), - strokeColor = style.getStrokeColor(); - if (fillColor) - fillColor.transform(this._matrix); - if (strokeColor) - strokeColor.transform(this._matrix); - this._matrix.reset(); - } + // Call applyMatrix if we need to directly apply the accumulated + // transformations to the item's content. + if (this._applyMatrix || arguments[1]) + this.applyMatrix(false); // We always need to call _changed since we're caching bounds on all // items, including Group. this._changed(/*#=*/ Change.GEOMETRY); @@ -2242,16 +2219,39 @@ var Item = this.Item = Base.extend(Callback, { return this; }, - _applyMatrix: function(matrix) { - // Pass on the transformation to the children, and apply it there too, - // by passing true for the 2nd hidden parameter. + _transformContent: function(matrix, applyMatrix) { if (this._children) { for (var i = 0, l = this._children.length; i < l; i++) - this._children[i].transform(matrix, true); + this._children[i].transform(matrix, applyMatrix); return true; } }, + applyMatrix: function(_dontNotify) { + // Call #_transformContent() 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. + if (this._transformContent(this._matrix, true)) { + // When the matrix could be applied, we also need to transform + // color styles with matrices (only gradients so far): + var style = this._style, + fillColor = style.getFillColor(true), + strokeColor = style.getStrokeColor(true); + if (fillColor) + fillColor.transform(this._matrix); + if (strokeColor) + strokeColor.transform(this._matrix); + // Reset the internal matrix to the identity transformation if it + // was possible to apply it. + this._matrix.reset(); + } + if (!_dontNotify) + this._changed(/*#=*/ Change.GEOMETRY); + }, + /** * Transform the item so that its {@link #bounds} fit within the specified * rectangle, without changing its aspect ratio. diff --git a/src/item/PlacedSymbol.js b/src/item/PlacedSymbol.js index 30326970..81def804 100644 --- a/src/item/PlacedSymbol.js +++ b/src/item/PlacedSymbol.js @@ -20,6 +20,7 @@ */ var PlacedSymbol = this.PlacedSymbol = Item.extend(/** @lends PlacedSymbol# */{ _class: 'PlacedSymbol', + _applyMatrix: false, // PlacedSymbol uses strokeBounds for bounds _boundsGetter: { getBounds: 'getStrokeBounds' }, _boundsSelected: true, diff --git a/src/item/Raster.js b/src/item/Raster.js index 3cc8082e..5899222f 100644 --- a/src/item/Raster.js +++ b/src/item/Raster.js @@ -19,6 +19,7 @@ */ var Raster = this.Raster = Item.extend(/** @lends Raster# */{ _class: 'Raster', + _applyMatrix: false, // Raster doesn't make the distinction between the different bounds, // so use the same name for all of them _boundsGetter: 'getBounds', diff --git a/src/item/Shape.js b/src/item/Shape.js index ae2bda56..3a11335f 100644 --- a/src/item/Shape.js +++ b/src/item/Shape.js @@ -19,6 +19,7 @@ */ var Shape = this.Shape = Item.extend(/** @lends Shape# */{ _class: 'Shape', + _applyMatrix: false, initialize: function(type, point, size) { this.base(point); diff --git a/src/paper.js b/src/paper.js index 26c10b4a..964f64fc 100644 --- a/src/paper.js +++ b/src/paper.js @@ -67,6 +67,7 @@ var paper = new function() { /*#*/ include('item/Item.js'); /*#*/ include('item/Group.js'); /*#*/ include('item/Layer.js'); +/*#*/ include('item/Clip.js'); /*#*/ include('item/Shape.js'); /*#*/ include('item/Raster.js'); /*#*/ include('item/PlacedSymbol.js'); diff --git a/src/path/Path.js b/src/path/Path.js index 106905be..de5afa2c 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -300,7 +300,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{ return true; }, - _applyMatrix: function(matrix) { + _transformContent: function(matrix) { var coords = new Array(6); for (var i = 0, l = this._segments.length; i < l; i++) this._segments[i]._transformCoordinates(matrix, coords, true); @@ -1539,7 +1539,7 @@ var Path = this.Path = PathItem.extend(/** @lends Path# */{ * the specified point */ getNearestLocation: function(point) { - point = this._matrix.inverseTransform(Point.read(arguments)); + point = Point.read(arguments); var curves = this.getCurves(), minDist = Infinity, minLoc = null; diff --git a/src/path/PathItem.js b/src/path/PathItem.js index 2568de87..734e86fa 100644 --- a/src/path/PathItem.js +++ b/src/path/PathItem.js @@ -20,9 +20,6 @@ * @extends Item */ var PathItem = this.PathItem = Item.extend(/** @lends PathItem# */{ - // All PathItems directly apply transformations by default. - applyMatrix: true, - /** * Returns all intersections between two {@link PathItem} items as an array * of {@link CurveLocation} objects. {@link CompoundPath} items are also diff --git a/src/style/Style.js b/src/style/Style.js index 44f821ce..b965dbb8 100644 --- a/src/style/Style.js +++ b/src/style/Style.js @@ -151,12 +151,13 @@ var Style = this.Style = Base.extend(new function() { } }; - fields[get] = function() { + fields[get] = function(/* dontMerge */) { var value, children = this._item && this._item._children; // If this item has children, walk through all of them and see if // they all have the same style. - if (!children || children.length === 0 + // If true is passed for dontMerge, don't merge children styles + if (!children || children.length === 0 || arguments[0] || this._item._type === 'compound-path') { var value = this._values[key]; if (value === undefined) { diff --git a/test/tests/Item_Bounds.js b/test/tests/Item_Bounds.js index 8978dbf8..920acdeb 100644 --- a/test/tests/Item_Bounds.js +++ b/test/tests/Item_Bounds.js @@ -53,7 +53,7 @@ test('path.bounds when contained in a transformed group', function() { var group = new Group([path]); compareRectangles(path.bounds, { x: 10, y: 10, width: 50, height: 50 }, 'path.bounds before group translation'); group.translate(100, 100); - compareRectangles(path.bounds, { x: 10, y: 10, width: 50, height: 50 }, 'path.bounds after group translation'); + compareRectangles(path.bounds, { x: 110, y: 110, width: 50, height: 50 }, 'path.bounds after group translation'); }); test('text.bounds', function() {