Merge remote-tracking branch 'origin/master'

Conflicts:
	src/basic/Matrix.js
	src/item/Raster.js
This commit is contained in:
Jürg Lehni 2011-02-28 23:27:03 +01:00
commit 3f972be500
13 changed files with 263 additions and 96 deletions

View file

@ -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: {

View file

@ -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();
}
}
});

View 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;
}
});

View file

@ -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);
}
};

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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:

View file

@ -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();

View file

@ -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];

View file

@ -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)));
}

View file

@ -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;

View file

@ -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;
}
};
});

View file

@ -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 {