#MonthOfCode - Day 22: whirlpool

My entry for the 22nd day of the month of code. The theme for today is: whirlpool.

Move your mouse over the canvas to adjust the whirlpool force!

Code after the break.

whirlpool.jsview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
document.addEventListener('DOMContentLoaded', function () {
var canvas = document.getElementById('moc-22');
var ctx = canvas.getContext('2d');
var img = new Image();

img.src = '/projects/monthofcode/22/my_cat.jpg';
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;

canvas.addEventListener('mousemove', function (ev) {
var ratio = event.offsetX / canvas.width;
var force = 4 * (ratio - 0.5); // between -2 and +2
whirlpool(force);
});

// initial rendering
whirlpool(1);
};



function whirlpool(force) {
// get the image pixels
ctx.drawImage(img, 0, 0);
var original = ctx.getImageData(0, 0, canvas.width, canvas.height);
var pixels = original.data;

// create a new image
var newImage = ctx.createImageData(canvas.width, canvas.height);
var data = newImage.data;


// for each new pixel, find the corresponding original pixel
for (var i = 0; i < data.length; i += 4) {
var x = (i / 4) % canvas.width;
var y = (i / 4 - x) / canvas.width;

var orig = getOriginalCoords(x, y, force);
var j = orig.x * 4 + orig.y * 4 * canvas.width;
data[i + 0] = pixels[j + 0];
data[i + 1] = pixels[j + 1];
data[i + 2] = pixels[j + 2];
data[i + 3] = pixels[j + 3];
}
ctx.putImageData(newImage, 0, 0);
}

function getOriginalCoords(x, y, force) {
// center
var cx = canvas.width / 2;
var cy = canvas.height / 2;

var dx = x - cx;
var dy = y - cy;
var alpha = Math.atan2(dy, dx);
var r = Math.sqrt(dx * dx + dy * dy);
var rmax = Math.min(canvas.width/2, canvas.height/2);

if (r < rmax) {
return {
x : Math.round(cx + r * Math.cos(alpha + (1 - r/rmax) * force * Math.PI)),
y : Math.round(cy + r * Math.sin(alpha + (1 - r/rmax) * force * Math.PI))
}
} else {
return {
x: x,
y: y
}
}
}
});