From 32f04f6162b904bbfea08859ec077546798897ae Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 11:54:26 +0200 Subject: [PATCH 1/8] Document: implement selectAll() and deselectAll(). --- src/document/Document.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/document/Document.js b/src/document/Document.js index f2e975d4..3dd00a0a 100644 --- a/src/document/Document.js +++ b/src/document/Document.js @@ -80,6 +80,26 @@ var Document = this.Document = Base.extend({ } }, + /** + * Selects all items in the document. + */ + selectAll: function() { + // TODO: is using for var i in good practice? + // or should we use Base.each? (JP) + for (var i = 0, l = this.layers.length; i < l; i++) + this.layers[i].setSelected(true); + }, + + /** + * Deselects all selected items in the document. + */ + deselectAll: function() { + // TODO: is using for var i in good practice? + // or should we use Base.each? (JP) + for (var i in this._selectedItems) + this._selectedItems[i].setSelected(false); + }, + draw: function() { if (this.canvas) { var ctx = this.context; From eed054af179ad28ec5de011c9231b0f318c2de61 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 12:33:03 +0200 Subject: [PATCH 2/8] Item: implement getStrokeBounds, move getBounds functionality into _getBounds with an includeStroke flag. Also simplify the _getBounds code. --- src/item/Item.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/item/Item.js b/src/item/Item.js index 913715a0..396dcbe5 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -428,20 +428,29 @@ var Item = this.Item = Base.extend({ } return false; }, - + + getStrokeBounds: function() { + return this._getBounds(true); + }, + getBounds: function() { - if (this.children && this.children.length) { - var rect = this.children[0].getBounds(), - x1 = rect.x, - y1 = rect.y, - x2 = rect.x + rect.width, - y2 = rect.y + rect.height; - for (var i = 1, l = this.children.length; i < l; i++) { - rect = this.children[i].getBounds(); + return this._getBounds(false); + }, + + _getBounds: function(includeStroke) { + var children = this.children; + if (children && children.length) { + var x1, x2; + var y1 = x1 = Infinity; + var y2 = x2 = -Infinity; + for (var i = 0, l = children.length; i < l; i++) { + var child = this.children[i], + rect = includeStroke ? child.getStrokeBounds() + : child.getBounds(); x1 = Math.min(rect.x, x1); y1 = Math.min(rect.y, y1); - x2 = Math.max(rect.x + rect.width, x1 + x2 - x1); - y2 = Math.max(rect.y + rect.height, y1 + y2 - y1); + x2 = Math.max(rect.x + rect.width, x2); + y2 = Math.max(rect.y + rect.height, y2); } return LinkedRectangle.create(this, 'setBounds', x1, y1, x2 - x1, y2 - y1); From 1666a362109b70b1e940fd3af30b577bac125684 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 12:38:57 +0200 Subject: [PATCH 3/8] Have Item#_getBounds return a normal rectangle when called with includeStroke flag, since Item#getStrokeBounds is read only. --- src/item/Item.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/item/Item.js b/src/item/Item.js index 396dcbe5..7a9a2746 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -445,15 +445,18 @@ var Item = this.Item = Base.extend({ var y2 = x2 = -Infinity; for (var i = 0, l = children.length; i < l; i++) { var child = this.children[i], - rect = includeStroke ? child.getStrokeBounds() + rect = includeStroke + ? child.getStrokeBounds() : child.getBounds(); x1 = Math.min(rect.x, x1); y1 = Math.min(rect.y, y1); x2 = Math.max(rect.x + rect.width, x2); y2 = Math.max(rect.y + rect.height, y2); } - return LinkedRectangle.create(this, 'setBounds', - x1, y1, x2 - x1, y2 - y1); + return includeStroke + ? Rectangle.create(x1, y1, x2 - x1, y2 - y1), + : LinkedRectangle.create(this, 'setBounds', + x1, y1, x2 - x1, y2 - y1); } // TODO: What to return if nothing is defined, e.g. empty Groups? // Scriptographer behaves weirdly then too. From 861812f3ad48bbbeb5ba83014b365ba90c69d475 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 12:39:55 +0200 Subject: [PATCH 4/8] Fix bug in previous commit. --- src/item/Item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/Item.js b/src/item/Item.js index 7a9a2746..7e4e05fe 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -454,7 +454,7 @@ var Item = this.Item = Base.extend({ y2 = Math.max(rect.y + rect.height, y2); } return includeStroke - ? Rectangle.create(x1, y1, x2 - x1, y2 - y1), + ? Rectangle.create(x1, y1, x2 - x1, y2 - y1) : LinkedRectangle.create(this, 'setBounds', x1, y1, x2 - x1, y2 - y1); } From e1f5d4952dc23871732987e61f87f4633575b2c8 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 12:50:53 +0200 Subject: [PATCH 5/8] Minor cleanups and fixes in Item. --- src/item/Item.js | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/item/Item.js b/src/item/Item.js index 7e4e05fe..a3054248 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -221,28 +221,28 @@ var Item = this.Item = Base.extend({ * The first item contained within this item. */ getFirstChild: function() { - return this.children ? this.children[0] : null; + return this.children && this.children[0] || null; }, /** * The last item contained within this item. */ getLastChild: function() { - return this.children ? this.children[this.children.length - 1] : null; + return this.children && this.children[this.children.length - 1] || null; }, /** * The next item on the same level as this item. */ getNextSibling: function() { - return this.parent ? this.parent.children[this.getIndex() + 1] : null; + return this.parent && this.parent.children[this.getIndex() + 1] || null; }, /** * The previous item on the same level as this item. */ getPreviousSibling: function() { - return this.parent ? this.parent.children[this.getIndex() - 1] : null; + return this.parent && this.parent.children[this.getIndex() - 1] || null; }, /** @@ -252,8 +252,7 @@ var Item = this.Item = Base.extend({ // TODO: Relying on indexOf() here is slow, especially since it is // used for getPrevious/NextSibling(). // We need linked lists instead. - // TODO: Return null instead of -1? - return this.parent ? this.parent.children.indexOf(this) : -1; + return this.parent && this.parent.children.indexOf(this) || null; }, /** @@ -272,7 +271,7 @@ var Item = this.Item = Base.extend({ * Removes the item. */ remove: function() { - if(this.isSelected()) + if (this.isSelected()) this.setSelected(false); return this.removeFromParent(); }, @@ -444,7 +443,7 @@ var Item = this.Item = Base.extend({ var y1 = x1 = Infinity; var y2 = x2 = -Infinity; for (var i = 0, l = children.length; i < l; i++) { - var child = this.children[i], + var child = children[i], rect = includeStroke ? child.getStrokeBounds() : child.getBounds(); @@ -506,11 +505,11 @@ var Item = this.Item = Base.extend({ // weird results on Scriptographer. Also we can't use antialiasing, since // Canvas doesn't support it yet. Document colorMode is also out of the // question for now. - var bounds = this.getStrokeBounds(); - var scale = resolution ? resolution / 72 : 1; - var canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)); - var ctx = canvas.getContext('2d'); - var matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); + var bounds = this.getStrokeBounds(), + scale = (resolution || 72) / 72, + canvas = CanvasProvider.getCanvas(bounds.getSize().multiply(scale)), + ctx = canvas.getContext('2d'), + matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y); matrix.applyToContext(ctx); this.draw(ctx, {}); var raster = new Raster(canvas); @@ -732,8 +731,8 @@ var Item = this.Item = Base.extend({ // Floor the offset and ceil the size, so we don't cut off any // antialiased pixels when drawing onto the temporary canvas. - var itemOffset = bounds.getTopLeft().floor(); - var size = bounds.getSize().ceil().add(1, 1); + var itemOffset = bounds.getTopLeft().floor(), + size = bounds.getSize().ceil().add(1, 1); tempCanvas = CanvasProvider.getCanvas(size); // Save the parent context, so we can draw onto it later @@ -906,7 +905,7 @@ var Item = this.Item = Base.extend({ for(var id in set) { var item = set[id]; item.remove(); - for(var type in sets) { + for (var type in sets) { var other = sets[type]; if (other != set && other[item.getId()]) delete other[item.getId()]; From ad8dfe91c7e885a24a2331c0023215f1dd93ced4 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 12:56:08 +0200 Subject: [PATCH 6/8] Implement Raster#getStrokeBounds(). --- src/item/Raster.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/item/Raster.js b/src/item/Raster.js index 4c230884..c85b4ee9 100644 --- a/src/item/Raster.js +++ b/src/item/Raster.js @@ -198,6 +198,10 @@ var Raster = this.Raster = Item.extend({ } return this._bounds; }, + + getStrokeBounds: function() { + return this.getBounds(); + }, draw: function(ctx, param) { if (param.selection) { From c67128ca7635e5920e8f948c58462ca0d2d67730 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 12:56:30 +0200 Subject: [PATCH 7/8] Add tests for strokeBounds of groups. --- test/tests/Group.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/tests/Group.js b/test/tests/Group.js index 8cdd6af0..261d5510 100644 --- a/test/tests/Group.js +++ b/test/tests/Group.js @@ -16,12 +16,20 @@ test('new Group([item])', function() { test('Group bounds', function() { var doc = new Document(); + doc.currentStyle = { + strokeWidth: 5, + strokeColor: 'black' + }; var path = new Path.Circle([150, 150], 60); var secondPath = new Path.Circle([175, 175], 85); var group = new Group([path, secondPath]); compareRectangles(group.bounds, { x: 90, y: 90, width: 170, height: 170 }); + compareRectangles(group.strokeBounds, { x: 87.5, y: 87.5, width: 175, height: 175 }); + group.rotate(20); compareRectangles(group.bounds, { x: 89.97681, y: 82.94095, width: 170.04639, height: 177.08224 }); + compareRectangles(group.strokeBounds, { x: 87.47681, y: 80.44095, width: 175.04639, height: 182.08224 }); group.rotate(20, new Point(50, 50)); compareRectangles(group.bounds, { x: 39.70692, y: 114.99196, width: 170.00412, height: 180.22401 }); + compareRectangles(group.strokeBounds, { x: 37.20692, y: 112.49196, width: 175.00412, height: 185.22401 }); }); \ No newline at end of file From e0b5dbad21d9385574d5b9df80239813146b7588 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Thu, 28 Apr 2011 13:45:55 +0200 Subject: [PATCH 8/8] Rectangle: compress unite, intersect and include. --- src/basic/Rectangle.js | 48 ++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/basic/Rectangle.js b/src/basic/Rectangle.js index 4af4ea14..a11672fc 100644 --- a/src/basic/Rectangle.js +++ b/src/basic/Rectangle.js @@ -193,33 +193,6 @@ var Rectangle = this.Rectangle = Base.extend({ && rect.y < this.y + this.height; }, - intersect: function(rect) { - rect = Rectangle.read(arguments); - var x1 = Math.max(this.x, rect.x), - y1 = Math.max(this.y, rect.y), - x2 = Math.min(this.x + this.width, rect.x + rect.width), - y2 = Math.min(this.y + this.height, rect.y + rect.height); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - unite: function(rect) { - rect = Rectangle.read(arguments); - var x1 = Math.min(this.x, rect.x), - y1 = Math.min(this.y, rect.y), - x2 = Math.max(this.x + this.width, rect.x + rect.width), - y2 = Math.max(this.y + this.height, rect.y + rect.height); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - - include: function(point) { - point = Point.read(arguments); - var x1 = Math.min(this.x, point.x), - y1 = Math.min(this.y, point.y), - x2 = Math.max(this.x + this.width, point.x), - y2 = Math.max(this.y + this.height, point.y); - return Rectangle.create(x1, y1, x2 - x1, y2 - y1); - }, - toString: function() { return '{ x: ' + this.x + ', y: ' + this.y @@ -234,6 +207,27 @@ var Rectangle = this.Rectangle = Base.extend({ return new Rectangle(Rectangle.dont).set(x, y, width, height); } } +}, new function() { // Scope for injecting intersect, unite and include. + return Base.each({ + intersect: [true, false], + unite: [false, false], + include: [false, true] + }, function(values, name) { + var intersect = values[0], + isPoint = values[1], + math1 = Math[intersect ? 'max' : 'min'], + math2 = Math[intersect ? 'min' : 'max']; + this[name] = function() { + var object = (isPoint ? Point : Rectangle).read(arguments), + x1 = math1(this.x, object.x), + y1 = math1(this.y, object.y), + x2 = math2(this.x + this.width, + object.x + (isPoint ? 0 : object.width)), + y2 = math2(this.y + this.height, + object.y + (isPoint ? 0 : object.height)); + return Rectangle.create(x1, y1, x2 - x1, y2 - y1); + }; + }, {}); }, new function() { return Base.each([ ['Top', 'Left'], ['Top', 'Right'],