// exports['instantiate'] = instantiate;
// exports['fabricate'] = fabricate;
-
, FN = "constructor"
, PT = "prototype"
, OP = _Object[PT]
+, FP = _Function[PT]
, slice = _Array[PT].slice
, getProto = _Object.getPrototypeOf
}
function type( o ) {
+ if ( o === null || o === undefined )
+ return o;
+
+ // If this is a Y.Class or similar, it will help us out
+ if (o.__class__)
+ return o.__class__;
+
switch ( typeof(o) ) {
- case "undefined" : return undefined;
case "string" : return _String;
case "number" : return _Number; // Note: NaN and Infinity are Number literals
case "boolean" : return _Boolean;
case "function" :
// If the function has a user-specified prototype, we can probably assume
// it's meant to be a class constructor (and therefore, a type)
- if ( o[PT] && o[PT] !== _Function[PT] )
+ var oP = o[PT];
+ if ( oP && !(oP === FP || oP === _Function[PT]) )
return o;
else
return _Function;
case "object" :
default :
- // Null is an object, obv
- if ( o === null )
- return null;
-
- return KNOWN_CLASSES[o.className] || o.__class__
- || (o[FN] && o[FN] !== _Object) ? o[FN] : _Object;
+ return (o[FN] && o[FN] !== _Object) ? o[FN] : _Object;
}
}
, getDesc = _Object.getOwnPropertyDescriptor
, setDesc = _Object.defineProperty
-, classToString = function toString(){ return this.className+"()"; }
+, objToString = _Object[P].toString
+, classToString = function toString(){ return this.className+"()"; }
;
// Private delegating constructor -- must be defined for every
this.posBleed = new Loc(0,0);
this.boundingBox = new BoundingBox(0,0, 0,0);
+ this._origin = this.boundingBox.origin;
- this._origin = new Loc('50%','50%');
this.transform = {
rotate : 0,
scale : new Loc(1.0,1.0),
this.layerWidth = w;
- var origin = this._origin
+ var bb = this.boundingBox.resize(w, this.layerHeight)
+ , ro = bb.relOrigin
, nb = this.negBleed
- , v = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long
+ , cw = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long
- this.boundingBox = this.boundingBox.resize(w, this.layerHeight);
- this.layer.width(w).css('margin-left', (-nb.x)+'px')
+ this.layer.css({
+ 'width' : w,
+ 'left' : bb.x1,
+ 'margin-left' : -ro.x
+ });
- // this.canvas.width(v);
this.canvas.css({
- 'width' : v+'px',
- 'margin-left' : (-nb)+'px'
+ 'width' : cw,
+ 'margin-left' : -nb.x
});
- this.canvas[0].width = v;
-
+ this.canvas[0].width = cw;
return this;
},
return this.layerHeight;
this.layerHeight = h;
- this.boundingBox = this.boundingBox.resize(this.layerWidth, h);
-
- var nb = this.negBleed.y
- , v = this.canvasHeight = Math.ceil(h + nb + this.posBleed.y); // HTMLCanvas.height is a long
- this.layer.height(h).css('margin-top', (-nb)+'px')
- this.canvas.height(v);
- // this.canvas.css({
- // 'height' : v+'px',
- // 'margin-top' : (-nb)+'px'
- // });
- this.canvas[0].height = v;
+
+ var bb = this.boundingBox.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
+ });
+ this.canvas[0].height = ch;
return this;
},
return this.layer.position();
if ( Y.isPlainObject(x) ){
- y = x.y;
- x = x.x;
+ y = x.y; x = x.x;
}
- // if (pos.x !== undefined) pos.x -= this.offsetX;
- // if (pos.y !== undefined) pos.y -= this.offsetY;
-
- this.boundingBox = this.boundingBox.relocate(x,y);
- this.loc = this.boundingBox.midpoint();
-
- var origin = this.origin();
- this.css({
- 'left' : x,
- 'top' : y,
- 'margin-left' : -origin.x,
- 'margin-top' : -origin.y
+ var bbox = this.boundingBox.relocate(x,y);
+ this.css({
+ 'left' : bbox.x1,
+ 'top' : bbox.y1
});
return this;
},
css : makeDelegate('css'),
/** Position relative to document. */
- offset : makeDelegate('offset'),
+ // offset : makeDelegate('offset'),
* to all sublayers (preserving knowledge of their individual scaling).
*/
scale : function scale(sx,sy){
- var o = this.transform.scale;
+ var ts = this.transform.scale;
if (arguments.length === 0)
- return o.absolute(this.layerWidth, this.layerHeight);
+ return ts;
- o.x = sx;
- o.y = sy;
+ ts.x = sx;
+ ts.y = sy;
this.dirty = true;
return this._applyTransforms();
},
* Translates draw calls by (x,y) within this layer only. This allows you to
* functionally move the coordinate system of the layer.
*/
- translate : function translate(x,y){
- var o = this.transform.translate;
+ translate : function translate(tx,ty){
+ var tt = this.transform.translate;
if (arguments.length === 0)
- return o.absolute(this.layerWidth, this.layerHeight);
+ return tt;
- o.x = x;
- o.y = y;
+ tt.x = tx;
+ tt.y = ty;
this.dirty = true;
return this;
},
var Y = require('Y').Y
+, Loc = require('ezl/loc/loc').Loc
, math = require('ezl/math')
, Vec = math.Vec
, Line = math.Line
, Rect = math.Rect
+
+, X1 = 0, Y1 = 1
+, X2 = 2, Y2 = 3
,
+
BoundingBox =
exports['BoundingBox'] =
Rect.subclass('BoundingBox', {
- attr : Y.attr.methodize(),
+ init : function initRect(_x1,_y1, _x2,_y2, originX, originY){
+ Rect.init.call(this, _x1,_y1, _x2,_y2);
+
+ if (originX === undefined) originX = '50%';
+ if (originY === undefined) originY = '50%';
+ this._origin = new Loc(originX,originY);
+ },
+
+ /**
+ * Changes origin definition without moving the bounds.
+ * Use relocate() to change the position by moving the origin.
+ */
+ set originX(v){ this._origin.x = v; return v; },
+ get originX(){ return this._origin.absolute(this.width,this.height, this.x1,this.x2).x; },
+
+ set originY(v){ this._origin.y = v; return v; },
+ get originY(){ return this._origin.absolute(this.width,this.height, this.x1,this.x2).y; },
+
+ /**
+ * Accessor for the realized numeric origin.
+ */
+ get absOrigin() { return this._origin.absolute(this.width,this.height, this.x1,this.x2); },
+ get relOrigin() { return this._origin.absolute(this.width,this.height); },
+
+ get origin(){ return this._origin; },
relocate : function relocate(x,y){
- return new BoundingBox(x,y, x+this.width,y+this.height);
+ var _x1 = this[X1], _y1 = this[Y1]
+ , _x2 = this[X2], _y2 = this[Y2]
+
+ , abs = this._origin.absolute(_x1-_x1, _y2-_y1, _x1,_y1)
+ , dx1 = _x1 - abs.x, dy1 = _y1 - abs.y
+ , dx2 = _x2 - abs.x, dy2 = _y2 - abs.y
+
+ , x1 = x + dx1, y1 = y + dy1
+ , x2 = x + dx2, y2 = y + dy2
+ ;
+ return this.set4(x1,y1, x2,y2);
},
resize : function resize(w,h){
- var x = this.x1, y = this.y1;
- return new BoundingBox(x,y, x+w,y+h);
+ var x1 = this[X1], y1 = this[Y1]
+ , x2 = this[X2], y2 = this[Y2]
+ , wOld = x2-x1, hOld = y2-y1
+
+ , p, abs, diff, o = this._origin
+ , xp = o.xPercentage, yp = o.yPercentage
+ ;
+ if ( xp !== null ) {
+ diff = w - wOld;
+ abs = x1 + xp*wOld;
+ x1 = abs - xp*diff;
+ x2 = abs + (1-xp)*diff;
+ } else
+ x2 = x1 + w;
+
+ if ( yp !== null ) {
+ diff = h - hOld;
+ abs = y1 + yp*hOld;
+ y1 = abs - yp*diff;
+ y2 = abs + (1-yp)*diff;
+ } else
+ y2 = y1 + h;
+
+ return this.set4(x1,y1, x2,y2);
},
clone : function clone(){
- return new BoundingBox(this[0],this[1], this[2],this[3]);
+ var o = this._origin;
+ return new BoundingBox(this[X1],this[Y1], this[X2],this[Y2], o[X1], o[Y1]);
}
});
// Vec.init.call(this, x,y);
// },
- set : function set(k, v, def){
- v = (v !== undefined ? v : def);
- switch (k) {
- case 'x': case 0:
- this.x = this[0] = v; break;
- case 'y': case 1:
- this.y = this[1] = v; break;
- default:
- this[k] = v;
- }
- return this;
- },
- attr : Y.attr.methodize(),
-
- clone : function clone(){
- return new Loc(this.x, this.y);
- },
-
moveBy : function moveBy(x,y){
return new Loc(this.x+x, this.y+y);
},
return Loc.Square.fromLoc(this.x, this.y);
},
+ get xIsPercentage(){ return Y.isString(this.x); },
+ get yIsPercentage(){ return Y.isString(this.x); },
+
+ get xPercentage(){ var x = this.x; return Y.isString(x) ? parseFloat(x.slice(0,-1))/100 : null; },
+ get yPercentage(){ var y = this.y; return Y.isString(y) ? parseFloat(y.slice(0,-1))/100 : null; },
+
/**
* Converts relative locations like ('50%','50%') to a numeric location.
*/
- 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 );
+ absolute : function absolute(w,h, x1,y1){
+ var x = this.x, y = this.y;
+ return new Loc( (x1 || 0) + (Y.isString(x) ? parseFloat(x.slice(0,-1))/100 * w : x)
+ , (y1 || 0) + (Y.isString(y) ? parseFloat(y.slice(0,-1))/100 * h : y) );
},
toUnits : function toUnits(units){
var Y = require('Y').Y
, Vec = require('ezl/math/vec').Vec
+, X1 = 0, Y1 = 1
+, X2 = 2, Y2 = 3
,
init : function initRect(_x1,_y1, _x2,_y2){
if (_x1 instanceof Array && _y1 instanceof Array) {
- _y2 = _y1[1]; _x2 = _y1[0];
- _y1 = _x1[1]; _x1 = _x1[0];
+ _y2 = _y1[Y1]; _x2 = _y1[X1];
+ _y1 = _x1[Y1]; _x1 = _x1[X1];
}
var x1 = Math.min(_x1,_x2), x2 = Math.max(_x1,_x2)
, y1 = Math.min(_y1,_y2), y2 = Math.max(_y1,_y2);
this.length = 4;
- this[0] = x1; this[1] = y1;
- this[2] = x2; this[3] = y2;
+ this.set4(x1,y1, x2,y2);
},
- get x1(){ return this[0]; }, set x1(v){ this[0] = v; },
- get y1(){ return this[1]; }, set y1(v){ this[1] = v; },
- get x2(){ return this[2]; }, set x2(v){ this[2] = v; },
- get y2(){ return this[3]; }, set y2(v){ this[3] = v; },
+ /**
+ * @protected
+ */
+ set4 : function set4(x1,y1, x2,y2){
+ this[X1] = x1; this[Y1] = y1;
+ this[X2] = x2; this[Y2] = y2;
+ 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 p1(){ return new Vec(this[X1],this[Y1]); },
+ set p1(v){ this[X1] = v.x; this[Y1] = this.y; return v; },
+ get p2(){ return new Vec(this[X2],this[Y2]); },
+ set p2(v){ this[X2] = v.x; this[Y2] = this.y; return v; },
- get p1(){ return new Vec(this[0],this[1]); },
- set p1(v){ this[0] = v.x; this[1] = this.y },
- get p2(){ return new Vec(this[2],this[3]); },
- set p2(v){ this[2] = v.x; this[3] = this.y },
+ get width(){ return this[X2] - this[X1]; },
+ get height(){ return this[Y2] - this[Y1]; },
- // TODO: .origin
- // TODO: Scale w/h proportionally if .origin is percentage
- get width(){ return this[2] - this[0]; },
- get height(){ return this[3] - this[1]; },
+ attr : Y.attr.methodize(),
get midpoint(){
- return new Vec( this[0] + (this[2]-this[0]) / 2
- , this[1] + (this[3]-this[1]) / 2 );
+ return new Vec( this[X1] + (this[X2]-this[X1]) / 2
+ , this[Y1] + (this[Y2]-this[Y1]) / 2 );
},
contains : function contains(x,y){
- return ( x >= this[0] && x <= this[2] &&
- y >= this[1] && y <= this[3] );
+ return ( x >= this[X1] && x <= this[X2] &&
+ y >= this[Y1] && y <= this[Y2] );
},
intersects : function intersects(line){
- var x1 = this[0], x2 = this[2]
- , y1 = this[1], y2 = this[3]
+ var x1 = this[X1], x2 = this[X2]
+ , y1 = this[Y1], y2 = this[Y2]
, cx1,cy1, cx2,cy2 ;
return ( ( (cx1 = line.calcX(y1)) >= x1 && cx1 <= x2 )
|| ( (cy1 = line.calcY(x1)) >= y1 && cy1 <= y2 )
},
clone : function clone(){
- return new Rect(this[0],this[1], this[2],this[3]);
+ return new this.__class__(this[X1],this[Y1], this[X2],this[Y2]);
},
toString : function toString(){
var Y = require('Y').Y
+, _X = 0, _Y = 1
,
/**
new Y.Class('Vec', [], {
init : function init(x, y){
- this.length = 2;
-
if ( x instanceof Array ) {
- y = x[1]; x = x[0];
+ y = x[_Y]; x = x[_X];
}
+ this.length = 2;
this.setXY(x,y);
},
- equals : function equals(b){
- return (this.x === b.x) && (this.y === b.y);
- },
-
- clone : function clone(){
- return new Vec(this.x, this.y);
- },
+ get x(){ return this[_X]; }, set x(v){ this[_X] = v; return v; },
+ get y(){ return this[_Y]; }, set y(v){ this[_Y] = v; return v; },
+ /**
+ * @protected
+ * One fewer function calls when setting both, which matters according to profiling.
+ */
setXY : function setXY(x,y){
- this.x = this[0] = x;
- this.y = this[1] = y;
+ this[_X] = x; this[_Y] = y;
return this;
},
+ attr : Y.attr.methodize(),
+
+ equals : function equals(b){
+ return (this[_X] === b[_X]) && (this[_Y] === b[_Y]);
+ },
+
add : function add(x,y){
if ( x instanceof Array ) {
- y = x[1]; x = x[0];
+ y = x[_Y]; x = x[_X];
}
- return this.setXY(this.x+x, this.y+y);
+ return this.setXY(this[_X]+x, this[_Y]+y);
},
subtract : function subtract(x,y){
if ( x instanceof Array ) {
- y = x[1]; x = x[0];
+ y = x[_Y]; x = x[_X];
}
- return this.setXY(this.x-x, this.y-y);
+ return this.setXY(this[_X]-x, this[_Y]-y);
},
scale : function scale(s){
- return this.setXY(this.x*s, this.y*s);
+ return this.setXY(this[_X]*s, this[_Y]*s);
},
invert : function invert(){
- return this.setXY(-this.x, -this.y);
+ return this.setXY(-this[_X], -this[_Y]);
},
normalize : function normalize(){
},
magnitude : function magnitude(){
- return Math.sqrt(this.x*this.x + this.y*this.y);
+ var x = this[_X], y = this[_Y];
+ return Math.sqrt(x*x + y*y);
},
sqMagnitude : function sqMagnitude(){
- return this.x*this.x + this.y*this.y;
+ var x = this[_X], y = this[_Y];
+ return x*x + y*y;
},
dot : function dot(b){
- return this.x*b.x + this.y*b.y;
+ return this[_X]*b[_X] + this[_Y]*b[_Y];
},
manhattan: function manhattan(x2,y2) {
- if (x2 instanceof Vec) {
- y2 = x2.y; x2 = x2.x;
+ if (x2 instanceof Array) {
+ y2 = x2[_Y]; x2 = x2[_X];
}
- var d1 = Math.abs(x2 - this.x)
- , d2 = Math.abs(y2 - this.y) ;
+ var d1 = Math.abs(x2 - this[_X])
+ , d2 = Math.abs(y2 - this[_Y]) ;
return d1 + d2;
},
rotate : function rotate(theta){
var sin = Math.sin(theta)
, cos = Math.cos(theta)
- , x = this.x, y = this.y ;
+ , x = this[_X], y = this[_Y] ;
return this.setXY(x*cos - y*sin, x*sin + y*cos);
},
toString : function toString(){
- var p = 2, x = this.x, y = this.y;
+ var p = 2, x = this[_X], y = this[_Y];
x = ((x % 1 !== 0) ? x.toFixed(p) : x);
y = ((y % 1 !== 0) ? y.toFixed(p) : y);
return '('+x+', '+y+')';
});
+function lerp(x, a, b) { return a + x*(b - a); }
+
Y.extend(Vec, {
sum : function sum(a, b) {
- return new Vec(a.x+b.x, a.y+b.y);
+ return new Vec(a[_X]+b[_X], a[_Y]+b[_Y]);
},
difference : function difference(a, b) {
- return new Vec(a.x-b.x, a.y-b.y);
+ return new Vec(a[_X]-b[_X], a[_Y]-b[_Y]);
},
dot : function dot(a, b) {
- return a.x*b.x + a.y*b.y;
+ return a[_X]*b[_X] + a[_Y]*b[_Y];
},
lerp : function lerp(x, a, b) {
- return new Vec( math.lerp(a.x, b.x, x),
- math.lerp(a.y, b.y, x) );
+ return new Vec( lerp(a[_X], b[_X], x),
+ lerp(a[_Y], b[_Y], x) );
},
manhattan: function manhattan(x1,y1, x2,y2) {
if (x1 instanceof Array && y1 instanceof Array) {
- y2 = y1[1]; x2 = y1[0];
- y1 = x1[1]; x1 = x1[0];
+ y2 = y1[_Y]; x2 = y1[_X];
+ y1 = x1[_Y]; x1 = x1[_X];
}
var d1 = Math.abs(x2 - x1)
, d2 = Math.abs(y2 - y1) ;
},
circumcenter : function circumcenter(){
- var offX = this.offsetX, offY = this.offsetY
+ var nb = this.negBleed
+ , offX = nb.x, offY = nb.y
, x1 = this.x1 - offX, y1 = -1 * (this.y1 - offY) // remember, DOM is Y-inverted
, x2 = this.x2 - offX, y2 = -1 * (this.y2 - offY) // which affects the signs
, x = sqX + (REF_SIZE-unit.width) /2
, y = sqY + (REF_SIZE-unit.height)/2 ;
- unit.setLocation(x,y);
+ unit.position(x,y);
unit.render( this.level );
}
moveUnitTo : function moveUnitTo(agent, x,y){
this.pathmap.removeBlocker(agent);
- agent.setLocation(x,y);
+ agent.position(x,y);
this.pathmap.addBlocker(agent);
return agent;
},
, side = null
, bw = bb.width, bh = bb.height
- , offX = agent.offsetX, offY = agent.offsetY
+
+ , ro = bb.relOrigin
+ , offX = ro.x, offY = ro.y
, x1 = to.x+offX, y1 = to.y+offY
, x2 = x1+bw, y2 = y1+bh
blocking : true,
active : false,
+ originX : 0,
+ originY : 0,
+
stats : {
hp : Infinity,
move : 0,
this.height = h;
this.isBoundary = !!isBoundary;
Thing.init.call(this);
- this.setLocation(x,y);
+ this.position(x,y);
},
// inactive
var loc = owner.getTurretLoc()
, x1 = loc.x, y1 = loc.y;
- this.setLocation(x1,y1);
+ this.position(x1,y1);
this.trajectory = new Trajectory(this, x1,y1, x2,y2, this.stats.move*REF_SIZE/1000);
this.addEventListener('collide', this.onCollide.bind(this));
bounces : 0,
bounceLimit : 1,
- offsetX : -3,
- offsetY : -3,
- width : 6,
- height : 6,
+ width : 6,
+ height : 6,
stats : {
move : 2.0 // move speed (squares/sec)
function initTank(align){
Thing.init.call(this, align);
this.onBulletDeath = this.onBulletDeath.bind(this);
-
- var self = this;
this.addEventListener('destroy', destroyPath);
};
return this.moveByAngle( this.angleTo(x,y), x2,y2 );
};
- /** @protected This method does not update this.trajectory -- call this.move(x,y) instead. */
+ /**
+ * @protected
+ * This method does not update this.trajectory -- call this.move(x,y) instead.
+ */
this['moveByAngle'] =
function moveByAngle(theta, targetX,targetY){
var abs = Math.abs
init : function init(align){
this.id = Thing.THING_ID++;
this.align = align || 0;
+ this.boundingBox = new BoundingBox(0,0, this.width,this.height, this.originX,this.originY);
this.fillStats();
this.createCooldowns();
rotation : 0,
// Bounding box offsets/dimensions
- offsetX : 0,
- offsetY : 0,
- width : REF_SIZE*0.7,
- height : REF_SIZE*0.6,
+ originX : '50%',
+ originY : '50%',
+ width : REF_SIZE,
+ height : REF_SIZE,
set : Y.op.set.methodize(),
attr : Y.op.attr.methodize(),
return this;
},
- setLocation : function setLocation(x,y){
- var loc = this.loc
- , x1 = x + this.offsetX
- , y1 = y + this.offsetY
- , x2 = x1 + this.width
- , y2 = y1 + this.height
- ;
-
- if (!loc)
- loc = this.loc = new Loc(x,y);
- else if (loc.x === x && loc.y === y)
- return loc;
- else
- this.loc.setXY(x,y);
-
- if (this.shape)
- this.shape.position(x,y);
-
- // this.createBoundingBox(x,y);
- var bb = this.boundingBox = new BoundingBox(x1,y1, x2,y2);
- this.midpoint = bb.midpoint();
-
+ position : function position(x,y){
+ var bb = this.boundingBox.relocate(x,y);
+ this.loc = bb.absOrigin;
+ this.midpoint = bb.midpoint;
+ if (this.shape) this.shape.position(x,y);
return this;
},
getTurretLoc : function getTurretLoc(){ return this.loc; },
angleTo : function angleTo(x,y){
- var bb = this.boundingBox
- , w = this.width, h = this.height
- , x0 = x - bb.x1 - w/2
- , y0 = y - bb.y1 - h/2
+ var loc = this.loc
+ , x0 = x - loc.x
+ , y0 = y - loc.y
;
return Math.atan2(y0,x0);
},