2011-04-19 15:27:59 -04:00
<!DOCTYPE html>
< html >
< head >
< meta http-equiv = "Content-Type" content = "text/html; charset=utf-8" >
< title > Example< / title >
< script type = "text/javascript" > var root = '../../' < / script >
< script type = "text/javascript" src = "../../src/load.js" > < / script >
< script type = "text/paperscript" canvas = "canvas" >
2011-04-20 07:07:40 -04:00
// Adapted from Flocking Processing example by Daniel Schiffman:
2011-04-19 17:32:34 -04:00
// http://processing.org/learning/topics/flocking.html
2011-04-19 15:27:59 -04:00
document.currentStyle = {
strokeColor: 'white',
strokeWidth: 2,
strokeCap: 'round'
};
var bounds = document.bounds,
width = bounds.width,
height = bounds.height;
2011-04-20 07:07:40 -04:00
var head = new Path.Oval([0, 0], [13, 8]);
head.fillColor = 'white';
head.strokeColor = null;
var headSymbol = new Symbol(head);
2011-04-19 15:27:59 -04:00
var Boid = Base.extend({
initialize: function(position, maxSpeed, maxForce) {
var strength = Math.random() * 0.5;
this.acc = new Point(0, 0);
this.vel = new Point(Math.random() * 2 - 1, Math.random() * 2 - 1);
this.loc = position.clone();
this.r = 30;
this.maxSpeed = maxSpeed + strength;
this.maxForce = maxForce + strength;
2011-04-20 07:07:40 -04:00
this.head = new PlacedSymbol(head);
2011-04-19 15:27:59 -04:00
this.path = new Path();
2011-04-20 07:07:40 -04:00
this.shortPath = new Path();
2011-04-26 10:01:41 -04:00
this.shortPath.strokeWidth = 4;
2011-04-19 15:27:59 -04:00
for(var i = 0, l = strength * 10 + 10; i < l ; i + + ) {
this.path.add(this.loc);
2011-04-20 07:07:40 -04:00
if (i < 3 )
this.shortPath.add(this.loc);
2011-04-19 15:27:59 -04:00
}
2011-04-20 07:07:40 -04:00
2011-04-19 15:27:59 -04:00
this.firstSegment = this.path.segments[0];
this.count = 0;
2011-04-20 07:07:40 -04:00
this.lastRot = 0;
2011-04-19 15:27:59 -04:00
},
run: function(boids) {
this.lastLoc = this.loc.clone();
2011-04-26 10:01:41 -04:00
if (!groupTogether) {
2011-04-19 15:27:59 -04:00
this.flock(boids);
2011-04-20 07:07:40 -04:00
} else {
this.align(boids);
}
2011-04-19 15:27:59 -04:00
this.borders();
this.update();
this.firstSegment.point = this.loc;
var lastPoint = this.firstSegment.point;
var lastVector = this.loc - this.lastLoc;
var segments = this.path.segments;
for (var i = 1, l = segments.length; i < l ; i + + ) {
var segment = segments[i];
var vector = lastPoint - segment.point;
this.count += this.vel.length * 10;
var rotated = lastVector.rotate(90).normalize(Math.sin((this.count + i * 3) / 300));
lastPoint = segment.point = lastPoint + lastVector.normalize(-5 - this.vel.length / 3);
segment.point += rotated;
lastVector = vector;
}
this.head.position = this.loc;
2011-04-20 07:07:40 -04:00
var vector = this.loc - this.lastLoc;
var rot = vector.angle;
this.head.rotate(rot - this.lastRot);
this.lastRot = rot;
var shortSegments = this.shortPath.segments;
2011-04-26 10:01:41 -04:00
for (var i = 0; i < 3 ; i + + )
2011-04-20 07:07:40 -04:00
shortSegments[i] = segments[i].clone();
2011-04-19 15:27:59 -04:00
},
// We accumulate a new acceleration each time based on three rules
flock: function(boids) {
var sep = this.separate(boids) * 3;
var ali = this.align(boids);
var coh = this.cohesion(boids);
this.acc += sep + ali + coh;
},
update: function() {
// Update velocity
this.vel += this.acc;
// Limit speed (vector#limit?)
this.vel.length = Math.min(this.maxSpeed, this.vel.length);
this.loc += this.vel;
// Reset acceleration to 0 each cycle
this.acc.length = 0;
},
seek: function(target) {
this.acc += this.steer(target, false);
},
arrive: function(target) {
this.acc += this.steer(target, true);
},
// A method that calculates a steering vector towards a target
// Takes a second argument, if true, it slows down as it approaches the target
steer: function(target, slowdown) {
var steer,
desired = target - this.loc,
d = desired.length;
if (d > 0) {
// Two options for desired vector magnitude (1 -- based on distance, 2 -- maxSpeed)
if (slowdown & & d < 100 ) {
desired.length = this.maxSpeed * (d / 100); // This damping is somewhat arbitrary
} else {
desired.length = this.maxSpeed;
}
steer = desired - this.vel;
steer.length = Math.min(this.maxForce, steer.length);
} else {
steer = new Point(0, 0);
}
return steer;
},
borders: function() {
var loc = this.loc,
r = this.r,
oldLoc = this.loc.clone();
if (loc.x < -r ) loc . x = width + r ;
if (loc.y < -r ) loc . y = height + r ;
if (loc.x > width + r) loc.x = -r;
if (loc.y > height + r) loc.y = -r;
var vector = this.loc - oldLoc;
2011-04-26 10:01:41 -04:00
if (!vector.isZero())
2011-04-19 15:27:59 -04:00
this.path.position += vector;
},
separate: function(boids) {
var desiredSeperation = 60,
steer = new Point(0, 0),
count = 0;
// For every boid in the system, check if it's too close
for (var i = 0, l = boids.length; i < l ; i + + ) {
var other = boids[i];
var d = other.loc.getDistance(this.loc);
if (d > 0 & & d < desiredSeperation ) {
// Calculate vector pointing away from neighbor
var diff = this.loc - other.loc;
steer += diff.normalize(1 / d);
count++;
}
}
// Average -- divide by how many
if (count > 0)
steer /= count;
2011-04-26 10:01:41 -04:00
if (steer.length > 0) {
2011-04-19 15:27:59 -04:00
// Implement Reynolds: Steering = Desired - Velocity
steer.length = this.maxSpeed;
steer -= this.vel;
steer.length = Math.min(steer.length, this.maxForce);
}
return steer;
},
// Alignment
// For every nearby boid in the system, calculate the average velocity
align: function(boids) {
var neighborDist = 25,
steer = new Point(0, 0),
count = 0,
2011-04-20 07:07:40 -04:00
nearest = 999,
closestPoint;
2011-04-19 15:27:59 -04:00
for (var i = 0, l = boids.length; i < l ; i + + ) {
var other = boids[i];
var d = this.loc.getDistance(other.loc);
2011-04-26 10:01:41 -04:00
if (d > 0 & & d < nearest ) {
2011-04-20 07:07:40 -04:00
closestPoint = other.loc;
2011-04-19 15:27:59 -04:00
nearest = d;
2011-04-20 07:07:40 -04:00
}
2011-04-26 10:01:41 -04:00
if (d > 0 & & d < neighborDist ) {
2011-04-19 15:27:59 -04:00
steer += other.vel;
count++;
}
}
2011-04-20 07:07:40 -04:00
2011-04-19 15:27:59 -04:00
if (count > 0)
steer /= count;
if (steer.length > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.length = this.maxSpeed;
steer -= this.vel;
steer.length = Math.min(steer.length, this.maxForce);
}
return steer;
},
// Cohesion
// For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
cohesion: function(boids) {
var neighborDist = 100,
sum = new Point(0, 0),
count = 0;
for (var i = 0, l = boids.length; i < l ; i + + ) {
var other = boids[i];
var d = this.loc.getDistance(other.loc);
2011-04-26 10:01:41 -04:00
if (d > 0 & & d < neighborDist ) {
2011-04-19 15:27:59 -04:00
sum += other.loc; // Add location
count++;
}
}
if (count > 0) {
sum /= count;
// Steer towards the location
return this.steer(sum, false);
}
return sum;
}
});
var boids = [],
center = document.bounds.center;
2011-04-20 07:07:40 -04:00
for (var i = 0; i < 30 ; i + + ) {
2011-04-19 15:27:59 -04:00
var position = Point.random() * document.size;
boids.push(new Boid(position, 10, 0.05));
}
2011-04-19 17:32:34 -04:00
var heartPath = new Path(
2011-04-20 07:07:40 -04:00
new Segment(new Point(514.6962890625, 624.703125), new Point(7.0966796875, -26.3369140625), new Point(-7.10205078125, -27.0244140625)),
new Segment(new Point(484.29052734375, 548.6025390625), new Point(13.16845703125, 23.7060546875), new Point(-13.173828125, -23.70703125)),
new Segment(new Point(407.84619140625, 438.14453125), new Point(37.79296875, 49.935546875), new Point(-27.71630859375, -36.6435546875)),
new Segment(new Point(356.654296875, 368.400390625), new Point(6.41015625, 9.8505859375), new Point(-10.53759765625, -16.02978515625)),
new Segment(new Point(333.80712890625, 324.25146484375), new Point(4.69189453125, 13.3994140625), new Point(-4.697265625, -13.39892578125)),
new Segment(new Point(326.76416015625, 283.53857421875), new Point(0, 13.74267578125), new Point(0, -25.42431640625)),
new Segment(new Point(352.18798828125, 219.634765625), new Point(-16.95263671875, 17.17822265625), new Point(16.94775390625, -17.1787109375)),
new Segment(new Point(415.0615234375, 193.8671875), new Point(-24.96826171875, 0), new Point(25.19287109375, 0)), new Segment(new Point(480.68310546875, 220.66552734375), new Point(-18.552734375, -17.86572265625), new Point(13.96826171875, 13.28662109375)), new Segment(new Point(514.6962890625, 280.10302734375), new Point(-8.70703125, -26.3369140625), new Point(7.55859375, -25.88037109375)), new Segment(new Point(546.6484375, 221.0087890625), new Point(-13.7431640625, 13.517578125), new Point(19.0087890625, -18.32177734375)), new Segment(new Point(612.61328125, 193.5234375), new Point(-24.9677734375, 0), new Point(24.7373046875, 0)), new Segment(new Point(675.486328125, 219.119140625), new Point(-17.177734375, -17.06005859375), new Point(17.1787109375, 17.06591796875)), new Segment(new Point(701.2548828125, 280.10302734375), new Point(0, -23.58837890625), new Point(0, 20.61376953125)), new Segment(new Point(686.1376953125, 344.52197265625), new Point(10.076171875, -22.33203125), new Point(-10.08203125, 22.33203125)), new Segment(new Point(627.73046875, 432.3046875), new Point(28.8603515625, -36.1875), new Point(-37.5673828125, 47.412109375)), new Segment(new Point(545.6171875, 549.1171875), new Point(17.1787109375, -30.458984375), new Point(-13.517578125, 24.0498046875))
2011-04-19 15:27:59 -04:00
);
2011-04-19 17:32:34 -04:00
heartPath.closed = true;
heartPath.position = document.bounds.center;
heartPath.strokeColor = null;
heartPath.scale(1.5);
2011-04-19 15:27:59 -04:00
var count = 0,
groupTogether = false,
2011-04-19 17:32:34 -04:00
pathLength = heartPath.length,
2011-04-19 15:27:59 -04:00
mouseDown = false;
2011-04-22 11:31:46 -04:00
function onFrame() {
2011-04-19 15:27:59 -04:00
count++;
for(var i = 0, l = boids.length; i < l ; i + + ) {
if (groupTogether) {
2011-04-27 16:40:52 -04:00
var point = heartPath.getPointAt(((i + count / 30) % l) / l * pathLength);
2011-04-19 15:27:59 -04:00
boids[i].arrive(point);
}
boids[i].run(boids);
}
}
function onMouseDown(event) {
2011-04-19 15:38:06 -04:00
groupTogether = !groupTogether;
2011-04-19 15:27:59 -04:00
}
< / script >
< / head >
< body style = 'background-color: black' >
< canvas id = 'canvas' width = 1024 height = 768 > < / canvas >
< / body >