Merge remote branch 'origin/master'

This commit is contained in:
Jonathan Puckey 2011-05-18 16:01:36 +02:00
commit 241d98a1cf
24 changed files with 208 additions and 211 deletions

View file

@ -71,7 +71,7 @@ var GradientColor = this.GradientColor = Color.extend({
var gradient; var gradient;
if (this.gradient.type === 'linear') { if (this.gradient.type === 'linear') {
gradient = ctx.createLinearGradient(this._origin.x, this._origin.y, gradient = ctx.createLinearGradient(this._origin.x, this._origin.y,
this.destination.x, this.destination.y); this._destination.x, this._destination.y);
} else { } else {
var origin = this._hilite || this._origin; var origin = this._hilite || this._origin;
gradient = ctx.createRadialGradient(origin.x, origin.y, gradient = ctx.createRadialGradient(origin.x, origin.y,
@ -79,7 +79,7 @@ var GradientColor = this.GradientColor = Color.extend({
} }
for (var i = 0, l = this.gradient._stops.length; i < l; i++) { for (var i = 0, l = this.gradient._stops.length; i < l; i++) {
var stop = this.gradient._stops[i]; var stop = this.gradient._stops[i];
gradient.addColorStop(stop._rampPoint, stop.color.toCssString()); gradient.addColorStop(stop._rampPoint, stop._color.toCssString());
} }
return gradient; return gradient;
}, },
@ -92,12 +92,10 @@ var GradientColor = this.GradientColor = Color.extend({
* @return true if the GrayColor is the same, false otherwise. * @return true if the GrayColor is the same, false otherwise.
*/ */
equals: function(color) { equals: function(color) {
if (color && color._colorType === this._colorType) { return color == this || color && color._colorType === this._colorType
return this.gradient.equals(color.gradient) && this.gradient.equals(color.gradient)
&& this._origin.equals(color._origin) && this._origin.equals(color._origin)
&& this._destination.equals(color._destination); && this._destination.equals(color._destination);
}
return false;
}, },
transform: function(matrix) { transform: function(matrix) {

View file

@ -28,7 +28,7 @@ var GradientStop = this.GradientStop = Base.extend({
}, },
setRampPoint: function(rampPoint) { setRampPoint: function(rampPoint) {
this._rampPoint = rampPoint !== null ? rampPoint : 0; this._rampPoint = rampPoint || 0;
}, },
getColor: function() { getColor: function() {
@ -38,14 +38,10 @@ var GradientStop = this.GradientStop = Base.extend({
setColor: function(color) { setColor: function(color) {
this._color = Color.read(arguments); this._color = Color.read(arguments);
}, },
equals: function(stop) { equals: function(stop) {
if (stop == this) { return stop == this || stop instanceof GradientStop
return true; && this._color.equals(stop._color)
} else if (stop instanceof GradientStop) { && rampPoint == stop._rampPoint;
return this._color.equals(stop._color)
&& rampPoint == stop.rampPoint;
}
return false;
} }
}); });

View file

@ -43,6 +43,14 @@ Base.inject({
return res; return res;
}, },
initialize: function(object, values, defaults) {
if (!values)
values = defaults;
return Base.each(defaults, function(value, key) {
this[key] = values[key] || value;
}, object);
},
/** /**
* Utility function for adding and removing items from a list of which * Utility function for adding and removing items from a list of which
* each entry keeps a reference to its index in the list in the private * each entry keeps a reference to its index in the list in the private

View file

@ -19,27 +19,18 @@
* global paper object, which simply is a pointer to the currently active scope. * global paper object, which simply is a pointer to the currently active scope.
*/ */
var PaperScope = this.PaperScope = Base.extend({ var PaperScope = this.PaperScope = Base.extend({
beans: true,
initialize: function(id) { initialize: function(id) {
this.project = null; this.project = null;
this.projects = []; this.projects = [];
this.view = null;
this.views = [];
this.tool = null;
this.tools = []; this.tools = [];
this.id = id; this.id = id;
PaperScope._scopes[id] = this; PaperScope._scopes[id] = this;
}, },
/**
* A short-cut to the currently active view of the active project.
*/
getView: function() {
return this.project.activeView;
},
getViews: function() {
return this.project.views;
},
evaluate: function(code) { evaluate: function(code) {
return PaperScript.evaluate(code, this); return PaperScript.evaluate(code, this);
}, },
@ -60,9 +51,12 @@ var PaperScope = this.PaperScope = Base.extend({
}, },
clear: function() { clear: function() {
// Remove all projects and tools. // Remove all projects, views and tools.
for (var i = this.projects.length - 1; i >= 0; i--) for (var i = this.projects.length - 1; i >= 0; i--)
this.projects[i].remove(); this.projects[i].remove();
// This also removes the installed event handlers.
for (var i = this.views.length - 1; i >= 0; i--)
this.views[i].remove();
for (var i = this.tools.length - 1; i >= 0; i--) for (var i = this.tools.length - 1; i >= 0; i--)
this.tools[i].remove(); this.tools[i].remove();
}, },

View file

@ -137,11 +137,12 @@ var PaperScript = this.PaperScript = new function() {
// so the active project is defined. // so the active project is defined.
var canvas = code.getAttribute('canvas'); var canvas = code.getAttribute('canvas');
if (canvas = canvas && document.getElementById(canvas)) { if (canvas = canvas && document.getElementById(canvas)) {
// Create a Project for this canvas, using the right scope // Create an empty Project for this scope, and a view for the
// canvas, both using the right paper scope
paper = scope; paper = scope;
// XXX: Do not pass canvas to Project. new Project();
// Create ProjectView here instead. // Activate the newly created view straight away
new Project(canvas); new View(canvas).activate();
} }
if (code.src) { if (code.src) {
// If we're loading from a source, request that first and then // If we're loading from a source, request that first and then
@ -154,7 +155,7 @@ var PaperScript = this.PaperScript = new function() {
} }
//#endif // BROWSER //#endif // BROWSER
var proj = scope.project, var proj = scope.project,
view = proj.activeView, view = scope.view,
// TODO: Add support for multiple tools // TODO: Add support for multiple tools
tool = scope.tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code) tool = scope.tool = /on(?:Key|Mouse)(?:Up|Down|Move|Drag)/.test(code)
&& new Tool(null, scope), && new Tool(null, scope),

View file

@ -21,12 +21,9 @@ var Group = this.Group = Item.extend({
this.base(); this.base();
this._children = []; this._children = [];
this._namedChildren = {}; this._namedChildren = {};
if (items) {
for (var i = 0, l = items.length; i < l; i++) {
this.appendTop(items[i]);
}
}
this._clipped = false; this._clipped = false;
this.setChildren(!items || !Array.isArray(items)
|| typeof items[0] !== 'object' ? arguments : items);
}, },
/** /**

View file

@ -19,6 +19,7 @@ var Item = this.Item = Base.extend({
initialize: function() { initialize: function() {
paper.project.activeLayer.appendTop(this); paper.project.activeLayer.appendTop(this);
this._style = PathStyle.create(this);
this.setStyle(this._project.getCurrentStyle()); this.setStyle(this._project.getCurrentStyle());
}, },
@ -38,29 +39,10 @@ var Item = this.Item = Base.extend({
* The unique id of the item. * The unique id of the item.
*/ */
getId: function() { getId: function() {
if (this._id == null) { if (this._id == null)
this._id = Item._id = (Item._id || 0) + 1; this._id = Item._id = (Item._id || 0) + 1;
}
return this._id; return this._id;
}, },
_removeFromNamed: function() {
var children = this._parent._children,
namedChildren = this._parent._namedChildren,
name = this._name,
namedArray = namedChildren[name];
if (children[name] = this)
delete children[name];
namedArray.splice(namedArray.indexOf(this), 1);
// If there are any items left in the named array, set
// the last of them to be this.parent.children[this.name]
if (namedArray.length) {
children[name] = namedArray[namedArray.length - 1];
} else {
// Otherwise delete the empty array
delete namedChildren[name];
}
},
/** /**
* The name of the item. * The name of the item.
@ -86,6 +68,47 @@ var Item = this.Item = Base.extend({
delete children[name]; delete children[name];
} }
}, },
_removeFromNamed: function() {
var children = this._parent._children,
namedChildren = this._parent._namedChildren,
name = this._name,
namedArray = namedChildren[name];
if (children[name] = this)
delete children[name];
namedArray.splice(namedArray.indexOf(this), 1);
// If there are any items left in the named array, set
// the last of them to be this.parent.children[this.name]
if (namedArray.length) {
children[name] = namedArray[namedArray.length - 1];
} else {
// Otherwise delete the empty array
delete namedChildren[name];
}
},
/**
* Removes the item from its parent's children list.
*/
_removeFromParent: function() {
if (this._parent) {
if (this._name)
this._removeFromNamed();
var res = Base.splice(this._parent._children, null, this._index, 1);
this._parent = null;
return !!res.length;
}
return false;
},
/**
* Removes the item.
*/
remove: function() {
if (this.isSelected())
this.setSelected(false);
return this._removeFromParent();
},
/** /**
* When passed a project, copies the item to the project, * When passed a project, copies the item to the project,
@ -238,7 +261,20 @@ var Item = this.Item = Base.extend({
return this._children; return this._children;
}, },
// TODO: #setChildren() setChildren: function(items) {
this.removeChildren();
for (var i = 0, l = items && items.length; i < l; i++)
this.appendTop(items[i]);
},
/**
* Checks if the item contains any children items.
*
* @return true if it has one or more children, false otherwise.
*/
hasChildren: function() {
return this._children && this._children.length > 0;
},
/** /**
* Reverses the order of this item's children * Reverses the order of this item's children
@ -252,6 +288,18 @@ var Item = this.Item = Base.extend({
} }
}, },
/**
* Removes all of the item's children, if it has any
*/
removeChildren: function() {
var removed = false;
if (this._children) {
for (var i = this._children.length - 1; i >= 0; i--)
removed = this._children[i].remove() || removed;
}
return removed;
},
/** /**
* The first item contained within this item. * The first item contained within this item.
*/ */
@ -281,38 +329,6 @@ var Item = this.Item = Base.extend({
return this._parent && this._parent._children[this._index - 1] || null; return this._parent && this._parent._children[this._index - 1] || null;
}, },
/**
* Removes the item from its parent's children list.
*/
_removeFromParent: function() {
if (this._parent) {
if (this._name)
this._removeFromNamed();
var res = Base.splice(this._parent._children, null, this._index, 1);
this._parent = null;
return !!res.length;
}
return false;
},
/**
* Removes the item.
*/
remove: function() {
if (this.isSelected())
this.setSelected(false);
return this._removeFromParent();
},
/**
* Checks if the item contains any children items.
*
* @return true if it has one or more children, false otherwise.
*/
hasChildren: function() {
return this._children && this._children.length > 0;
},
/** /**
* Checks whether the item is editable. * Checks whether the item is editable.
* *
@ -629,7 +645,7 @@ var Item = this.Item = Base.extend({
}, },
setStyle: function(style) { setStyle: function(style) {
this._style = PathStyle.create(this, style); this._style.initialize(style);
}, },
// TODO: toString // TODO: toString
@ -649,7 +665,7 @@ var Item = this.Item = Base.extend({
} }
}, },
// TODO: Implement ProjectView into the drawing // TODO: Implement View into the drawing
// TODO: Optimize temporary canvas drawing to ignore parts that are // TODO: Optimize temporary canvas drawing to ignore parts that are
// outside of the visible view. // outside of the visible view.
draw: function(item, ctx, param) { draw: function(item, ctx, param) {
@ -781,15 +797,6 @@ var Item = this.Item = Base.extend({
*/ */
appendBottom: append(false), appendBottom: append(false),
/**
* A link to {@link #appendTop}
*
* @deprecated use {@link #appendTop} or {@link #appendBottom} instead.
*/
appendChild: function(item) {
return this.appendTop(item);
},
/** /**
* Moves this item above the specified item. * Moves this item above the specified item.
* *

View file

@ -18,6 +18,8 @@ var Layer = this.Layer = Group.extend({
beans: true, beans: true,
initialize: function() { initialize: function() {
// TODO: Isn't there a way to call this.base() here and then move the
// layer into layers instead?
this._children = []; this._children = [];
this._namedChildren = {}; this._namedChildren = {};
this._project = paper.project; this._project = paper.project;

View file

@ -29,13 +29,13 @@ var PathStyle = this.PathStyle = Base.extend(new function() {
beans: true, beans: true,
initialize: function(style) { initialize: function(style) {
if (style) { // Note: This relies on bean setters that get implicetly
for (var i = 0, l = keys.length; i < l; i++) { // called when setting values on this[key].
var key = keys[i], for (var i = 0, l = style && keys.length; i < l; i++) {
value = style[key]; var key = keys[i],
if (value !== undefined) value = style[key];
this[key] = value; if (value !== undefined)
} this[key] = value;
} }
}, },
@ -44,10 +44,9 @@ var PathStyle = this.PathStyle = Base.extend(new function() {
}, },
statics: { statics: {
create: function(item, other) { create: function(item) {
var style = new PathStyle(PathStyle.dont); var style = new PathStyle(PathStyle.dont);
style._item = item; style._item = item;
style.initialize(other);
return style; return style;
} }
} }
@ -101,8 +100,8 @@ var PathStyle = this.PathStyle = Base.extend(new function() {
} }
}; };
// 'this' = the Base.each() side-car = the object that is injected into // 'this' = the Base.each() side-car = the object that is returned from
// Item above: // Base.each and injected into Item above:
this[set] = function(value) { this[set] = function(value) {
this._style[set](value); this._style[set](value);
return this; return this;

View file

@ -35,7 +35,6 @@ var sources = [
'src/basic/Matrix.js', 'src/basic/Matrix.js',
'src/basic/Line.js', 'src/basic/Line.js',
'src/project/ProjectView.js',
'src/project/Project.js', 'src/project/Project.js',
'src/project/Symbol.js', 'src/project/Symbol.js',
@ -73,6 +72,7 @@ var sources = [
'src/browser/DomElement.js', 'src/browser/DomElement.js',
'src/browser/DomEvent.js', 'src/browser/DomEvent.js',
'src/ui/View.js',
'src/ui/Event.js', 'src/ui/Event.js',
'src/ui/KeyEvent.js', 'src/ui/KeyEvent.js',
'src/ui/Key.js', 'src/ui/Key.js',

View file

@ -52,7 +52,6 @@ var paper = new function() {
//#include "basic/Matrix.js" //#include "basic/Matrix.js"
//#include "basic/Line.js" //#include "basic/Line.js"
//#include "project/ProjectView.js"
//#include "project/Project.js" //#include "project/Project.js"
//#include "project/Symbol.js" //#include "project/Symbol.js"
@ -92,8 +91,8 @@ var paper = new function() {
//#include "browser/DomElement.js" //#include "browser/DomElement.js"
//#include "browser/DomEvent.js" //#include "browser/DomEvent.js"
//#include "browser/Request.js"
//#include "ui/View.js"
//#include "ui/Event.js" //#include "ui/Event.js"
//#include "ui/KeyEvent.js" //#include "ui/KeyEvent.js"
//#include "ui/Key.js" //#include "ui/Key.js"

View file

@ -28,6 +28,7 @@ var CompoundPath = this.CompoundPath = PathItem.extend({
// clockwise orientation when creating a compound path, so that they // clockwise orientation when creating a compound path, so that they
// appear as holes, but only if their orientation was not already // appear as holes, but only if their orientation was not already
// specified before (= _clockwise is defined). // specified before (= _clockwise is defined).
// TODO: This should really be handled in appendTop / Bottom, right?
if (path._clockwise === undefined) if (path._clockwise === undefined)
path.setClockwise(i < l - 1); path.setClockwise(i < l - 1);
this.appendTop(path); this.appendTop(path);

View file

@ -643,8 +643,14 @@ var Path = this.Path = PathItem.extend({
}; };
return { return {
beans: true, _setStyles: function(ctx) {
for (var i in styles) {
var style = this._style[i]();
if (style)
ctx[styles[i]] = style;
}
},
smooth: function() { smooth: function() {
// This code is based on the work by Oleg V. Polikarpotchkin, // This code is based on the work by Oleg V. Polikarpotchkin,
// http://ov-p.spaces.live.com/blog/cns!39D56F0C7A08D703!147.entry // http://ov-p.spaces.live.com/blog/cns!39D56F0C7A08D703!147.entry
@ -742,14 +748,6 @@ var Path = this.Path = PathItem.extend({
var segment = this._segments[0]; var segment = this._segments[0];
segment.setHandleIn(handleIn.subtract(segment._point)); segment.setHandleIn(handleIn.subtract(segment._point));
} }
},
_setStyles: function(ctx) {
for (var i in styles) {
var style = this[i]();
if (style)
ctx[styles[i]] = style;
}
} }
}; };
}, new function() { // PostScript-style drawing commands }, new function() { // PostScript-style drawing commands

View file

@ -17,8 +17,8 @@
var Project = this.Project = Base.extend({ var Project = this.Project = Base.extend({
beans: true, beans: true,
// XXX: Add arguments to define pages, but do not pass canvas here // TODO: Add arguments to define pages
initialize: function(canvas) { initialize: function() {
// Store reference to the currently active global paper scope: // Store reference to the currently active global paper scope:
this._scope = paper; this._scope = paper;
// Push it onto this._scope.projects and set index: // Push it onto this._scope.projects and set index:
@ -27,13 +27,11 @@ var Project = this.Project = Base.extend({
// Layer and DoumentView constructors. // Layer and DoumentView constructors.
this.activate(); this.activate();
this.layers = []; this.layers = [];
this.views = [];
this.symbols = []; this.symbols = [];
this.activeLayer = new Layer(); this.activeLayer = new Layer();
this.activeView = canvas ? new ProjectView(canvas) : null;
this._selectedItems = {}; this._selectedItems = {};
this._selectedItemCount = 0; this._selectedItemCount = 0;
this.setCurrentStyle(null); this._currentStyle = PathStyle.create(null);
}, },
getCurrentStyle: function() { getCurrentStyle: function() {
@ -41,7 +39,7 @@ var Project = this.Project = Base.extend({
}, },
setCurrentStyle: function(style) { setCurrentStyle: function(style) {
this._currentStyle = PathStyle.create(null, style); this._currentStyle.initialize(style);
}, },
activate: function() { activate: function() {
@ -55,9 +53,6 @@ var Project = this.Project = Base.extend({
remove: function() { remove: function() {
var res = Base.splice(this._scope.projects, null, this._index, 1); var res = Base.splice(this._scope.projects, null, this._index, 1);
this._scope = null; this._scope = null;
// Remove all views. This also removes the installed event handlers.
for (var i = this.views.length - 1; i >= 0; i--)
this.views[i].remove();
return !!res.length; return !!res.length;
}, },
@ -130,8 +125,10 @@ var Project = this.Project = Base.extend({
} }
}, },
/**
* @deprecated
*/
redraw: function() { redraw: function() {
for (var i = 0, l = this.views.length; i < l; i++) this._scope.view.draw();
this.views[i].draw();
} }
}); });

View file

@ -16,16 +16,17 @@
var CharacterStyle = this.CharacterStyle = PathStyle.extend({ var CharacterStyle = this.CharacterStyle = PathStyle.extend({
initialize: function(style) { initialize: function(style) {
this.fontSize = style.fontSize || 10; Base.initialize(this, style, {
this.font = style.font || 'sans-serif'; fontSize: 10,
font: 'sans-serif'
});
this.base(style); this.base(style);
}, },
statics: { statics: {
create: function(item, other) { create: function(item) {
var style = new CharacterStyle(CharacterStyle.dont); var style = new CharacterStyle(CharacterStyle.dont);
style._item = item; style._item = item;
style.initialize(other);
return style; return style;
} }
} }

View file

@ -15,21 +15,16 @@
*/ */
var ParagraphStyle = this.ParagraphStyle = Base.extend({ var ParagraphStyle = this.ParagraphStyle = Base.extend({
beans: true,
initialize: function(style) { initialize: function(style) {
this.justification = (style && style.justification) || 'left'; Base.initialize(this, style, {
}, justification: 'left'
});
clone: function() {
return new PathStyle(this);
}, },
statics: { statics: {
create: function(item, other) { create: function(item) {
var style = new ParagraphStyle(PathStyle.dont); var style = new CharacterStyle(CharacterStyle.dont);
style._item = item; style._item = item;
style.initialize(other);
return style; return style;
} }
} }

View file

@ -19,8 +19,7 @@ var PointText = this.PointText = TextItem.extend({
initialize: function(point) { initialize: function(point) {
this.base(); this.base();
point = Point.read(arguments, 0, 1); this._point = Point.read(arguments, 0);
this._point = point || new Point();
this.matrix = new Matrix().translate(this._point); this.matrix = new Matrix().translate(this._point);
}, },
@ -37,13 +36,13 @@ var PointText = this.PointText = TextItem.extend({
} }
}, },
// TODO: position should be the center point of the bounds
// but we currently don't support bounds for PointText.
getPosition: function() { getPosition: function() {
return this._point; return this._point;
}, },
setPosition: function(point) { setPosition: function(point) {
// TODO: position should be the center point of the bounds
// but we currently don't support bounds for PointText.
this.setPoint.apply(this, arguments); this.setPoint.apply(this, arguments);
}, },

View file

@ -19,9 +19,10 @@ var TextItem = this.TextItem = Item.extend({
initialize: function() { initialize: function() {
this.base(); this.base();
point = Point.read(arguments, 0, 1);
this.content = null; this.content = null;
this._characterStyle = CharacterStyle.create(this);
this.setCharacterStyle(this._project.getCurrentStyle()); this.setCharacterStyle(this._project.getCurrentStyle());
this._paragraphStyle = ParagraphStyle.create(this);
this.setParagraphStyle(); this.setParagraphStyle();
}, },
@ -30,7 +31,7 @@ var TextItem = this.TextItem = Item.extend({
}, },
setCharacterStyle: function(style) { setCharacterStyle: function(style) {
this._characterStyle = CharacterStyle.create(this, style); this._characterStyle.initialize(style);
}, },
getParagraphStyle: function() { getParagraphStyle: function() {
@ -38,6 +39,6 @@ var TextItem = this.TextItem = Item.extend({
}, },
setParagraphStyle: function(style) { setParagraphStyle: function(style) {
this._paragraphStyle = ParagraphStyle.create(this, style); this._paragraphStyle.initialize(style);
} }
}); });

View file

@ -65,7 +65,7 @@ var Key = this.Key = new function() {
var character = String.fromCharCode(charCode), var character = String.fromCharCode(charCode),
key = keys[keyCode] || character.toLowerCase(), key = keys[keyCode] || character.toLowerCase(),
handler = down ? 'onKeyDown' : 'onKeyUp', handler = down ? 'onKeyDown' : 'onKeyUp',
scope = ProjectView.focused && ProjectView.focused._scope, scope = View.focused && View.focused._scope,
tool = scope && scope.tool; tool = scope && scope.tool;
keyMap[key] = down; keyMap[key] = down;
if (tool && tool[handler]) { if (tool && tool[handler]) {

View file

@ -14,19 +14,17 @@
* All rights reserved. * All rights reserved.
*/ */
var ProjectView = this.ProjectView = Base.extend({ var View = this.View = Base.extend({
beans: true, beans: true,
// TODO: Add bounds parameter that defines position within canvas? // TODO: Add bounds parameter that defines position within canvas?
// Find a good name for these bounds, since #bounds is already the artboard // Find a good name for these bounds, since #bounds is already the artboard
// bounds of the visible area. // bounds of the visible area.
initialize: function(canvas) { initialize: function(canvas) {
// To go with the convention of never passing project to constructors, // Associate this view with the active paper scope.
// in all items, associate the view with the currently active project. this._scope = paper;
this._project = paper.project;
this._scope = this._project._scope;
// Push it onto project.views and set index: // Push it onto project.views and set index:
this._index = this._project.views.push(this) - 1; this._index = this._scope.views.push(this) - 1;
// Handle canvas argument // Handle canvas argument
var size; var size;
if (canvas && canvas instanceof HTMLCanvasElement) { if (canvas && canvas instanceof HTMLCanvasElement) {
@ -86,12 +84,8 @@ var ProjectView = this.ProjectView = Base.extend({
this._events = this._createEvents(); this._events = this._createEvents();
DomEvent.add(this._canvas, this._events); DomEvent.add(this._canvas, this._events);
// Make sure the first view is focused for keyboard input straight away // Make sure the first view is focused for keyboard input straight away
if (!ProjectView.focused) if (!View.focused)
ProjectView.focused = this; View.focused = this;
},
getProject: function() {
return this._project;
}, },
getViewBounds: function() { getViewBounds: function() {
@ -150,14 +144,19 @@ var ProjectView = this.ProjectView = Base.extend({
setZoom: function(zoom) { setZoom: function(zoom) {
// TODO: Clamp the view between 1/32 and 64, just like Illustrator? // TODO: Clamp the view between 1/32 and 64, just like Illustrator?
var mx = new Matrix(); this._transform(new Matrix().scale(zoom / this._zoom, this.getCenter()));
mx.scale(zoom / this._zoom, this._center);
this.transform(mx);
this._zoom = zoom; this._zoom = zoom;
}, },
scrollBy: function(point) { scrollBy: function(point) {
this.transform(new Matrix().translate(Point.read(arguments).negate())); this._transform(new Matrix().translate(Point.read(arguments).negate()));
},
_transform: function(matrix, flags) {
this._matrix.preConcatenate(matrix);
// Force recalculation of these values next time they are requested.
this._bounds = null;
this._inverse = null;
}, },
draw: function() { draw: function() {
@ -166,46 +165,40 @@ var ProjectView = this.ProjectView = Base.extend({
// Initial tests conclude that clearing the canvas using clearRect // Initial tests conclude that clearing the canvas using clearRect
// is always faster than setting canvas.width = canvas.width // is always faster than setting canvas.width = canvas.width
// http://jsperf.com/clearrect-vs-setting-width/7 // http://jsperf.com/clearrect-vs-setting-width/7
var bounds = this._viewBounds; var ctx =this._context,
this._context.clearRect(bounds._x, bounds._y, bounds = this._viewBounds;
ctx.clearRect(bounds._x, bounds._y,
// TODO: +1... what if we have multiple views in one canvas? // TODO: +1... what if we have multiple views in one canvas?
bounds._width + 1, bounds._height + 1); bounds._width + 1, bounds._height + 1);
this._project.draw(this._context);
ctx.save();
this._matrix.applyToContext(ctx);
// Just draw the active project for now
this._scope.project.draw(ctx);
ctx.restore();
}, },
activate: function() { activate: function() {
this._project.activeView = this; this._scope.view = this;
}, },
remove: function() { remove: function() {
var res = Base.splice(this._project.views, null, this._index, 1); var res = Base.splice(this._scope.views, null, this._index, 1);
// Uninstall event handlers again for this view. // Uninstall event handlers again for this view.
DomEvent.remove(this._canvas, this._events); DomEvent.remove(this._canvas, this._events);
this._project = this._scope = this._canvas = this._events = null;
// Clearing _onFrame makes the frame handler stop automatically. // Clearing _onFrame makes the frame handler stop automatically.
this._onFrame = null; this._scope = this._canvas = this._events = this._onFrame = null;
return !!res.length; return !!res.length;
}, },
transform: function(matrix, flags) {
this._matrix.preConcatenate(matrix);
// Force recalculation of these values next time they are requested.
this._bounds = null;
this._inverse = null;
},
_getInverse: function() {
if (!this._inverse)
this._inverse = this._matrix.createInverse();
return this._inverse;
},
// TODO: getInvalidBounds // TODO: getInvalidBounds
// TODO: invalidate(rect) // TODO: invalidate(rect)
// TODO: style: artwork / preview / raster / opaque / ink // TODO: style: artwork / preview / raster / opaque / ink
// TODO: getShowGrid // TODO: getShowGrid
// TODO: getMousePoint // TODO: getMousePoint
// TODO: artworkToView(rect) // TODO: artworkToView(rect)
// TODO: Consider naming these projectToView, viewToProject
artworkToView: function(point) { artworkToView: function(point) {
return this._matrix._transformPoint(Point.read(arguments)); return this._matrix._transformPoint(Point.read(arguments));
}, },
@ -214,6 +207,12 @@ var ProjectView = this.ProjectView = Base.extend({
return this._getInverse()._transformPoint(Point.read(arguments)); return this._getInverse()._transformPoint(Point.read(arguments));
}, },
_getInverse: function() {
if (!this._inverse)
this._inverse = this._matrix.createInverse();
return this._inverse;
},
/** /**
* Handler to be called whenever a view gets resized. * Handler to be called whenever a view gets resized.
*/ */
@ -279,7 +278,7 @@ var ProjectView = this.ProjectView = Base.extend({
function mousedown(event) { function mousedown(event) {
// Tell the Key class which view should receive keyboard input. // Tell the Key class which view should receive keyboard input.
ProjectView.focused = that; View.focused = that;
if (!(tool = that._scope.tool)) if (!(tool = that._scope.tool))
return; return;
curPoint = viewToArtwork(event); curPoint = viewToArtwork(event);

View file

@ -37,10 +37,14 @@ test('clockwise', function() {
return path3.clockwise; return path3.clockwise;
}, true); }, true);
new CompoundPath([ var compound = new CompoundPath(path1, path2, path3);
path1, path2, path3
]);
equals(function() {
return compound.lastChild == path3;
}, true);
equals(function() {
return compound.firstChild == path1;
}, true);
equals(function() { equals(function() {
return path1.clockwise; return path1.clockwise;
}, true); }, true);

View file

@ -23,16 +23,17 @@ test('Group bounds', function() {
strokeWidth: 5, strokeWidth: 5,
strokeColor: 'black' strokeColor: 'black'
}; };
var path = new Path.Circle([150, 150], 60); var path = new Path.Circle([150, 150], 60);
var secondPath = new Path.Circle([175, 175], 85); var secondPath = new Path.Circle([175, 175], 85);
var group = new Group([path, secondPath]); var group = new Group([path, secondPath]);
compareRectangles(group.bounds, { x: 90, y: 90, width: 170, height: 170 }); compareRectangles(group.bounds, { x: 90, y: 90, width: 170, height: 170 }, 'group.bounds');
compareRectangles(group.strokeBounds, { x: 87.5, y: 87.5, width: 175, height: 175 }); compareRectangles(group.strokeBounds, { x: 87.5, y: 87.5, width: 175, height: 175 }, 'group.strokeBounds');
group.rotate(20); group.rotate(20);
compareRectangles(group.bounds, { x: 89.97681, y: 82.94095, width: 170.04639, height: 177.08224 }); compareRectangles(group.bounds, { x: 89.97681, y: 82.94095, width: 170.04639, height: 177.08224 }, 'group.bounds');
compareRectangles(group.strokeBounds, { x: 87.47681, y: 80.44095, width: 175.04639, height: 182.08224 }); compareRectangles(group.strokeBounds, { x: 87.47681, y: 80.44095, width: 175.04639, height: 182.08224 }, 'group.strokeBounds');
group.rotate(20, new Point(50, 50)); group.rotate(20, new Point(50, 50));
compareRectangles(group.bounds, { x: 39.70692, y: 114.99196, width: 170.00412, height: 180.22401 }); compareRectangles(group.bounds, { x: 39.70692, y: 114.99196, width: 170.00412, height: 180.22401 }, 'group.bounds');
compareRectangles(group.strokeBounds, { x: 37.20692, y: 112.49196, width: 175.00412, height: 185.22401 }); compareRectangles(group.strokeBounds, { x: 37.20692, y: 112.49196, width: 175.00412, height: 185.22401 }, 'group.strokeBounds');
}); });

View file

@ -42,10 +42,10 @@ test('clone()', function() {
}, true); }, true);
}); });
test('appendChild(item)', function() { test('appendTop(item)', function() {
var proj = paper.project; var proj = paper.project;
var path = new Path(); var path = new Path();
proj.activeLayer.appendChild(path); proj.activeLayer.appendTop(path);
equals(function() { equals(function() {
return proj.activeLayer.children.length; return proj.activeLayer.children.length;
}, 1); }, 1);
@ -55,7 +55,7 @@ test('item.parent / item.isChild / item.isParent', function() {
var proj = paper.project; var proj = paper.project;
var secondDoc = new Project(); var secondDoc = new Project();
var path = new Path(); var path = new Path();
proj.activeLayer.appendChild(path); proj.activeLayer.appendTop(path);
equals(function() { equals(function() {
return proj.activeLayer.children.indexOf(path) != -1; return proj.activeLayer.children.indexOf(path) != -1;
}, true); }, true);