/hacks/Swarm/Swarm.js
/* Copyright 2011 The Dominion of Awesome
 * See COPYING for licensing information */
enyo.kind({
	name: "Pixel",
	constructor: function(canvas) {
		this.x = Math.random() * canvas.width;
		this.y = Math.random() * canvas.height;
		this.vx = Math.random() * 20.0 - 10.0;
		this.vy = Math.random() * 20.0 - 10.0;
		this.lastupdate = (new Date()).getTime();
	},
	accel: function(x, y) {
		this.vx += x;
		if (this.vx > 200.0)
			this.vx = 200.0;
		else if (this.vx < -200.0)
			this.vx = -200.0;
		this.vy += y;
		if (this.vy > 200.0)
			this.vy = 200.0;
		else if (this.vy < -200.0)
			this.vy = -200.0;
	},
	update: function() {
		var now = (new Date()).getTime();
		var dt = (now - this.lastupdate) / 75.0;
		this.x += this.vx * dt;
		this.y += this.vy * dt;
		this.lastupdate = now;
	},
	draw: function(ctx) {	
		ctx.beginPath();
		ctx.moveTo(this.x,this.y);
		ctx.lineTo(this.x - this.vx / 4.0, this.y - this.vy / 4.0);
		ctx.stroke();
		ctx.closePath();
	}	
});

enyo.kind({
	name: "Swarm",
	kind: "Hack",
	align: "stretch",
	pack: "stretch",
	components: [
		{name: "canvas", nodeTag: "canvas", style: "background-color: gray",
		 onmousemove: "mouseMove", onmousedown: "mouseDown"}
	],
	npixels: 100,
	pixels: [],
	rendered: function() {
		this.canvas = this.$.canvas.hasNode();
		this.resize();
		this.mx = this.canvas.width / 2;
		this.my = this.canvas.width / 2;
		for (var x = 0; x < this.npixels; x++)
			this.pixels.push(new Pixel(this.canvas));
		//this.stopTime = (new Date()).getTime();
		this.engine();
	},
	start: function() {
		if (this.stopTime) {
			var delta = (new Date()).getTime() - this.stopTime;
			for (var i = 0; i < this.npixels; i++)
				this.pixels[i].lastupdate += delta;
			this.stopTime = null;
		}
		if (!this.timer)
			this.timer = setInterval(this.engine.bind(this), 20);
	},
	stop: function() {
		this.stopTime = (new Date()).getTime();
		clearInterval(this.timer);
		this.timer = null;
	},
	resize: function(w, h) {
		this.canvas.width = w;
		this.canvas.height = h;
		//this.canvas.style.width = w + 'px';
		//this.canvas.style.height = h + 'px';
		this.context = this.canvas.getContext('2d');
		this.context.strokeStyle = '#000000';
		this.context.lineCap = 'round';
		this.context.lineWidth = 1.5;
	},
	mouseMove: function(inSender, inEvent) {
		this.mx = inEvent.clientX;
		this.my = inEvent.clientY;
	},
	mouseDown: function(inSender, inEvent) {
		this.mouseMove(inSender, inEvent);
		// Apply force to each pixel outwards from the mouse
		for (var i = 0; i < this.npixels; i++) {
			// Get the direction
			var dx = this.pixels[i].x - inEvent.clientX;
			var dy = this.pixels[i].y - inEvent.clientY;
			var mag = Math.sqrt(dx*dx + dy*dy);
			dx /= mag;
			dy /= mag;

			// Apply force.
			this.pixels[i].accel(dx * 40.0, dy * 40.0);
		}
	},
	engine: function() {
		// Clear the canvas
		this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
		// Update all pixel objects
		for (var i = 0; i < this.npixels; i++) {
			// Get distance
			var dx = this.mx - this.pixels[i].x;
			var dy = this.my - this.pixels[i].y;
			var d = Math.sqrt(dx*dx + dy*dy);

			// Apply acceleration to pixels towards the mouse cursor
			var ax = dx / 50.0;
			var ay = dy / 50.0;

			// Apply a drag proportional to the distance from the cursor
			if (d > 100.0) {
				ax += -this.pixels[i].vx * (d - 100.0) / 2000.0;
				ay += -this.pixels[i].vy * (d - 100.0) / 2000.0;
			}

			// And a "chaotic" acceleration inversely proportional
			// to the distance from the cursor
			ax += (Math.random() * 160.0 - 80.0) / d;
			ay += (Math.random() * 160.0 - 80.0) / d;

			this.pixels[i].accel(ax,ay);
			this.pixels[i].update();
			this.pixels[i].draw(this.context);
		}

		this.context.strokeRect(this.mx - 10, this.my - 10, 20, 20);
	}
});