From 786b840c19b7d8817da7c882b213fd297df4dc8b Mon Sep 17 00:00:00 2001 From: dsc Date: Thu, 4 Nov 2010 01:54:23 -0700 Subject: [PATCH] CSS transforms now being used for rotation. --- src/Y/core.js | 4 +- src/Y/y-function.js | 21 ++--- src/Y/y-op.js | 29 +++--- src/portal/layer.js | 226 ++++++++++++++++++++++++++---------------------- src/portal/shape.js | 10 ++- src/portal/util/loc.js | 67 ++++++++------ src/tanks/game/map.js | 4 +- src/tanks/util/grid.js | 4 +- 8 files changed, 195 insertions(+), 170 deletions(-) diff --git a/src/Y/core.js b/src/Y/core.js index 4536fb6..19e33ae 100644 --- a/src/Y/core.js +++ b/src/Y/core.js @@ -9,9 +9,9 @@ function reduce(o, fn, acc, cxt){ if ( !o ) return acc; - fn = Function.toFunction(fn); + // fn = Function.toFunction(fn); if ( notSelfOrWrapped(o.reduce) ) - return o.reduce.apply(o, slice.call(arguments,1)); + return o.reduce.apply(o, [fn, acc, cxt]); cxt = cxt || o; for ( var name in o ) diff --git a/src/Y/y-function.js b/src/Y/y-function.js index 01edb5a..bc08bf2 100644 --- a/src/Y/y-function.js +++ b/src/Y/y-function.js @@ -192,28 +192,24 @@ YFunction.prototype.memoize = methodize(memoize); * @param {Object} [cacher] Object on which to store the memorization cache for lookups. Useful for meta-caches. Defaults to the memorized function. * @param {String} [cacheName="cache"] Property this cache will be stored under. */ -function memoize(fn, keyfn, cacher, cacheName){ +function memoize(fn){ if (fn.__memoized__) return fn.__memoized__; var m = fn.__memoized__ = function memorizer(){ - var key = keyfn(Y(arguments)) - , cache = cacher[cacheName] ; + // toJSON would be a better alternative, but that won't work cross-browser + var key = Y(arguments).join('\0\0\0') + , cache = arguments.callee.cache; if ( !(key in cache) ) cache[key] = fn.apply(this, arguments); return cache[key]; }; - // toJSON would be a better alternative, but that won't work cross-browser - keyfn = keyfn || function keyfn(args){ return args.join('\0\0\0'); }; - cacher = cacher || m; - cacheName = cacheName || 'cache'; - m.__wraps__ = fn; m.purge = function purge(){ - var cache = cacher[cacheName]; - cacher[cacheName] = {}; + var cache = this.cache; + this.cache = {}; return cache; }; @@ -249,13 +245,10 @@ function aritize(n){ }; YFunction.prototype.limit = -function limit(fn, n){ +function limit(n){ var fn = this , cache = fn.__limited__ ; - if (fn.length === n) - return fn; - if ( !cache ) cache = fn.__limited__ = {}; else if ( cache[n] ) diff --git a/src/Y/y-op.js b/src/Y/y-op.js index 36394e3..5017606 100644 --- a/src/Y/y-op.js +++ b/src/Y/y-op.js @@ -35,27 +35,26 @@ Y.op = { rshift: function(x,y){ return x >> y; }, zrshift: function(x,y){ return x >>> y; }, - // typecast - bool: function(x){ return !!x; }, - number: function(x){ return Number(x); }, - int: function(x){ return parseInt(x); }, - float: function(x){ return parseFloat(x); }, - str: function(x){ return String(x); }, - - // functional - I: function(x){ return x; }, - K: function(k){ return function(){ return k; }; }, + // values nop: function(){}, + I: function(x){ return x; }, + K: function(k){ return function(){ return k; }; }, + val: function(def,o){ return o !== undefined ? o : def; }, + ok: function(o){ return o !== undefined && o !== null; }, - // accessors - isset: function(def,o){ return o !== undefined ? o : def; }, + // values & accessors has: function(k,o){ return k in o; }, - get: function(k,o){ return o[k] }, + get: function(k,o){ return o[k] }, getdef: function(def,k,o){ return (k in o ? o[k] : def); }, - set: function(o,k,v){ if (o) o[k] = v; return o; }, + set: function(o,k,v){ if (o && k !== undefined) o[k] = v; return o; }, method: function(name){ var args = Y(arguments,1); - return function(obj){ return obj[name].apply(obj, args); }; + return function(obj){ + if (obj && obj[name]) + return obj[name].apply( obj, args.concat(Y(arguments)) ); + else + return obj; + }; } }; diff --git a/src/portal/layer.js b/src/portal/layer.js index 244a684..62e2da1 100644 --- a/src/portal/layer.js +++ b/src/portal/layer.js @@ -11,36 +11,37 @@ Layer = new Y.Class('Layer', { 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(''); this.ctx = this.canvas[0].getContext('2d'); @@ -49,7 +50,8 @@ Layer = new Y.Class('Layer', { .addClass(this._cssClasses) .append(this.canvas); - this.layer[0].layer = this; + this.layer[0].layer = + this.canvas[0].layer = this; }, /// Scene Graph Heirarchy /// @@ -57,7 +59,6 @@ Layer = new Y.Class('Layer', { /** @param {Layer} child */ append : function append(child){ new Y(arguments).invoke('appendTo', this); - // if (child) child.appendTo(this); return this; }, @@ -122,75 +123,111 @@ Layer = new Y.Class('Layer', { */ 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). @@ -224,55 +261,40 @@ Layer = new Y.Class('Layer', { 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); @@ -300,26 +322,24 @@ Layer = new Y.Class('Layer', { }, _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. */ diff --git a/src/portal/shape.js b/src/portal/shape.js index b36a333..02a9a7a 100644 --- a/src/portal/shape.js +++ b/src/portal/shape.js @@ -33,9 +33,13 @@ Shape = new Y.Class('Shape', Layer, { _calcDimension : function _calcDimension(which, values){ values.unshift(0); var self = this - , off = -1 * Math.min.apply(Math, values); + , neg = -1 * Math.min.apply(Math, values) + // , pos = Math.min(0, Math.max.apply(Max, values) - max) + ; + + self.negBleed.attr(which, neg); + // self.posBleed.attr(which, pos); - self._offset.attr(which, off); return values.map(function(v, i){ return (self[which+i] = v); }); @@ -55,7 +59,7 @@ Rect = new Y.Class('Rect', Shape, { }, drawShape : function drawShape(ctx){ - ctx.rect(0,0, this._width,this._height); + ctx.rect(0,0, this.canvasWidth,this.canvasHeight); ctx.fill(); } diff --git a/src/portal/util/loc.js b/src/portal/util/loc.js index 7228012..5e400c3 100644 --- a/src/portal/util/loc.js +++ b/src/portal/util/loc.js @@ -52,8 +52,24 @@ Loc = new Y.Class('Loc', [], { 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+')'; } }); @@ -111,49 +127,42 @@ Loc.Rect = new Y.Class('Rect', [], { 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){ @@ -162,7 +171,7 @@ Loc.Rect = new Y.Class('Rect', [], { }, toString : function toString(){ - return '['+this.top+' '+this.bottom+']'; + return '['+this.top()+' '+this.bottom()+']'; } }); diff --git a/src/tanks/game/map.js b/src/tanks/game/map.js index 2a1d21a..a154714 100644 --- a/src/tanks/game/map.js +++ b/src/tanks/game/map.js @@ -11,8 +11,8 @@ Y(Game.prototype).extend({ .appendTo(this.viewport); this.level = new Layer() - .width( root._width ) - .height( root._height ) + .width( root.layerWidth ) + .height( root.layerHeight ) .appendTo(this.root); this.byId = {}; diff --git a/src/tanks/util/grid.js b/src/tanks/util/grid.js index 43e6140..497da96 100644 --- a/src/tanks/util/grid.js +++ b/src/tanks/util/grid.js @@ -12,8 +12,8 @@ Grid = new Y.Class('Grid', Rect, { var size = this.size , rows = this.rows , cols = this.cols - , w = this._width - , h = this._height + , w = this.canvasWidth + , h = this.canvasHeight // , cx = -this.originX // , cy = -this.originY ; -- 1.7.0.4