mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-01 02:38:43 -05:00
Merge remote-tracking branch 'origin/master'
Conflicts: src/basic/Matrix.js src/item/Raster.js
This commit is contained in:
commit
3f972be500
13 changed files with 263 additions and 96 deletions
|
@ -458,10 +458,18 @@ var Matrix = Base.extend({
|
|||
// Canvas contexts seem to use another orientation: The scaleX (m00) and
|
||||
// scaleY (m11) values need to be flipped to get correct behaviour e.g.
|
||||
// when using rotation or shearing.
|
||||
context.setTransform(
|
||||
-this._m00, this._m01, this._m10,
|
||||
-this._m11, this._m02, this._m12
|
||||
);
|
||||
if (reset) {
|
||||
context.setTransform(
|
||||
-this._m00, this._m01, this._m10,
|
||||
-this._m11, this._m02, this._m12
|
||||
);
|
||||
} else {
|
||||
context.transform(
|
||||
-this._m00, this._m01, this._m10,
|
||||
-this._m11, this._m02, this._m12
|
||||
);
|
||||
}
|
||||
>>>>>>> origin/master
|
||||
},
|
||||
|
||||
statics: {
|
||||
|
|
|
@ -2,17 +2,25 @@ Doc = Base.extend({
|
|||
beans: true,
|
||||
|
||||
initialize: function(canvas) {
|
||||
if (canvas) {
|
||||
if (canvas && canvas instanceof HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
this.size = new Size(canvas.offsetWidth, canvas.offsetHeight);
|
||||
} else {
|
||||
this.size = Size.read(arguments) || new Size(1024, 768);
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.canvas.width = this.size.width;
|
||||
this.canvas.height = this.size.height;
|
||||
}
|
||||
this.bounds = new Rectangle(new Point(0, 0), this.size);
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
Paper.documents.push(this);
|
||||
this.activate();
|
||||
this.layers = [];
|
||||
this.activeLayer = new Layer();
|
||||
this.currentStyle = null;
|
||||
this.symbols = [];
|
||||
this.views = [new DocumentView(this)];
|
||||
this.activeView = this.views[0];
|
||||
},
|
||||
|
||||
getCurrentStyle: function() {
|
||||
|
@ -29,13 +37,18 @@ Doc = Base.extend({
|
|||
|
||||
redraw: function() {
|
||||
if (this.canvas) {
|
||||
// Initial tests conclude that clearing the canvas is always
|
||||
// faster than using clearRect:
|
||||
// Initial tests conclude that clearing the canvas using clearRect
|
||||
// is always faster than setting canvas.width = canvas.width
|
||||
// http://jsperf.com/clearrect-vs-setting-width/7
|
||||
var view = this.activeView;
|
||||
var bounds = view.bounds;
|
||||
this.ctx.clearRect(0, 0, this.size.width + 1, this.size.height + 1);
|
||||
this.ctx.save();
|
||||
view.matrix.applyToContext(this.ctx, true);
|
||||
for (var i = 0, l = this.layers.length; i < l; i++) {
|
||||
this.layers[i].draw(this.ctx, {});
|
||||
}
|
||||
this.ctx.restore();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
97
src/document/DocumentView.js
Normal file
97
src/document/DocumentView.js
Normal file
|
@ -0,0 +1,97 @@
|
|||
DocumentView = Base.extend({
|
||||
beans: true,
|
||||
|
||||
initialize: function(document) {
|
||||
this.document = document;
|
||||
this._bounds = this.document.bounds.clone();
|
||||
this.matrix = new Matrix();
|
||||
this._zoom = 1;
|
||||
this._center = this._bounds.center;
|
||||
},
|
||||
|
||||
// TODO: test this.
|
||||
getCenter: function() {
|
||||
return this._center;
|
||||
},
|
||||
|
||||
setCenter: function() {
|
||||
var center = Point.read(arguments);
|
||||
if (center) {
|
||||
var delta = center.subtract(this._center);
|
||||
this.scrollBy(delta);
|
||||
this._center = center;
|
||||
}
|
||||
},
|
||||
|
||||
getZoom: function() {
|
||||
return this._zoom;
|
||||
},
|
||||
|
||||
setZoom: function(zoom) {
|
||||
// TODO: clamp the view between 1/32 and 64?
|
||||
var mx = new Matrix();
|
||||
mx.scale(zoom / this._zoom, this.center);
|
||||
this.transform(mx);
|
||||
this._zoom = zoom;
|
||||
},
|
||||
|
||||
scrollBy: function() {
|
||||
var point = Point.read(arguments).negate();
|
||||
var mx = new Matrix().translate(point);
|
||||
this.transform(mx);
|
||||
},
|
||||
|
||||
getBounds: function() {
|
||||
return this._bounds;
|
||||
},
|
||||
|
||||
// TODO:
|
||||
// setBounds: function(rect) {
|
||||
//
|
||||
// },
|
||||
|
||||
// TODO: getInvalidBounds
|
||||
// TODO: invalidate(rect)
|
||||
// TODO: style: artwork / preview / raster / opaque / ink
|
||||
// TODO: getShowGrid
|
||||
// TODO: getMousePoint
|
||||
// TODO: artworkToView(rect)
|
||||
artworkToView: function(point) {
|
||||
return this.matrix.transform(point);
|
||||
},
|
||||
|
||||
viewToArtwork: function(point) {
|
||||
// TODO: cache the inverse matrix:
|
||||
return this.matrix.createInverse().transform(point);
|
||||
},
|
||||
|
||||
// TODO: inherit this code somehow?
|
||||
transform: function(matrix, flags) {
|
||||
var width = this.document.bounds.width;
|
||||
var height = this.document.bounds.height;
|
||||
var x = width * -0.5;
|
||||
var y = height * -0.5;
|
||||
var coords = [
|
||||
x, y,
|
||||
x + width, y,
|
||||
x + width, y + height,
|
||||
x, y + height];
|
||||
this.matrix.preConcatenate(matrix);
|
||||
this.matrix.createInverse().transform(coords, 0, coords, 0, 4);
|
||||
var xMin = coords[0], xMax = coords[0];
|
||||
var yMin = coords[1], yMax = coords[1];
|
||||
for (var i = 2; i < 8; i += 2) {
|
||||
var x = coords[i];
|
||||
var y = coords[i + 1];
|
||||
xMin = Math.min(x, xMin);
|
||||
xMax = Math.max(x, xMax);
|
||||
yMin = Math.min(y, yMin);
|
||||
yMax = Math.max(y, yMax);
|
||||
};
|
||||
var bounds = this._bounds;
|
||||
bounds.x = xMin;
|
||||
bounds.y = yMin;
|
||||
bounds.width = xMax - xMin;
|
||||
bounds.height = yMax - yMin;
|
||||
}
|
||||
});
|
|
@ -30,15 +30,18 @@ BlendMode = {
|
|||
process: function(documentContext, item, param) {
|
||||
// TODO: use strokeBounds
|
||||
var itemBounds = item.bounds;
|
||||
if (!itemBounds)
|
||||
return;
|
||||
var top = Math.floor(itemBounds.top);
|
||||
var left = Math.floor(itemBounds.left);
|
||||
var size = itemBounds.size.ceil();
|
||||
var size = itemBounds.size.ceil().add(1, 1);
|
||||
var width = size.width;
|
||||
var height = size.height;
|
||||
|
||||
var itemCanvas = CanvasProvider.getCanvas(size);
|
||||
var itemContext = itemCanvas.getContext('2d');
|
||||
if(item.matrix) {
|
||||
itemContext.save();
|
||||
if (item.matrix) {
|
||||
var matrix = item.matrix.clone();
|
||||
var transMatrix = Matrix.getTranslateInstance(-left, -top);
|
||||
matrix.preConcatenate(transMatrix);
|
||||
|
@ -210,6 +213,7 @@ BlendMode = {
|
|||
}
|
||||
}
|
||||
documentContext.putImageData(dstD, left, top);
|
||||
itemContext.restore();
|
||||
CanvasProvider.returnCanvas(itemCanvas);
|
||||
}
|
||||
};
|
|
@ -18,7 +18,7 @@ Group = Item.extend({
|
|||
// temporary canvas, and then draw that canvas onto ctx afterwards
|
||||
// with globalAlpha set.
|
||||
var tempCanvas, originalCtx;
|
||||
if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
if (this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
BlendMode.process(ctx, this, param);
|
||||
} else {
|
||||
param.ignoreBlendMode = false;
|
||||
|
@ -27,6 +27,8 @@ Group = Item.extend({
|
|||
// TODO: use strokeBounds for this, when implemented:
|
||||
tempCanvas = CanvasProvider.getCanvas(this.document.size);
|
||||
ctx = tempCanvas.getContext('2d');
|
||||
ctx.save();
|
||||
this.document.activeView.matrix.applyToContext(ctx);
|
||||
}
|
||||
for (var i = 0, l = this.children.length; i < l; i++) {
|
||||
this.children[i].draw(ctx, param);
|
||||
|
@ -34,11 +36,18 @@ Group = Item.extend({
|
|||
ctx.clip();
|
||||
}
|
||||
if (tempCanvas) {
|
||||
// restore the activeView.matrix transformation,
|
||||
// so we can draw the image without transformation.
|
||||
originalCtx.restore();
|
||||
originalCtx.save();
|
||||
originalCtx.globalAlpha = this.opacity;
|
||||
originalCtx.drawImage(tempCanvas, 0, 0);
|
||||
originalCtx.restore();
|
||||
// Return the canvas, so it can be reused
|
||||
// apply the view transformation again.
|
||||
this.document.activeView.matrix.applyToContext(ctx);
|
||||
// Restore the state of the temp canvas:
|
||||
ctx.restore();
|
||||
// Return the temp canvas, so it can be reused
|
||||
CanvasProvider.returnCanvas(tempCanvas);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -478,7 +478,7 @@ Item = Base.extend({
|
|||
while(parent) {
|
||||
// Find group parents. Check for parent.parent, since don't want
|
||||
// top level layers, because they also inherit from Group
|
||||
if(parent.parent
|
||||
if (parent.parent
|
||||
&& (parent instanceof Group || parent instanceof CompoundPath)
|
||||
&& item.isDescendant(parent))
|
||||
return true;
|
||||
|
@ -538,7 +538,7 @@ 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.
|
||||
if(!resolution)
|
||||
if (!resolution)
|
||||
resolution = 72;
|
||||
// TODO: use strokebounds for this:
|
||||
var bounds = this.bounds;
|
||||
|
@ -547,7 +547,7 @@ Item = Base.extend({
|
|||
var context = canvas.getContext('2d');
|
||||
var matrix = new Matrix().scale(scale).translate(-bounds.x, -bounds.y);
|
||||
matrix.applyToContext(context);
|
||||
this.draw(context);
|
||||
this.draw(context, {});
|
||||
var raster = new Raster(canvas);
|
||||
raster.position = this.bounds.center;
|
||||
raster.scale(1 / scale);
|
||||
|
|
|
@ -40,7 +40,7 @@ PlacedSymbol = Item.extend({
|
|||
|
||||
var xMin = coords[0], xMax = coords[0];
|
||||
var yMin = coords[1], yMax = coords[1];
|
||||
for(var i = 2; i < 8; i += 2) {
|
||||
for (var i = 2; i < 8; i += 2) {
|
||||
var x = coords[i];
|
||||
var y = coords[i + 1];
|
||||
xMin = Math.min(x, xMin);
|
||||
|
@ -60,16 +60,42 @@ PlacedSymbol = Item.extend({
|
|||
},
|
||||
|
||||
draw: function(ctx, param) {
|
||||
if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
if (this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
BlendMode.process(ctx, this, param);
|
||||
} else {
|
||||
var tempCanvas, originalCtx;
|
||||
if (this.opacity < 1) {
|
||||
originalCtx = ctx;
|
||||
// TODO: use strokeBounds for this, when implemented:
|
||||
tempCanvas = CanvasProvider.getCanvas(this.document.size);
|
||||
ctx = tempCanvas.getContext('2d');
|
||||
ctx.save();
|
||||
this.document.activeView.matrix.applyToContext(ctx);
|
||||
}
|
||||
|
||||
// TODO: we need to preserve strokewidth, but still transform the fill
|
||||
ctx.save();
|
||||
if(param.ignoreBlendMode !== true)
|
||||
if (param.ignoreBlendMode !== true)
|
||||
this.matrix.applyToContext(ctx);
|
||||
param.ignoreBlendMode = false;
|
||||
this.symbol.definition.draw(ctx, param);
|
||||
ctx.restore();
|
||||
|
||||
if (tempCanvas) {
|
||||
// restore the activeView.matrix transformation,
|
||||
// so we can draw the image without transformation.
|
||||
originalCtx.restore();
|
||||
originalCtx.save();
|
||||
originalCtx.globalAlpha = this.opacity;
|
||||
originalCtx.drawImage(tempCanvas, 0, 0);
|
||||
originalCtx.restore();
|
||||
// apply the view transformation again.
|
||||
this.document.activeView.matrix.applyToContext(ctx, true);
|
||||
// Restore the state of the temp canvas:
|
||||
ctx.restore();
|
||||
// Return the temp canvas, so it can be reused
|
||||
CanvasProvider.returnCanvas(tempCanvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO:
|
||||
|
|
|
@ -186,7 +186,7 @@ Raster = Item.extend({
|
|||
},
|
||||
|
||||
draw: function(ctx, param) {
|
||||
if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
if (this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
BlendMode.process(ctx, this, param);
|
||||
} else {
|
||||
ctx.save();
|
||||
|
|
|
@ -20,10 +20,10 @@ CompoundPath = PathItem.extend(new function() {
|
|||
},
|
||||
|
||||
draw: function(ctx, param) {
|
||||
if(!this.visible)
|
||||
if (!this.visible)
|
||||
return;
|
||||
if (this.children.length) {
|
||||
if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
if (this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
BlendMode.process(ctx, this, param);
|
||||
} else {
|
||||
var firstChild = this.children[0];
|
||||
|
|
|
@ -110,7 +110,7 @@ Path.inject({ statics: new function() {
|
|||
var three = !(numSides % 3);
|
||||
var vector = new Point(0, three ? -radius : radius);
|
||||
var offset = three ? -1 : 0.5;
|
||||
for(var i = 0; i < numSides; i++) {
|
||||
for (var i = 0; i < numSides; i++) {
|
||||
var angle = (360 / numSides) * (i + offset);
|
||||
path.add(center.add(vector.rotate(angle)));
|
||||
}
|
||||
|
|
|
@ -139,11 +139,11 @@ Path = PathItem.extend({
|
|||
throw new Error('Nesting capacity exceeded in Path#getLenght()');
|
||||
// Multiply by 3 again, as derivative was divided by 3
|
||||
var length = 3 * integral;
|
||||
if(goal == undefined || goal < 0 || goal >= length)
|
||||
if (goal == undefined || goal < 0 || goal >= length)
|
||||
return length;
|
||||
var result = MathUtils.unsimpson(goal, ds, 0, goal / integral,
|
||||
100 * MathUtils.EPSILON, integral, Math.sqrt(MathUtils.EPSILON), 1);
|
||||
if(!result)
|
||||
if (!result)
|
||||
throw new Error('Nesting capacity exceeded in computing arctime');
|
||||
return -result.b;
|
||||
},
|
||||
|
@ -408,7 +408,7 @@ Path = PathItem.extend({
|
|||
|
||||
draw: function(ctx, param) {
|
||||
if (!this.visible) return;
|
||||
if(this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
if (this.blendMode != 'normal' && !param.ignoreBlendMode) {
|
||||
BlendMode.process(ctx, this, param);
|
||||
} else {
|
||||
param.ignoreBlendMode = false;
|
||||
|
|
148
src/tool/Tool.js
148
src/tool/Tool.js
|
@ -1,74 +1,84 @@
|
|||
Tool = ToolHandler.extend({
|
||||
beans: true,
|
||||
Tool = ToolHandler.extend(new function() {
|
||||
function viewToArtwork(event, document) {
|
||||
var point = Point.create(event.offset.x, event.offset.y);
|
||||
// TODO: always the active view?
|
||||
return document.activeView.viewToArtwork(point);
|
||||
};
|
||||
|
||||
return {
|
||||
beans: true,
|
||||
|
||||
initialize: function(handlers, doc) {
|
||||
this.base(handlers);
|
||||
if (Paper.document)
|
||||
this.document = Paper.document;
|
||||
},
|
||||
|
||||
setDocument: function(doc) {
|
||||
if (this._document)
|
||||
$(this._document.canvas).removeEvents();
|
||||
this._document = doc || Paper.document;
|
||||
var that = this, curPoint;
|
||||
var dragging = false;
|
||||
var events = {
|
||||
dragstart: function(e) {
|
||||
curPoint = new Point(e.offset);
|
||||
that.onHandleEvent('MOUSE_DOWN', curPoint, null, null);
|
||||
if (that.onMouseDown)
|
||||
that._document.redraw();
|
||||
if (that.eventInterval != -1)
|
||||
this.intervalId = setInterval(events.drag, that.eventInterval);
|
||||
dragging = true;
|
||||
},
|
||||
drag: function(e) {
|
||||
if (e) curPoint = new Point(e.offset);
|
||||
if (curPoint) {
|
||||
that.onHandleEvent('MOUSE_DRAG', curPoint, null, null);
|
||||
if (that.onMouseDrag)
|
||||
initialize: function(handlers, doc) {
|
||||
this.base(handlers);
|
||||
if (Paper.document)
|
||||
this.document = Paper.document;
|
||||
},
|
||||
|
||||
setDocument: function(doc) {
|
||||
if (this._document)
|
||||
$(this._document.canvas).removeEvents();
|
||||
this._document = doc || Paper.document;
|
||||
var that = this, curPoint;
|
||||
var dragging = false;
|
||||
var events = {
|
||||
dragstart: function(e) {
|
||||
curPoint = viewToArtwork(e, that._document);
|
||||
that.onHandleEvent('MOUSE_DOWN', curPoint, null, null);
|
||||
if (that.onMouseDown)
|
||||
that._document.redraw();
|
||||
}
|
||||
},
|
||||
dragend: function(e) {
|
||||
curPoint = null;
|
||||
if (this.eventInterval != -1)
|
||||
clearInterval(this.intervalId);
|
||||
that.onHandleEvent('MOUSE_UP', new Point(e.offset), null, null);
|
||||
if (that.onMouseUp)
|
||||
that._document.redraw();
|
||||
dragging = false;
|
||||
},
|
||||
mousemove: function(e) {
|
||||
if(!dragging) {
|
||||
that.onHandleEvent('MOUSE_MOVE', new Point(e.offset), null, null);
|
||||
if (that.onMouseMove)
|
||||
if (that.eventInterval != -1)
|
||||
this.intervalId = setInterval(events.drag, that.eventInterval);
|
||||
dragging = true;
|
||||
},
|
||||
drag: function(e) {
|
||||
if (e) curPoint = viewToArtwork(e, that._document);
|
||||
if (curPoint) {
|
||||
that.onHandleEvent('MOUSE_DRAG', curPoint, null, null);
|
||||
if (that.onMouseDrag)
|
||||
that._document.redraw();
|
||||
}
|
||||
},
|
||||
dragend: function(e) {
|
||||
curPoint = null;
|
||||
if (this.eventInterval != -1)
|
||||
clearInterval(this.intervalId);
|
||||
that.onHandleEvent('MOUSE_UP',
|
||||
viewToArtwork(e, that._document), null, null);
|
||||
if (that.onMouseUp)
|
||||
that._document.redraw();
|
||||
dragging = false;
|
||||
},
|
||||
mousemove: function(e) {
|
||||
if (!dragging) {
|
||||
that.onHandleEvent('MOUSE_MOVE',
|
||||
viewToArtwork(e, that._document), null, null);
|
||||
if (that.onMouseMove)
|
||||
that._document.redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
$(doc.canvas).addEvents(events);
|
||||
},
|
||||
|
||||
/**
|
||||
* The fixed time delay between each call to the {@link #onMouseDrag}
|
||||
* event. Setting this to an interval means the {@link #onMouseDrag} event
|
||||
* is called repeatedly after the initial {@link #onMouseDown} until the
|
||||
* user releases the mouse.
|
||||
*
|
||||
* Sample code:
|
||||
* <code>
|
||||
* // Fire the onMouseDrag event once a second,
|
||||
* // while the mouse button is down
|
||||
* tool.eventInterval = 1000;
|
||||
* </code>
|
||||
*
|
||||
* @return the interval time in milliseconds
|
||||
*/
|
||||
eventInterval: -1,
|
||||
|
||||
getDocument: function() {
|
||||
return this._document;
|
||||
}
|
||||
};
|
||||
$(doc.canvas).addEvents(events);
|
||||
},
|
||||
|
||||
/**
|
||||
* The fixed time delay between each call to the {@link #onMouseDrag}
|
||||
* event. Setting this to an interval means the {@link #onMouseDrag} event
|
||||
* is called repeatedly after the initial {@link #onMouseDown} until the
|
||||
* user releases the mouse.
|
||||
*
|
||||
* Sample code:
|
||||
* <code>
|
||||
* // Fire the onMouseDrag event once a second,
|
||||
* // while the mouse button is down
|
||||
* tool.eventInterval = 1000;
|
||||
* </code>
|
||||
*
|
||||
* @return the interval time in milliseconds
|
||||
*/
|
||||
eventInterval: -1,
|
||||
|
||||
getDocument: function() {
|
||||
return this._document;
|
||||
}
|
||||
};
|
||||
});
|
|
@ -6,11 +6,11 @@
|
|||
CanvasProvider = {
|
||||
canvases: [],
|
||||
getCanvas: function(size) {
|
||||
if(this.canvases.length) {
|
||||
if (this.canvases.length) {
|
||||
var canvas = this.canvases.pop();
|
||||
// If they are not the same size, we don't need to clear them
|
||||
// using clearRect and visa versa.
|
||||
if((canvas.width != size.width) || (canvas.height != size.height)) {
|
||||
if ((canvas.width != size.width) || (canvas.height != size.height)) {
|
||||
canvas.width = size.width;
|
||||
canvas.height = size.height;
|
||||
} else {
|
||||
|
|
Loading…
Reference in a new issue