From be9f312110d05c0b4fab5e61caf8c7962ec6cb19 Mon Sep 17 00:00:00 2001 From: Jonathan Puckey Date: Sun, 17 Apr 2011 18:46:35 +0200 Subject: [PATCH] Implement a first version of drawing path selection. --- src/document/Document.js | 25 +++++- src/item/Item.js | 41 +++++++-- src/path/Path.js | 180 ++++++++++++++++++++++++++------------- 3 files changed, 176 insertions(+), 70 deletions(-) diff --git a/src/document/Document.js b/src/document/Document.js index bf8ea734..74285f2b 100644 --- a/src/document/Document.js +++ b/src/document/Document.js @@ -55,7 +55,18 @@ var Document = this.Document = Base.extend({ } return false; }, - + + getSelectionContext: function(param) { + var context = this._selectionContext; + if (!context) { + var canvas = CanvasProvider.getCanvas(this.size); + context = this._selectionContext = canvas.getContext('2d'); + context.strokeWidth = 1; + } + context.strokeStyle = context.fillStyle = param.layerColor; + return context; + }, + draw: function() { if (this.canvas) { // Initial tests conclude that clearing the canvas using clearRect @@ -66,9 +77,21 @@ var Document = this.Document = Base.extend({ this.context.save(); var param = { offset: new Point(0, 0) }; for (var i = 0, l = this.layers.length; i < l; i++) { + // TODO: use Layer#color: + param.layerColor = '#4f7aff'; Item.draw(this.layers[i], this.context, param); } this.context.restore(); + + // If, during drawing, one of the paths was selected, there will + // be a selectionContext which needs to be composited onto the + // canvas: + if (this._selectionContext) { + var canvas = this._selectionContext.canvas; + this.context.drawImage(canvas, 0, 0); + CanvasProvider.returnCanvas(canvas); + this._selectionContext = null; + } } }, diff --git a/src/item/Item.js b/src/item/Item.js index 3f7eaeb4..8d5f22f4 100644 --- a/src/item/Item.js +++ b/src/item/Item.js @@ -53,7 +53,29 @@ var Item = this.Item = Base.extend({ return copy; }, - // TODO: isSelected / setSelected + setSelected: function(selected) { + if (this._children) { + for (var i = 0, l = this.children.length; i < l; i++) { + var child = this._children[i]; + child.setSelected(selected); + } + } else { + this._selected = selected; + } + }, + + getSelected: function() { + if (this._children) { + for (var i = 0, l = this._children.length; i < l; i++) { + var child = this._children[i]; + if (child.getSelected()) + return true; + } + } else { + return !!this._selected; + } + }, + // TODO: isFullySelected / setFullySelected /** @@ -650,11 +672,14 @@ var Item = this.Item = Base.extend({ // on the temporary canvas. context.translate(-itemOffset.x, -itemOffset.y); } - - item.draw(context, { - offset: itemOffset || param.offset, - compound: param.compound - }); + var savedOffset; + if (itemOffset) { + savedOffset = param.offset; + param.offset = itemOffset; + } + item.draw(context, param); + if (itemOffset) + param.offset = savedOffset; // If we created a temporary canvas before, composite it onto the // parent canvas: @@ -807,7 +832,7 @@ var Item = this.Item = Base.extend({ item.remove(); for(var type in sets) { var other = sets[type]; - if(other != set && other[item.getId()]) + if (other != set && other[item.getId()]) delete other[item.getId()]; } } @@ -827,7 +852,7 @@ var Item = this.Item = Base.extend({ removeAll(sets[name]); sets[name] = {}; // Call the script's overridden handler, if defined - if(this.base) + if (this.base) this.base(event); } paper.tool.inject(hash); diff --git a/src/path/Path.js b/src/path/Path.js index 81d21ffe..952a40ee 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -99,13 +99,13 @@ var Path = this.Path = PathItem.extend({ return segment; }, - add: function() { - var segment = Segment.read(arguments); + add: function(segment) { + segment = Segment.read(arguments); return segment ? this._add(segment) : null; }, insert: function(index, segment) { - var segment = Segment.read(arguments, 1); + segment = Segment.read(arguments, 1); return segment ? this._add(segment, index) : null; }, @@ -248,69 +248,127 @@ var Path = this.Path = PathItem.extend({ return loc ? loc.getCurve().getNormal(loc.getParameter()) : null; - }, - - draw: function(ctx, param) { - if (!param.compound) - ctx.beginPath(); - var segments = this._segments, - length = segments.length, - handleOut, outX, outY; - for (var i = 0; i < length; i++) { + } +}, new function() { // Scope for drawing + + function drawHandles(ctx, segments) { + for (var i = 0, l = segments.length; i < l; i++) { var segment = segments[i], - point = segment._point, - x = point.x, - y = point.y, - handleIn = segment._handleIn; - if (i == 0) { - ctx.moveTo(x, y); - } else { - if (handleIn.isZero() && handleOut.isZero()) { - ctx.lineTo(x, y); - } else { - ctx.bezierCurveTo( - outX, outY, - handleIn.x + x, handleIn.y + y, - x, y - ); - } - } - handleOut = segment._handleOut; - outX = handleOut.x + x; - outY = handleOut.y + y; - } - if (this.closed && length > 1) { - var segment = segments[0], - point = segment._point, - x = point.x, - y = point.y, - handleIn = segment._handleIn; - ctx.bezierCurveTo(outX, outY, handleIn.x + x, handleIn.y + y, x, y); - ctx.closePath(); - } - // If the path is part of a compound path or doesn't have a fill or - // stroke, there is no need to continue. - var fillColor = this.getFillColor(), - strokeColor = this.getStrokeColor(); - if (!param.compound && (fillColor || strokeColor)) { - this.setContextStyles(ctx); + handleIn = segment.handleIn, + handleOut = segment.handleOut, + point = segment.point, + rounded = point.round(); + // TODO: draw handles depending on selection state of + // segment.point and neighbouring segments. + drawHandle(ctx, point, handleIn); + drawHandle(ctx, point, handleOut); + // Draw a rectangle at segment.point: ctx.save(); - // If the path only defines a strokeColor or a fillColor, - // draw it directly with the globalAlpha set, otherwise - // we will do it later when we composite the temporary canvas. - if (!fillColor || !strokeColor) - ctx.globalAlpha = this.opacity; - if (fillColor) { - ctx.fillStyle = fillColor.getCanvasStyle(ctx); - ctx.fill(); - } - if (strokeColor) { - ctx.strokeStyle = strokeColor.getCanvasStyle(ctx); - ctx.stroke(); - } + ctx.beginPath(); + ctx.rect(rounded.x - 2, rounded.y - 2, 4, 4); + ctx.fill(); + // TODO: Only draw white rectangle if point.isSelected() + // is false: + ctx.beginPath(); + ctx.rect(rounded.x - 1, rounded.y - 1, 2, 2); + ctx.fillStyle = '#ffffff'; + ctx.fill(); ctx.restore(); } } + + function drawHandle(ctx, point, handle) { + if (!handle.isZero()) { + handle = handle.add(point); + ctx.beginPath(); + ctx.moveTo(point.x, point.y); + ctx.lineTo(handle.x, handle.y); + ctx.stroke(); + ctx.beginPath(); + var rounded = handle.round(); + ctx.rect(rounded.x - 1, rounded.y - 1, 2, 2); + ctx.stroke(); + } + } + + return { + draw: function(ctx, param) { + if (!param.compound) + ctx.beginPath(); + var segments = this._segments, + length = segments.length, + handleOut, outX, outY; + for (var i = 0; i < length; i++) { + var segment = segments[i], + point = segment._point, + x = point.x, + y = point.y, + handleIn = segment._handleIn; + if (i == 0) { + ctx.moveTo(x, y); + } else { + if (handleIn.isZero() && handleOut.isZero()) { + ctx.lineTo(x, y); + } else { + ctx.bezierCurveTo( + outX, outY, + handleIn.x + x, handleIn.y + y, + x, y + ); + } + } + handleOut = segment._handleOut; + outX = handleOut.x + x; + outY = handleOut.y + y; + } + if (this.closed && length > 1) { + var segment = segments[0], + point = segment._point, + x = point.x, + y = point.y, + handleIn = segment._handleIn; + ctx.bezierCurveTo(outX, outY, handleIn.x + x, handleIn.y + y, x, y); + ctx.closePath(); + } + // If the path is part of a compound path or doesn't have a fill or + // stroke, there is no need to continue. + var fillColor = this.getFillColor(), + strokeColor = this.getStrokeColor(); + // If we are drawing onto the selection canvas, stroke the + // path and draw its handles. + if (param.selection) { + ctx.stroke(); + drawHandles(ctx, this.segments); + } else { + if (!param.compound && (fillColor || strokeColor)) { + this.setContextStyles(ctx); + ctx.save(); + // If the path only defines a strokeColor or a fillColor, + // draw it directly with the globalAlpha set, otherwise + // we will do it later when we composite the temporary canvas. + if (!fillColor || !strokeColor) + ctx.globalAlpha = this.opacity; + if (fillColor) { + ctx.fillStyle = fillColor.getCanvasStyle(ctx); + ctx.fill(); + } + if (strokeColor) { + ctx.strokeStyle = strokeColor.getCanvasStyle(ctx); + ctx.stroke(); + } + ctx.restore(); + } + // If the path is selected, draw it again on the separate + // selection canvas, which will be composited onto the canvas + // after drawing of the document is complete. + if (this.getSelected()) { + param.selection = true; + this.draw(this.document.getSelectionContext(param), param); + param.selection = false; + } + } + } + }; }, new function() { // Scope for segments list change detection var segmentsFields = Base.each(