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
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> |