Team:WHU-China/bill.js

From 2011.igem.org

Revision as of 15:04, 1 October 2011 by BillChen (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

// ====================================================================== // script by Gerard Ferrandez | February, 2010 // -------------------------------------------------------------------- // http://www.dhteumeuleu.com | http://www.twitter.com/ge1doot // Javascript code licensed under CC-BY-NC - Do not remove this notice // ======================================================================

var res = function () { // ---- private vars ---- var scr, a0, a1, divs = new Array(), moves = new Array(), idle = false, nw, nh, wu, hu, mx = [1,0,-1,0,2,0,-2,0,3,0,-3,0,4,0,-4,0], my = [0,1,0,-1,0,2,0,-2,0,3,0,-3,0,4,0,-4], bw = 10;

//////////////////////////////////////////////////////////////////////////// // ============== grid class ============= var grid = { // ===== calculate grid ===== calc : function () { // ---- empty grid ---- this.grid = new Array(24); for (var i = 0; i < 24; i++) this.grid[i] = 0; // ---- load grid ---- var i = 0, o; while ( o = divs[i++] ) this.add(o); }, // ===== return cell value ===== cell : function (x, y) { return this.grid[x * 4 + y]; }, // ===== area weight ===== weight : function (x, y, w, h) { var gw = 0; for(var i = 0; i < w; i++) for(var j = 0; j < h; j++) gw += this.cell(i + x, j + y); return gw; }, // ===== add div ===== add : function (o) { for(var i = 0; i < o.w; i++) for(var j = 0; j < o.h; j++) this.grid[(i + o.x) * 4 + (j + o.y)] += o.f ? 20 : 1; } }

//////////////////////////////////////////////////////////////////////////// // ============== tween class ============= var tween = { // ===== tween stack ===== divs : new Array(), // ===== create tween object ===== tween : function (o, x, y, w, h) { return { div : o, x : x, y : y, w : w, h : h }; }, // ===== add new tween ===== add : function (o, x, y, w, h) { if (o == true) return false; // ---- consolidate with last move ---- var t = this.divs.length - 1; if (t >= 0 && this.divs[t].div == o) this.divs[t] = this.tween(o, x, y, w, h); else { // ---- push tween ---- this.divs.push( this.tween(o, x, y, w, h) ); } }, // ======== start next tween ========= next : function () { if (this.divs.length) { var t = this.divs[0]; var o = t.div; o.dx = t.x - o.xc; o.dy = t.y - o.yc; o.dw = t.w - o.wc; o.dh = t.h - o.hc; o.s = 1; o.p = 0; return o; } else return false; } } //////////////////////////////////////////////////////////////////////////// // =============== Div constructor =============== var Frame = function (i, div) { // ---- random position ---- do { this.x = this.x0 = Math.floor(Math.random() * 6); this.y = this.y0 = Math.floor(Math.random() * 4); } while (moves[this.x * 4 + this.y]); moves[this.x * 4 + this.y] = true; // ---- dimensions ---- var img = div.getElementsByTagName("img")[0]; var wh = img.alt.split(","); img.alt = ""; this.w = 1; this.h = 1; this.xc = this.x + .5; this.yc = this.y + .5; this.wc = 0; this.hc = 0; this.w1 = wh[0] * 1; this.h1 = wh[1] * 1; this.i = i; this.s = 0; div.parent = this; div.onclick = function () { this.parent.click(); } this.css = div.style; // ---- push tween ---- tween.add(this, this.x, this.y, 1, 1); }

// ========== calculate moving cost =========== Frame.prototype.costMove = function (m) { // ---- what direction ---- var sx = mx[m] > 0 ? 1 : mx[m] < 0 ? -1 : 0, sy = my[m] > 0 ? 1 : my[m] < 0 ? -1 : 0, cm = 0; // ---- horizontal ---- if (sx != 0) { for (var i = this.x; i != this.x + mx[m]; i += sx) cm += grid.weight(i, this.y, this.w, this.h); // ---- vertical ---- } else if (sy != 0) { for (var i = this.y; i != this.y + my[m]; i += sy) cm += grid.weight(this.x, i, this.w, this.h); } // ---- return cost ---- return cm; }

// ============ determine moving direction ============= Frame.prototype.findMove = function () { var d = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], mx = 1000, cm, m; // =========== loop distance ============ for (var p = 0; p < 4 && mx >= 20; p++) { // ======== vertical neighbors weights ======== for (var i = 0; i < this.w; i++) { // ---- up ---- if (this.y - p > 0) d[3 + p * 4] += grid.cell(this.x + i, this.y - (p + 1)); else // ---- border ---- d[3 + p * 4] = 100; // ---- bottom ---- if (this.y + p + this.h < 4) d[1 + p * 4] += grid.cell(this.x + i, this.y + this.h + p); else // ---- border ---- d[1 + p * 4] = 100; } // ======== horizontal neighbors weights ======== for (var j = 0; j < this.h; j++) { // ---- left ---- if (this.x - p > 0) d[2 + p * 4] += grid.cell(this.x - (p + 1), this.y + j); else // ---- border ---- d[2 + p * 4] = 100; // ---- right ---- if (this.x + this.w + p < 6) d[0 + p * 4] += grid.cell(this.x + this.w + p, this.y + j); else // ---- border ---- d[0 + p * 4] = 100; } // =============== find direction to the weakest neighbor ================ mx = 1000; for (var i = 0; i < 4 * (p + 1); i++) { if ( d[i] < mx) { // ---- store min values ---- mx = d[i]; m = i; cm = this.costMove(i); // ---- equality case ---- } else if ( d[i] == mx) { // ---- less costly move ---- var cmm = this.costMove(i); if (cmm < cm) { cm = cmm; m = i; mx = d[i]; } } } } // ====== return direction [0 = right, 1 = bottom, 2 = left, 3 = up] ====== return m; }

// ============ HTML rendering ============= Frame.prototype.paint = function () { this.css.left = Math.round(bw + this.xc * wu) + "px"; this.css.top = Math.round(bw + this.yc * hu) + "px"; this.css.width = Math.round(Math.max(0, this.wc * wu - bw)) + "px"; this.css.height = Math.round(Math.max(0, this.hc * hu - bw)) + "px"; }

// ========== easing function ============== Frame.prototype.tween = function () { if (this.s != 0) { // ---- easing ---- this.p += this.s; this.xc += this.dx * this.p * .01; this.yc += this.dy * this.p * .01; this.wc += this.dw * this.p * .01; this.hc += this.dh * this.p * .01; // ---- HTML rendering ---- this.paint(); // ---- percentage steps [1+2+3+4+5+6+7+8+9+10+9+8+7+6+5+4+3+2+1=100] ---- if (this.p == 10) // ---- reverse acceleration this.s = -1; else if (this.p == 0) // ---- moving end this.s = false; } return this.s; }

// ============= Main IA function for moving divs =============== Frame.prototype.click = function () { if (moves.length) { var i = 0, o; while ( o = moves[i++] ) { o.f = false; // ---- zoom out ---- if (o.w != 1 || o.h != 1) { o.w = 1; o.h = 1; tween.add(o, o.x, o.y, o.w, o.h); } } } // ---- stop here if same div ---- if (moves[0] == this) moves = new Array(); else { // ---- init grid weights ---- moves = new Array(); grid.calc(); // =========== find the best place for the zoomed div =========== var mvx = 0, mvy = 0; // ---- right border limit ---- if (this.y + this.h1 > 3) this.y -= this.y + this.h1 - 4; else { // ---- vertical backward move ---- for (var i = this.y - 1; i > this.y - this.h - 1; i--) if (i >= 0 && grid.weight(this.x, i, this.w1, 1) == 0) mvy++; } // ---- bottom border limit ---- if (this.x + this.w1 > 5) this.x -= this.x + this.w1 - 6; else if (!mvy) { // ---- horizontal backward move ---- for (var i = this.x - 1; i > this.x - this.w - 1; i--) if (i >= 0 && grid.weight(i, this.y, 1, this.h1) == 0) mvx++; } // ============ move zoomed div ============= this.x -= mvx; this.y -= mvy; this.w = this.w1; this.h = this.h1; this.f = true; this.findMove(); grid.add(this); moves.push(this); // ========= cascading child moves ========== var k = 0, o; // ---- loop through all divs ---- while ( o = divs[k++] ) { // ---- skip frozen div ---- if (o.f != true) { // ---- loop through all cells ---- for (var i = 0; i < o.w; i++) { for (var j = 0; j < o.h; j++) { // ---- collision (non empty cell) ---- if (grid.cell(i + o.x, j + o.y) > 1) { // ---- move to a better place ---- var m = o.findMove(); o.x += mx[m]; o.y += my[m]; // ---- not inside another one ? ---- if (grid.weight(o.x, o.y, o.w, o.h) < 20) { // ---- freeze div and push move ---- o.f = true; grid.add(o); moves.push(o); // ---- reset main loop and exit ---- k = 0; break; } } } if (o.f) break; } } } // ========= push moves in reverse order ========= var i = moves.length, o; while ( o = moves[--i] ) tween.add(o, o.x, o.y, o.w, o.h); } // ---- start tweens engine ---- a0 = tween.next(); if (idle) { idle = false; run(); } }

// ============== main loop ================ var run = function () { // ---- first tween ---- if (a0) { a0.tween(); if (a0.p == 10) { // ---- next ---- a1 = a0; tween.divs.splice(0,1); a0 = tween.next(); } } // ---- second tween ---- if (a1) { a1.tween(); // ---- end anim ---- if (a1.s === false) a1 = false; } // ---- loop ---- if (a0 || a1) setTimeout(run, 32); else idle = true; } // ============== init script ============== var init = function () { // ---- init container ---- scr = document.getElementById('screen'); // ---- load divs ---- var k = 0, o; while ( o = scr.getElementsByTagName("div")[k++] ) divs.push( new Frame(k, o) ); // ---- ajust dimensions ---- nw = scr.offsetWidth - bw; nh = scr.offsetHeight - bw; wu = nw / 6; hu = nh / 4; // ---- start animation ----- a0 = tween.next(); run(); } return { // ==== public bloc ==== init : init } }();