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.

229 lines
6.1 KiB
HTML

<!doctype html>
<html>
<head>
<title>canvas.js | basics (2)</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">
/* canvas.js includes a Class object useful for OO-style programming.
* This example demonstrates classes (Food, Pheromone, Ant, Colony)
* for building an ant colony simulation.
* The class syntax is as follows:
*
* var Person = Class.extend({
* init: function(name) {
* this.name = name;
* },
* greet: function() {
* return "Hello, my name is " + this.name;
* }
* });
*
* var p = new Person("Aunt Hillary");
*
* Note the comma between class methods.
*/
function distance(v1, v2) {
return Math.sqrt(Math.pow(v1.x-v2.x, 2) + Math.pow(v1.y-v2.y, 2));
}
var Food = Class.extend({
init: function(x, y, amount) {
this.x = x;
this.y = y;
this.amount = amount;
}
});
var Pheromone = Class.extend({
init: function(x, y) {
this.x = x;
this.y = y;
this.strength = 1.0;
},
evaporate: function(m) {
this.strength *= (this.strength > 0.01)? (m || 0.995) : 0;
}
});
var Ant = Class.extend({
init: function(colony, x, y) {
this.colony = colony;
this.x = x;
this.y = y;
this.v = {x:0, y:0};
this.food = false;
this.trail = [];
this.roaming = random(100);
},
goal: function(x, y) {
/* Sets the current position to move towards.
*/
var d = distance(this, {x:x,y:y}) + 0.0001;
this.v.x = (x - this.x) / d;
this.v.y = (y - this.y) / d;
this.roaming = 0;
},
roam: function(m) {
/* Roam around freely.
* Eventually, the ant returns to the colony to start a new roaming cycle.
*/
this.v.x += random(-m, m);
this.v.y += random(-m, m);
this.roaming += 1;
if (this.roaming > this.colony.radius) {
this.goal(this.colony.x, this.colony.y);
}
if (distance(this, this.colony) < 10) {
this.roaming = 0;
}
},
track: function() {
/* Follow nearby trails, from pheromone to pheromone.
* If the trail has evaporated too much, the ant may lose interest.
*/
for (var i=0; i < this.colony.ants.length; i++) {
for (var j=0; j < this.colony.ants[i].trail.length; j++) {
var pheromone = this.colony.ants[i].trail[j];
if (distance(this, pheromone) < pheromone.strength * 30) {
if (random() < pheromone.strength) {
this.goal(pheromone.x, pheromone.y);
}
return;
}
}
}
},
harvest: function() {
/* Collect nearby food.
* Mark the food source with pheromones for other ants.
*/
for (var i=0; i < this.colony.foodsources.length; i++) {
var food = this.colony.foodsources[i];
if (distance(this, food) < Math.max(1, food.amount/2)) {
food.amount -= 1;
if (food.amount <= 0) {
this.colony.foodsources.splice(i, 1);
}
this.trail = [];
this.trail.push(new Pheromone(food.x, food.y));
this.trail.push(new Pheromone(this.x, this.y));
this.food = true;
}
}
},
hoard: function(m) {
/* Return home with food.
* Mark the trail with pheromones for other ants.
*/
this.goal(this.colony.x, this.colony.y);
if (random() < (m || 0.5)) {
this.trail.push(new Pheromone(this.x, this.y));
}
if (distance(this, this.colony) < 5) {
this.food = false;
this.colony.food += 1;
}
},
forage: function() {
if (this.food === false) {
this.roam(0.3); // 1) Roam around.
this.track(); // 2) Follow trails to food.
this.harvest(); // 3) Pick up food and mark source.
} else { // 4) Bring food home + mark trail.
this.hoard();
}
this.v.x = Math.clamp(this.v.x, -1, +1);
this.v.y = Math.clamp(this.v.y, -1, +1);
this.x += this.v.x;
this.y += this.v.y;
// Evaporate trail.
for (var i=0; i < this.trail.length; i++) {
var pheromone = this.trail[i];
pheromone.evaporate();
if (pheromone.strength === 0) {
this.trail.splice(i, 1);
}
}
}
});
var Colony = Class.extend({
init: function(x, y, radius, size) {
this.x = x;
this.y = y;
this.radius = radius;
this.foodsources = [];
this.food = 0;
this.ants = [];
for (var i=0; i < size; i++) {
this.ants.push(new Ant(this, x, y));
}
}
});
function setup(canvas) {
canvas.size(600, 300);
// Create a fancy gradient background image.
terrain = render(function() {
rect(0, 0, 600, 300, {
fill: new Gradient(new Color(0.25,0.35,0.10), new Color(0.10,0.15,0.05), {
x: 300,
y: 150,
spread: 250,
type: RADIAL
})
});
}, 600, 300);
// Create a colony with 20 ants and 10 food sources.
colony = new Colony(300, 150, 200, 20);
for (var i=0; i < 10; i++) {
colony.foodsources.push(
new Food(random(30,-30 + canvas.width),
random(30,-30 + canvas.height),
random(20, 50)));
}
}
function draw(canvas) {
canvas.clear();
// 1) Draw pre-rendered terrain.
image(terrain);
// 2) Draw the hoarded food in the colony.
fill(1, 0.1);
ellipse(colony.x, colony.y, colony.food, colony.food);
// 3) Draw food sources.
fill(0, 0.4);
Array.enumerate(colony.foodsources, function(i, food) {
ellipse(food.x, food.y, food.amount, food.amount);
});
// 4) Draw ant trails.
var p = new BezierPath();
Array.enumerate(colony.ants, function(i, ant) {
Array.enumerate(ant.trail, function(i, pheromone) {
if (i === 0) {
p.moveto(pheromone.x, pheromone.y);
} else {
p.lineto(pheromone.x, pheromone.y);
}
});
});
drawpath(p, {stroke: new Color(1, 0.2), strokewidth: 0.5, fill: null});
// 5) Draw ants: roaming = white, hoarding = green.
Array.enumerate(colony.ants, function(i, ant) {
ant.forage();
if (ant.food) {
fill(0.5, 1, 0);
} else {
fill(1, 0.4);
}
ellipse(ant.x, ant.y, 3, 3);
});
}
</script>
</body>
</html>