CSS transforms now being used for rotation.
authordsc <david.schoonover@gmail.com>
Thu, 4 Nov 2010 08:54:23 +0000 (01:54 -0700)
committerdsc <david.schoonover@gmail.com>
Thu, 4 Nov 2010 08:54:23 +0000 (01:54 -0700)
src/Y/core.js
src/Y/y-function.js
src/Y/y-op.js
src/portal/layer.js
src/portal/shape.js
src/portal/util/loc.js
src/tanks/game/map.js
src/tanks/util/grid.js

index 4536fb6..19e33ae 100644 (file)
@@ -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 )
index 01edb5a..bc08bf2 100644 (file)
@@ -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] )
index 36394e3..5017606 100644 (file)
@@ -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;
+        };
     }
     
 };
index 244a684..62e2da1 100644 (file)
@@ -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('<canvas />');
         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.
      */
index b36a333..02a9a7a 100644 (file)
@@ -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();
     }
     
index 7228012..5e400c3 100644 (file)
@@ -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()+']';
     }
     
 });
index 2a1d21a..a154714 100644 (file)
@@ -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 = {};
index 43e6140..497da96 100644 (file)
@@ -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
         ;