From: dsc Date: Mon, 28 Feb 2011 14:57:08 +0000 (-0800) Subject: Reduces number of calls to jQuery and coerces floats to ints before positioning. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=d0258bbc6ef418d38ac6d9b065fb6cbe4a1ffd6e;p=tanks.git Reduces number of calls to jQuery and coerces floats to ints before positioning. --- diff --git a/src/Y/op.cjs b/src/Y/op.cjs index 21021f7..5b7aeed 100644 --- a/src/Y/op.cjs +++ b/src/Y/op.cjs @@ -43,9 +43,15 @@ var core = require('Y/core') K : function(k){ return function(){ return k; }; }, kObject : function(){ return {}; }, kArray : function(){ return []; }, - nth : function(n){ return function(){ return arguments[n]; }; }, val : function(def,o){ return o !== undefined ? o : def; }, ok : function(o){ return o !== undefined && o !== null; }, + first : function(a){ return a; }, + second : function(_,a){ return a; }, + nth : function(n){ + if (n === 0) return op.first; + if (n === 1) return op.second; + return function(){ return arguments[n]; }; + }, // reduce-ordered values & accessors khas : function(k,o){ return k in o; }, @@ -74,11 +80,15 @@ var core = require('Y/core') // misc end : function end(o){ return ((o && o.__y__) ? o.end() : o); }, - parseBool : function(s){ - var i = parseInt(s); - return isNaN(i) ? (s && s.toLowerCase() !== 'false') : i; - } + parseBool : parseBool, + toBool : parseBool, + toInt : function(s){ return parseInt(s) } }; +function parseBool(s){ + var i = parseInt(s); + return isNaN(i) ? (s && s.toLowerCase() !== 'false') : i; +} + core.extend(exports, op); diff --git a/src/Y/types/collection.cjs b/src/Y/types/collection.cjs index 46bbff5..d30fbcf 100644 --- a/src/Y/types/collection.cjs +++ b/src/Y/types/collection.cjs @@ -7,6 +7,8 @@ var Y = require('Y/y').Y , slice = core.slice , internal = require('Y/internal') +, first = op.nth(0) +, second = op.nth(1) ; @@ -76,8 +78,7 @@ YBase.subclass('YCollection', { 'map' : function map( fn ){ var cxt = arguments[1] || this; return this.reduce(function(acc, v, k, o){ - var _v = fn.call(cxt, v, k, o); - return acc.attr(k, _v); + return acc.attr(k, fn.call(cxt, v, k, o)); }, new this.__class__() ); }, @@ -97,6 +98,13 @@ YBase.subclass('YCollection', { }, new this.__class__() ); }, + amap : function amap( fn ){ + var cxt = arguments[1] || this; + return this.reduce(function(acc, v, k, o){ + return acc.push(fn.call(cxt, v, k, o)); + }, Y([]) ); + }, + // 'find' : function find( fn ){ // var cxt = arguments[1] || this; // @@ -140,19 +148,16 @@ YBase.subclass('YCollection', { }, 'pluck' : function pluck(key){ - return this.map(function(v){ - return del.attr(v, key); - }); + return this.map(plucker, key); }, 'invoke' : function invoke(name){ var args = slice.call(arguments,1); - return del.map(this, function(o){ - return (o && type.isFunction(o[name])) ? o[name].apply(o, args) : null; - }); + return del.map(this, invoker, [name, args]); // XXX: why is this del.map and not this.map? }, - // FIXME: this._o[name].apply + + // FIXME: this._o[name].apply? 'apply' : function apply(name, args){ return this[name].apply(this, args); }, @@ -177,7 +182,16 @@ YBase.subclass('YCollection', { return this.filter(A.has, A); } - }); +// `this` == [name, args] +function invoker(o){ + var name = this[0], args = this[1]; + return (o && (typeof o[name] == 'function')) ? o[name].apply(o, args) : null; +} + +// `this` == key +function plucker(v){ + return del.attr(v, this); +} diff --git a/src/ezl/layer/layerable.cjs b/src/ezl/layer/layerable.cjs index 5c46bfc..40af4a6 100644 --- a/src/ezl/layer/layerable.cjs +++ b/src/ezl/layer/layerable.cjs @@ -54,14 +54,15 @@ Mixin.subclass('Layerable', { animQueue : null, _erased : null, - layerWidth : 0, canvasWidth : 0, realWidth : 0, - layerHeight : 0, canvasHeight : 0, realHeight : 0, + realWidth : 0, layerWidth : 0, canvasWidth : 0, + realHeight : 0, layerHeight : 0, canvasHeight : 0, - x: 0, y: 0, loc : null, // Position relative to parent + loc : null, // Position relative to parent (float) + layerX : 0, layerY: 0, // Position cast to int and cached to avoid DOM hits // Bleeds are marks outside the declared layer-size - negBleed : null, // Loc - posBleed : null, // Loc + negBleed : null, // Loc(0,0) + posBleed : null, // Loc(0,0) // Transforms _origin : null, // rotational origin @@ -247,37 +248,75 @@ Mixin.subclass('Layerable', { if (w === undefined && h === undefined) return new Vec(this.layerWidth,this.layerHeight); - if (w === null) w = this.realWidth; - if (h === null) h = this.realHeight; - - this.realWidth = w; - this.realHeight = h; + var bb = this.bbox + , ox1 = bb.x1, oy1 = bb.y1 + , ow = this.realWidth, lw, olw = lw = this.layerWidth, cw + , oh = this.realHeight, lh, olh = lh = this.layerHeight, ch + , changes = {}, cchanges = {} + , dirtied = false + ; - // HTMLElement.{width,height} is a long - this.layerWidth = Math.round(w); - this.layerHeight = Math.round(h); + if (w === null || w === undefined) w = ow; + if (h === null || h === undefined) h = oh; - var bb = this.bbox.resize(w,h) - , nb = this.negBleed, pb = this.posBleed + if (w !== ow) { + this.realWidth = w; + lw = Math.round(w); // HTMLElement.{width,height} is a long + if (lw !== olw) { + this.layerWidth = changes.width = lw; + this.canvasWidth = cchanges.width = cw = Math.ceil(w); + dirtied = true; + } + } - , cw = this.canvasWidth = Math.ceil(w + nb.x + pb.x) - , ch = this.canvasHeight = Math.ceil(h + nb.y + pb.y) - ; + if (h !== oh) { + this.realHeight = h; + lh = Math.round(h); // HTMLElement.{width,height} is a long + if (lh !== olh) { + this.layerHeight = changes.height = lh; + this.canvasHeight = cchanges.height = ch = Math.ceil(h); + dirtied = true; + } + } - this.layer.css({ - 'width' : w, 'height' : h, - 'left' : bb.x1, 'top' : bb.y1 - }); - this.canvas.css({ - 'width' : cw, 'height' : ch, - 'margin-left' : -nb.x, 'margin-top' : -nb.y - }); - var el = this.canvas[0]; - if (el) { - el.width = cw; - el.height = ch; + if (dirtied) { + bb.resize(w,h); + + var x1 = bb.x1 | 0 + , y1 = bb.y1 | 0; + if (ox1 !== x1) + changes.left = x1; + if (oy1 !== y1) + changes.top = y1; + + this.layer.css(changes); + + var el = this.canvas[0]; + if (el) { + this.canvas.css(cchanges); + el.width = cw; + el.height = ch; + } } + // If I find I ever need bleeds again, I'll work out the caching implications of them changing + // var ocw = this.canvasWidth, och = this.canvasHeight + // , nb = this.negBleed, pb = this.posBleed + // , cw = this.canvasWidth = Math.ceil(w + nb.x + pb.x) + // , ch = this.canvasHeight = Math.ceil(h + nb.y + pb.y) + // , canvasDirtied = false + // ; + // + // this.canvas.css({ + // 'width' : cw, 'height' : ch, + // 'margin-left' : -nb.x, 'margin-top' : -nb.y + // }); + // var el = this.canvas[0]; + // if (el) { + // el.width = cw; + // el.height = ch; + // } + return this; }, @@ -287,53 +326,13 @@ Mixin.subclass('Layerable', { width : function width(w){ if (w === undefined) return this.layerWidth; - - this.layerWidth = w; - - var bb = this.bbox.resize(w, this.layerHeight) - // , ro = bb.relOrigin - , nb = this.negBleed - , cw = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long - - this.layer.css({ - 'width' : w, - 'left' : bb.x1, - // 'margin-left' : -ro.x - }); - - this.canvas.css({ - 'width' : cw, - 'margin-left' : -nb.x - }); - if (this.canvas.length) this.canvas[0].width = cw; - - return this; + return this.size(w,null); }, height : function height(h){ if (h === undefined) return this.layerHeight; - - this.layerHeight = h; - - var bb = this.bbox.resize(this.layerWidth, h) - // , ro = bb.relOrigin - , nb = this.negBleed - , ch = this.canvasHeight = Math.ceil(h + nb.y + this.posBleed.y); // HTMLCanvas.height is a long - - this.layer.css({ - 'height' : h, - 'top' : bb.y1, - // 'margin-top' : -ro.y - }); - - this.canvas.css({ - 'height' : ch, - 'margin-top' : -nb.y - }); - if (this.canvas.length) this.canvas[0].height = ch; - - return this; + return this.size(null,h); }, /** @@ -344,8 +343,8 @@ Mixin.subclass('Layerable', { /** * position(x,y) -> {this} * Sets the position of this node, and then returns it. - * @param {Number|String|undefined} x - * @param {Number|String|undefined} y If omitted, this method must be invoked with `undefined` as the first argument. + * @param {Number|String|undefined} [x] + * @param {Number|String|undefined} [y] If x is omitted, this method must be invoked with `undefined` as the first argument. * @return {this} */ /** @@ -356,20 +355,32 @@ Mixin.subclass('Layerable', { */ position : function position(x,y){ if (x === undefined && y === undefined) - return this.layer.position(); + return this.loc; if ( x instanceof Array ) { y = x[_Y]; x = x[_X]; - } else if ( Y.isPlainObject(x) ){ + } else if ( x !== null && typeof x == 'object' && !(x instanceof Number) ){ y = ('top' in x ? x.top : x.y); x = ('left' in x ? x.left : x.x); } + if (x === null || x === undefined) x = bb.x1; + if (y === null || y === undefined) y = bb.y1; - var bbox = this.bbox.relocate(x,y); - this.css({ - 'left' : bbox.x1, - 'top' : bbox.y1 - }); + var bb = this.bbox; + if (x === bb.x1 && y === bb.y1) + return this; + + bb.relocate(x,y); + this.loc = bb.loc; + var ix = bb.x1 | 0 + , iy = bb.y1 | 0 + , changes = {}, dirtied = false + ; + + if (ix !== this.layerX) { changes.left = this.layerX = ix; dirtied = true; } + if (iy !== this.layerY) { changes.top = this.layerY = iy; dirtied = true; } + + if (dirtied) this.layer.css(changes); return this; }, @@ -773,4 +784,4 @@ $(function(){ '.ezl.layer canvas { z-index:0; }' ].join('\n')) .appendTo('head'); -}); +}); \ No newline at end of file diff --git a/src/ezl/loc/boundingbox.cjs b/src/ezl/loc/boundingbox.cjs index dc0b4ae..cff66a6 100644 --- a/src/ezl/loc/boundingbox.cjs +++ b/src/ezl/loc/boundingbox.cjs @@ -20,6 +20,14 @@ Rect.subclass('BoundingBox', { }, /** + * @protected + * Called by Rect and BoundingBox whenever dirtied. + */ + _uncache : function _uncache(){ + this._loc = this._relOrigin = null; + }, + + /** * Changes origin position without moving the bounds. * Use relocate() to change the position by moving the origin. */ @@ -30,28 +38,50 @@ Rect.subclass('BoundingBox', { * Changes origin x-position without moving the bounds. * Use relocate() to change the position by moving the origin. */ - set originX(v){ this._origin.x = v - this.x1; return v; }, - get originX(){ return this.absOrigin.x; }, + set originX(v){ this._origin.x = v - this[X1]; return v; }, + get originX(){ return (this._loc || this.loc).x; }, /** * Changes origin y-position without moving the bounds. * Use relocate() to change the position by moving the origin. */ - set originY(v){ this._origin.y = v - this.y1; return v; }, - get originY(){ return this.absOrigin.y; }, + set originY(v){ this._origin.y = v - this[Y1]; return v; }, + get originY(){ return (this._loc || this.loc).y; }, + + // Accessors for the realized numeric origin relative to the coordinate system of the BoundingBox. + get loc() { + if (!this._loc) + this._loc = this._origin.absolute(this[X2]-this[X1], this[Y2]-this[Y1], this[X1],this[Y1]); + return this._loc; + }, + set loc(v) { + this._origin.setXY(v.x - this[X1], v.y - this[Y1]); + this._uncache(); + return this.loc; + }, - // Accessors for the realized numeric origin. - get absOrigin() { return this._origin.absolute(this.width,this.height, this.x1,this.y1); }, // TODO: cache absolute and invalidate - set absOrigin(v) { this._origin.setXY(v.x - this.x1, v.y - this.y1); return this._origin; }, - get relOrigin() { return this._origin.absolute(this.width,this.height); }, // TODO: cache absolute and invalidate - set relOrigin(v) { this._origin.setXY(v.x, v.y); return this._origin; }, + // Alias to match relOrigin + get absOrigin(){ return this._loc || this.loc; }, + set absOrigin(v){ return (this.loc = v); }, + + // Accessors for the realized numeric origin relative to the box bounds. + get relOrigin() { + if (!this._relOrigin) + this._relOrigin = this._origin.absolute(this[X2]-this[X1],this[Y2]-this[Y1]); + return this._relOrigin; + }, + set relOrigin(v) { + this._origin.setXY(v.x, v.y); + this._uncache(); + return this.relOrigin; + }, // Accessors for distance from origin to requested bound. - get originLeft(){ return this.relOrigin.x; }, - get originTop(){ return this.relOrigin.y; }, - get originRight(){ return this.width - this.relOrigin.x; }, - get originBottom(){ return this.height - this.relOrigin.y; }, + get originLeft(){ return (this._relOrigin || this.relOrigin).x; }, + get originTop(){ return (this._relOrigin || this.relOrigin).y; }, + get originRight(){ return this.width - (this._relOrigin || this.relOrigin).x; }, + get originBottom(){ return this.height - (this._relOrigin || this.relOrigin).y; }, /** * Calculates realized distance from origin to requested bound. @@ -75,16 +105,16 @@ Rect.subclass('BoundingBox', { * FIXME: this is just wrong if delta-xy is big enough */ collision : function collision(trj, from, to){ - if (from.x2 <= this.x1 && to.x2 >= this.x1) + if (from.x2 <= this[X1] && to.x2 >= this[X1]) return this.leftSide; - if (from.x1 >= this.x2 && to.x1 <= this.x2) + if (from.x1 >= this[X2] && to.x1 <= this[X2]) return this.rightSide; - if (from.y2 <= this.y1 && to.y2 >= this.y1) + if (from.y2 <= this[Y1] && to.y2 >= this[Y1]) return this.topSide; - if (from.y1 >= this.y2 && to.y1 <= this.y2) + if (from.y1 >= this[Y2] && to.y1 <= this[Y2]) return this.bottomSide; return null; @@ -96,17 +126,17 @@ Rect.subclass('BoundingBox', { * @return {Vec} Furthest point or null if no collision is detected. */ furthest : function furthest(trj, from, to){ - if (from.x2 <= this.x1 && to.x2 >= this.x1) - return tr.pointAtX(this.x1 - 1 - from.originRight); + if (from.x2 <= this[X1] && to.x2 >= this[X1]) + return tr.pointAtX(this[X1] - 1 - from.originRight); - if (from.x1 >= this.x2 && to.x1 <= this.x2) - return tr.pointAtX(this.x2 + 1 + from.originLeft); + if (from.x1 >= this[X2] && to.x1 <= this[X2]) + return tr.pointAtX(this[X2] + 1 + from.originLeft); - if (from.y2 <= this.y1 && to.y2 >= this.y1) - return tr.pointAtY(this.y1 - 1 - from.originTop); + if (from.y2 <= this[Y1] && to.y2 >= this[Y1]) + return tr.pointAtY(this[Y1] - 1 - from.originTop); - if (from.y1 >= this.y2 && to.y1 <= this.y2) - return tr.pointAtY(this.y2 + 1 + from.originBottom ); + if (from.y1 >= this[Y2] && to.y1 <= this[Y2]) + return tr.pointAtY(this[Y2] + 1 + from.originBottom ); return null; }, @@ -139,7 +169,7 @@ Rect.subclass('BoundingBox', { /** * Resizes bounds to proportionally maintain their distance from the origin. - * This is an in-place modification of the BoundingBox. + * nb. This is an in-place modification of the BoundingBox. */ resize : function resize(w,h){ if (w instanceof Array) { h=w[1]; w=w[0]; } diff --git a/src/ezl/math/index.cjs b/src/ezl/math/index.cjs index a891b65..cc3dc45 100644 --- a/src/ezl/math/index.cjs +++ b/src/ezl/math/index.cjs @@ -1,6 +1,7 @@ //#exports clamp lerp -require('Y').Y.core -.extend(exports, { +var Y = require('Y').Y; + +Y.core.extend(exports, { 'clamp' : function clamp(value, min, max) { return Math.min(Math.max(value, min), max); @@ -8,6 +9,18 @@ require('Y').Y.core 'lerp' : function lerp(x, a, b) { return a + x*(b - a); - } + }, + + // Arg-masked versions of Math functions + 'min' : Y(Math.min).limit(2), + 'max' : Y(Math.max).limit(2), + + 'floor' : Y(Math.floor).limit(1), + 'ceil' : Y(Math.ceil).limit(1), + 'round' : Y(Math.round).limit(1), + + 'abs' : Y(Math.abs).limit(1), + 'sqrt' : Y(Math.sqrt).limit(1) + }); diff --git a/src/ezl/math/rect.cjs b/src/ezl/math/rect.cjs index 75d3b76..6554269 100644 --- a/src/ezl/math/rect.cjs +++ b/src/ezl/math/rect.cjs @@ -27,22 +27,29 @@ Y.subclass('Rect', [], { /** * @protected + * Called whenever the Rect is dirtied (a no-op by default). + */ + _uncache : Y.op.nop, + + /** + * @protected */ set4 : function set4(x1,y1, x2,y2){ this[X1] = x1; this[Y1] = y1; this[X2] = x2; this[Y2] = y2; + this._uncache(); return this; }, - get x1(){ return this[X1]; }, set x1(v){ this[X1] = v; return v; }, - get y1(){ return this[Y1]; }, set y1(v){ this[Y1] = v; return v; }, - get x2(){ return this[X2]; }, set x2(v){ this[X2] = v; return v; }, - get y2(){ return this[Y2]; }, set y2(v){ this[Y2] = v; return v; }, + get x1(){ return this[X1]; }, set x1(v){ this[X1] = v; this._uncache(); return v; }, + get y1(){ return this[Y1]; }, set y1(v){ this[Y1] = v; this._uncache(); return v; }, + get x2(){ return this[X2]; }, set x2(v){ this[X2] = v; this._uncache(); return v; }, + get y2(){ return this[Y2]; }, set y2(v){ this[Y2] = v; this._uncache(); return v; }, get p1(){ return new Vec(this[X1],this[Y1]); }, - set p1(v){ this[X1] = v.x; this[Y1] = this.y; return v; }, + set p1(v){ this[X1] = v.x; this[Y1] = this.y; this._uncache(); return v; }, get p2(){ return new Vec(this[X2],this[Y2]); }, - set p2(v){ this[X2] = v.x; this[Y2] = this.y; return v; }, + set p2(v){ this[X2] = v.x; this[Y2] = this.y; this._uncache(); return v; }, get width(){ return this[X2] - this[X1]; }, get height(){ return this[Y2] - this[Y1]; }, diff --git a/src/tanks/map/level.cjs b/src/tanks/map/level.cjs index 435f1ad..05c8f79 100644 --- a/src/tanks/map/level.cjs +++ b/src/tanks/map/level.cjs @@ -1,21 +1,20 @@ var Y = require('Y').Y , op = require('Y/op') -, proxy = require('Y/utils').proxy +, proxy = require('Y/utils').proxy + , evt = require('evt') + +, math = require('ezl/math') +, Speciated = require('ezl/mixins/speciated').Speciated , Rect = require('ezl/shape').Rect , Buff = require('tanks/effects/buff').Buff -, Map = require('tanks/map/pathing/map').Map +, Map = require('tanks/map/pathing/map').Map , Thing = require('tanks/thing/thing').Thing , Tank = require('tanks/thing/tank').Tank , Item = require('tanks/thing/item').Item -, Player = require('tanks/thing/player').Player +, Player = require('tanks/thing/player').Player , Wall = require('tanks/map/wall').Wall -, Speciated = require('ezl/mixins/speciated').Speciated - -, min = Y(Math.min).limit(2) -, max = Y(Math.max).limit(2) -, toInt = Y(parseInt).limit(1) , @@ -25,15 +24,12 @@ Rect.subclass('Level', { __mixins__ : [ Speciated ], _layerClasses : 'ezl layer level', - init : function init(game, capacity, buffer_size){ + init : function initLevel(game, capacity, buffer_size){ this.game = game; this.map = new Map(0,0, this.levelWidth, this.levelHeight, capacity, buffer_size); Rect.init.call(this, this.levelWidth,this.levelHeight); this.fill('transparent'); - // var shape = this.shape = new Rect(this.levelWidth,this.levelHeight).fill('transparent'); - // shape.layer.attr('class', this._layerClasses); - // this.bbox = shape.bbox; }, setup : function setup(repo){ @@ -61,9 +57,6 @@ Rect.subclass('Level', { return game.addUnit(obj, x.loc[0], x.loc[1]); }); - // I = - // game.addUnit(Item.create('nitro'), 8,8); - return this; } @@ -81,19 +74,19 @@ Level.on('speciate', ; if (size) { - size = size.split(' ').map(toInt); - proto.levelWidth = size[0]; + size = size.split(' ').map(op.toInt); + proto.levelWidth = size[0]; proto.levelHeight = size[1]; } - if (!proto.levelWidth) { - proto.levelWidth = - bounds.right.pluck(0).reduce(max,0) - - bounds.left.pluck(0).reduce(min,Infinity); - } - if (!proto.levelHeight) { - proto.levelHeight = - bounds.bottom.pluck(1).reduce(max,0) - - bounds.top.pluck(1).reduce(min,Infinity); - } + // if (!proto.levelWidth) { + // proto.levelWidth = + // bounds.right.pluck(0).reduce(math.max,0) + // - bounds.left.pluck(0).reduce(math.min,Infinity); + // } + // if (!proto.levelHeight) { + // proto.levelHeight = + // bounds.bottom.pluck(1).reduce(math.max,0) + // - bounds.top.pluck(1).reduce(math.min,Infinity); + // } });