ctx : null,
dirty : true,
- canvasWidth : 0, layerWidth : 0,
+ canvasWidth : 0, layerWidth : 0,
canvasHeight : 0, layerHeight : 0,
- x0: 0, y0: 0,
-
- // Rotational origin of the layer (or shape)
- _origin : null, // loc
+ x: 0, y: 0, loc : null, // Position relative to parent
// Bleeds are marks outside the declared layer-size
- negBleed : null, // loc
- posBleed : null, // loc
+ negBleed : null, // Loc
+ posBleed : null, // Loc
// Transforms
- transforms : null, // YArray
+ transform : null, // Object
/// Setup ///
init : function init(){
- this._loc = new Loc(0,0);
- this._origin = new Loc(0,0);
- this._offset = new Loc(0,0);
-
this.children = new Y.YArray();
- var transforms = this.transforms = new Y.YArray();
- transforms.rotation = 0;
- transforms.scale = new Loc(1.0,1.0);
- transforms.translate = new Loc(0,0);
+ this.loc = new Loc(0,0);
+ this.negBleed = new Loc(0,0);
+ this.posBleed = new Loc(0,0);
+
+ this.boundingBox = new Loc.Rect(0,0, 0,0);
+
+ this.transform = {
+ origin : new Loc('50%','50%'),
+ rotate : 0,
+ scale : new Loc(1.0,1.0),
+ translate : new Loc(0,0)
+ };
this.canvas = jQuery('<canvas />');
this.ctx = this.canvas[0].getContext('2d');
.addClass(this._cssClasses)
.append(this.canvas);
- this.layer[0].layer = this;
+ this.layer[0].layer =
+ this.canvas[0].layer = this;
},
/// Scene Graph Heirarchy ///
/** @param {Layer} child */
append : function append(child){
new Y(arguments).invoke('appendTo', this);
- // if (child) child.appendTo(this);
return this;
},
*/
width : function width(w){
if (w === undefined)
- return this.layer.width();
+ return this.layerWidth;
this.layerWidth = w;
this.layer.width(w);
- // We want a canvas larger than our viewport so rotation doesn't require a
- // bunch of math. We only really need v*sqrt(2), but whatever, this is faster.
- var off = this._offset.x
- , w2 = this.canvasWidth = w*2 + off;
+
+ var nb = this.negBleed.x
+ , v = this.canvasWidth = w + nb + this.posBleed.x;
+ this.canvas[0].width = v;
this.canvas.css({
- 'width' : w2+'px',
- 'margin-left' : (-1 * w/2 + off)+'px'
+ 'width' : v+'px',
+ 'margin-left' : (-nb)+'px'
});
- this.canvas[0].width = w2;
return this;
},
height : function height(h){
if (h === undefined)
- return this.layer.height();
+ return this.layerHeight;
- this._height = h;
+ this.layerHeight = h;
this.layer.height(h);
- var h2 = h*2;
+ var nb = this.negBleed.y
+ , v = this.canvasHeight = h + nb + this.posBleed.y;
+ this.canvas[0].height = v;
this.canvas.css({
- 'height' : h2+'px',
- 'margin-top': (-h/2)+'px'
+ 'height' : v+'px',
+ 'margin-top' : (-nb)+'px'
});
- this.canvas[0].height = h2;
return this;
},
+ /**
+ * position() -> object
+ * Gets position of the layer relative to the parent.
+ */
+ /**
+ * position(top, left) -> this
+ * Sets the position of this node, and then returns it.
+ * @param {Number|String|undefined} top If omitted, this method must be invoked with `undefined` as the first argument.
+ * @param {Number|String|undefined} left
+ */
+ /**
+ * position(pos) -> this
+ * Sets the position of this node, and then returns it.
+ * @param {Object} pos An object with "top" and/or "left" properties as in `position(top, left)`.
+ */
+ position : function position(left, top){
+ if (top === undefined && left === undefined)
+ return this.layer.position();
+
+ if (top && Y.isPlainObject(top))
+ var pos = top;
+ else
+ var pos = { 'top': top, 'left':left };
+
+ // if (pos.left !== undefined) pos.left -= this.offsetX;
+ // if (pos.top !== undefined) pos.top -= this.offsetY;
+
+ this.css(pos);
+ return this;
+ },
+
+ hide : makeDelegate('hide'),
+ show : makeDelegate('show'),
+
+ /** CSS properties */
+ css : makeDelegate('css'),
+
+ /** Position relative to document. */
+ offset : makeDelegate('offset'),
+
+
+
+ /// Transformations ///
+
+ /**
+ * Gets and sets the transformation origin.
+ */
origin : function origin(x,y){
- var o = this._origin;
+ var o = this.transform.origin;
if (arguments.length === 0)
- return o.clone();
+ return o.absolute(this.layerWidth, this.layerHeight);
o.x = x;
o.y = y;
- return this;
+ // this.dirty = true;
+ return this._applyTransforms();
},
/**
- * Rotates this layer by theta radians, and then applies rotation relatively
- * to all sublayers (preserving knowledge of their individual rotations).
+ * Rotates this layer by r radians.
*/
- rotate : function rotate(rotation){
- if (rotation === undefined)
- return this.transforms.rotation;
-
- // Record my relative rotation...
- this.transforms.rotation = rotation;
+ rotate : function rotate(r){
+ var t = this.transform;
+ if (r === undefined)
+ return t.rotate;
- // Propogate...
- var p = this.parent;
- return this._rotate(p ? p.rotation : 0);
+ t.rotate = r;
+ return this._applyTransforms();
},
- _rotate : function _rotate(cumRotation){
- this.dirty = true;
- var absR = this.absRotation = this.rotation+cumRotation;
- this.children.invoke('_rotate', absR);
- return this;
- },
-
-
/**
* Scales this layer by (sx,sy), and then applies scaling relatively
* to all sublayers (preserving knowledge of their individual scaling).
return this;
},
- hide : makeDelegate('hide'),
- show : makeDelegate('show'),
-
- /** Position relative to document. */
- offset : makeDelegate('offset'),
-
- /**
- * position() -> object
- * Gets position of the layer relative to the parent.
- */
- /**
- * position(top, left) -> this
- * Sets the position of this node, and then returns it.
- * @param {Number|String|undefined} top If omitted, this method must be invoked with `undefined` as the first argument.
- * @param {Number|String|undefined} left
- */
- /**
- * position(pos) -> this
- * Sets the position of this node, and then returns it.
- * @param {Object} pos An object with "top" and/or "left" properties as in `position(top, left)`.
- */
- position : function position(left, top){
- if (top === undefined && left === undefined)
- return this.layer.position();
-
- if (top && Y.isPlainObject(top))
- var pos = top;
- else
- var pos = { 'top': top, 'left':left };
-
- // if (pos.left !== undefined) pos.left -= this.offsetX;
- // if (pos.top !== undefined) pos.top -= this.offsetY;
-
- this.css(pos);
+ // origin : new Loc('50%','50%'),
+ // rotate : 0,
+ // scale : new Loc(1.0,1.0),
+ // translate : new Loc(0,0)
+ _applyTransforms : function _applyTransforms(){
+ var t = this.transform, tfns = [];
+
+ if (t.rotate !== 0)
+ tfns.push('rotate('+t.rotate+'rad)');
+
+ if (t.scale.x !== 1 || t.scale.y !== 1)
+ tfns.push('scale('+t.scale.x+','+t.scale.y+')');
+
+ var trans = (tfns.length ? tfns.join(' ') : 'none')
+ , o = t.origin.toUnits()
+ , origin = o.x+' '+o.y ;
+ this.layer.css(
+ ['', '-moz-', '-webkit-'].reduce(
+ function(values, prefix){
+ values[prefix+'transform'] = trans;
+ values[prefix+'origin'] = origin;
+ return values;
+ }, {}) );
return this;
},
- /** CSS properties */
- css : makeDelegate('css'),
-
/// Drawing Functions ///
// for debugging
- point : function point(x,y, color, noTransforms){
+ point : function point(x,y, color){
var ctx = this.ctx;
this._openPath(ctx);
- if (!noTransforms) this._applyTransforms(ctx);
var r = 2;
ctx.arc(x+r,y+r, r, 0, Math.PI*2, false);
},
_openPath : function _openPath(ctx){
- var w = this.canvas.width()
- , h = this.canvas.height();
+ var w = this.canvasWidth
+ , h = this.canvasHeight
+ , neg = this.negBleed
+ ;
ctx.beginPath();
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(-w,-h, 2*w,2*h);
+ ctx.translate(neg.x, neg.y);
- ctx.translate(w/4 + this.originX, h/4 + this.originY);
- ctx.rotate(this.absRotation);
- ctx.translate(-this.originX, -this.originY);
+ // ctx.rotate(this.absRotation);
+ // ctx.translate(-this.originX, -this.originY);
- ctx.scale(this.absScaleX, this.absScaleY);
+ // ctx.scale(this.absScaleX, this.absScaleY);
return this;
},
- _applyTransforms : function _applyTransforms(){
-
- },
-
/**
* To be implemented by subclasses.
*/
return Loc.Square.fromLoc(this.x, this.y);
},
+ absolute : function absolute(w,h){
+ var x = this.x, y = this.y
+ return new Loc( Y.isString(x) ? parseFloat(x.slice(0,-1))/100 * w : x
+ , Y.isString(y) ? parseFloat(y.slice(0,-1))/100 * h : y );
+ },
+
+ toUnits : function toUnits(units){
+ units = units || 'px';
+ var x = this.x, y = this.y;
+ return { 'x' : Y.isNumber(x) ? x+units : x
+ , 'y' : Y.isNumber(y) ? y+units : y };
+ },
+
toString : function toString(){
- return '('+Math.round(this.x, 2)+','+Math.round(this.y, 2)+')';
+ var x = this.x, y = this.y;
+ x = Y.isNumber(x) ? Math.round(x,2) : x;
+ y = Y.isNumber(y) ? Math.round(y,2) : y;
+ return '('+x+','+y+')';
}
});
set : function set(k, v, def){
v = (v !== undefined ? v : def);
+
switch (k) {
- case 'x1': case 0: case 'x':
- this.x1 = this[0] = this.x = this.top.x = this.left.x = v;
- break;
- case 'y1': case 1: case 'y':
- this.y1 = this[1] = this.y = this.top.y = this.left.y = v;
- break;
- case 'x2': case 2:
- this.x2 = this[2] = this.bottom.x = this.right.x = v;
- break;
- case 'y1': case 3:
- this.y2 = this[3] = this.bottom.y = this.right.y = v;
- break;
- default:
- this[k] = v;
+ case 'x1': case 0: case 'x': this.x1 = this[0] = this.x = v; break;
+ case 'y1': case 1: case 'y': this.y1 = this[1] = this.y = v; break;
+ case 'x2': case 2: this.x2 = this[2] = v; break;
+ case 'y1': case 3: this.y2 = this[3] = v; break;
+
+ default: this[k] = v;
}
return this;
},
attr : Y.attr.methodize(),
- // XXX: This would slow performance, but prevent state duplication and out of sync errors
-
- // top : function(){ return new Loc(this.x1, this.y1); },
- // bottom : function bottom(){ return new Loc(this.x2, this.y2); },
- //
- // left : function(){ return new Loc(this.x1, this.y1); },
- // right : function(){ return new Loc(this.x2, this.y2); },
+ top : function top(x1,y1){
+ if ( x1 !== undefined && y1 !== undefined )
+ return new Loc(this.x1, this.y1);
+ else
+ return this.attr({ 'x1':x1, 'y1': y1 });
+ },
+ bottom : function bottom(){ return new Loc(this.x2, this.y2); },
+ left : function left(){ return new Loc(this.x1, this.y1); },
+ right : function right(){ return new Loc(this.x2, this.y2); },
moveTo : function moveTo(x,y){
return new Loc.Rect(x,y, x+this.width,y+this.height);
},
midpoint : function midpoint(){
- return new Loc(
- this.x1 + this.width /2,
- this.y1 + this.height/2 );
+ return new Loc( this.x1 + this.width /2
+ , this.y1 + this.height/2 );
},
clone : function clone(){
- return new Loc.Rect(
- this.top.clone(),
- this.bottom.clone() );
+ return new Loc.Rect( this.top.clone()
+ , this.bottom.clone() );
},
contains : function contains(x,y){
},
toString : function toString(){
- return '['+this.top+' '+this.bottom+']';
+ return '['+this.top()+' '+this.bottom()+']';
}
});