You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

173 lines
5.0 KiB
HTML

<!doctype html>
<html>
<head>
<title>pack</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="../../pattern/canvas.js"></script>
</head>
<body>
<script type="text/canvas">
var Circle = Class.extend({
init: function(x, y, radius, image) {
/* An object that can be passed to pack(),
* with a repulsion radius and an image to draw inside the radius.
*/
this.x = x;
this.y = y;
this.radius = radius;
this.image = image;
this.goal = new Point(x,y);
},
contains: function(x, y) {
return geometry.distance(this.x, this.y, x, y) <= this.radius;
},
draw: function() {
var a = geometry.angle(this.x, this.y, this.goal.x, this.goal.y);
var r = this.radius * 1.25; // The cells can overlap a little bit.
var w = this.image.width;
var h = this.image.height;
push();
translate(this.x, this.y);
scale(r * 2 / Math.min(w, h));
rotate(a);
image(this.image, -w/2, -h/2); // Rotate from image center.
pop();
}
});
function pack(circles, x, y, padding, exclude) {
/* Circle-packing algorithm.
* Groups the given list of Circle objects around (x,y) in an organic way.
*/
// Ported from Sean McCullough's Processing code:
// http://www.cricketschirping.com/processing/CirclePacking1/
// See also: http://en.wiki.mcneel.com/default.aspx/McNeel/2DCirclePacking
// Repulsive force: move away from intersecting circles.
for (var i=0; i < circles.length; i++) {
for (var j=i+1; j < circles.length; j++) {
var circle1 = circles[i];
var circle2 = circles[j];
var d = geometry.distance(circle1.x, circle1.y, circle2.x, circle2.y);
var r = circle1.radius + circle2.radius + padding;
if (d < r - 0.01) {
var dx = circle2.x - circle1.x;
var dy = circle2.y - circle1.y;
var vx = (dx / d) * (r-d) * 0.5;
var vy = (dy / d) * (r-d) * 0.5;
if (!Array.contains(exclude, circle1)) {
circle1.x -= vx;
circle1.y -= vy;
}
if (!Array.contains(exclude, circle2)) {
circle2.x += vx;
circle2.y += vy;
}
}
}
}
// Attractive force: move all circles to center.
Array.enumerate(circles, function(i, circle) {
circle.goal.x = x;
circle.goal.y = y;
if (!Array.contains(exclude, circle)) {
var damping = Math.pow(circle.radius, 3) * 0.000001; // Big ones in the middle.
var vx = (circle.x - x) * damping;
var vy = (circle.y - y) * damping;
circle.x -= vx;
circle.y -= vy;
}
});
}
function cell(t) {
// Returns a random PNG-image (artwork © Ludivine Lechat).
// Some cells occur more frequently than others:
// t is a number between 0.0 and 1.0 that determines which image to pick.
// This is handy when combined with smoothstep(),
// then we can put a preference on empty blue cells,
// while still ensuring that some of each cell appear.
var url = "http://www.clips.ua.ac.be/media/canvas/examples/g/";
if (t < 0.4) {
url += Array.choice([
"green-empty1.png",
"green-empty2.png",
"green-empty3.png",
"green-block1.png",
"green-block2.png"]);
} else if (t < 0.5) {
url += Array.choice([
"green-circle1.png",
"green-circle2.png"]);
} else if (t < 0.6) {
url += Array.choice([
"green-star1.png",
"green-star2.png"]);
} else {
url += Array.choice([
"blue-block.png",
"blue-circle.png",
"blue-star.png",
"blue-empty1.png",
"blue-empty1.png",
"blue-empty2.png",
"blue-empty2.png",
"blue-empty2.png"]);
}
return new Image(url);
}
function setup(canvas) {
circles = [];
dragged = null;
size(500, 500);
var n = 60;
for (var i=0; i < n; i++) {
// Create a group of n cells.
// Smoothstep yields more numbers near 1.0 than near 0.0,
// so we'll got mostly empty blue cells.
var t = geometry.smoothstep(0, n, i);
circles.push(
new Circle(
random(-100), // Start offscreen to the left.
random(canvas.height),
10 + 0.5 * t * i, // Make the blue cells bigger.
cell(t)
)
);
}
}
var iterations = 0;
function draw(canvas) {
background(1);
// Cells can be dragged.
if (dragged) {
dragged.x = canvas.mouse.x;
dragged.y = canvas.mouse.y;
iterations = 0;
}
if (!canvas.mouse.pressed) {
dragged = null;
} else if (!dragged) {
for (var i=0; i < circles.length; i++) {
if (circles[i].contains(canvas.mouse.x, canvas.mouse.y)) {
dragged = circles[i];
break;
}
}
}
// Draw all cells.
Array.enumerate(circles, function(i, circle) {
circle.draw();
});
// Circle packing.
if (iterations < 1000) {
pack(circles, 250, 250, 2, [dragged]);
}
iterations++;
}
</script>
</body>
</html>