Skip to content

Commit 845e8cf

Browse files
committed
Optimize and clean up Tadpoles example.
1 parent bef908a commit 845e8cf

1 file changed

Lines changed: 129 additions & 124 deletions

File tree

examples/Paperjs.org/Tadpoles.html

Lines changed: 129 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -9,162 +9,176 @@
99
// Adapted from Flocking Processing example by Daniel Schiffman:
1010
// http://processing.org/learning/topics/flocking.html
1111

12-
project.currentStyle = {
13-
strokeColor: 'white',
14-
strokeWidth: 2,
15-
strokeCap: 'round'
16-
};
17-
18-
var head = new Path.Ellipse([0, 0], [13, 8]);
19-
head.fillColor = 'white';
20-
head.strokeColor = null;
21-
var headSymbol = new Symbol(head);
22-
23-
var size = view.size;
24-
2512
var Boid = Base.extend({
2613
initialize: function(position, maxSpeed, maxForce) {
2714
var strength = Math.random() * 0.5;
28-
this.acc = new Point(0, 0);
29-
this.vel = Point.random() * 2 - 1;
30-
this.loc = position.clone();
31-
this.r = 30;
15+
this.acceleration = new Point();
16+
this.vector = Point.random() * 2 - 1;
17+
this.position = position.clone();
18+
this.radius = 50;
3219
this.maxSpeed = maxSpeed + strength;
3320
this.maxForce = maxForce + strength;
34-
this.head = headSymbol.place();
35-
this.path = new Path();
36-
this.shortPath = new Path();
37-
this.shortPath.strokeWidth = 4;
21+
this.points = [];
3822
for (var i = 0, l = strength * 10 + 10; i < l; i++) {
39-
this.path.add(this.loc);
23+
this.points.push(new Point());
4024
}
41-
42-
this.firstSegment = this.path.firstSegment;
4325
this.count = 0;
44-
this.lastRot = 0;
26+
this.lastAngle = 0;
27+
this.distances = [];
28+
this.createItems();
4529
},
4630

4731
run: function(boids) {
48-
this.lastLoc = this.loc.clone();
32+
this.lastLoc = this.position.clone();
4933
if (!groupTogether) {
5034
this.flock(boids);
5135
} else {
5236
this.align(boids);
5337
}
5438
this.borders();
55-
5639
this.update();
57-
this.firstSegment.point = this.loc;
58-
var lastPoint = this.firstSegment.point;
59-
var lastVector = this.loc - this.lastLoc;
60-
var segments = this.path.segments;
61-
for (var i = 1, l = segments.length; i < l; i++) {
62-
var segment = segments[i];
63-
var vector = lastPoint - segment.point;
64-
this.count += this.vel.length * 10;
65-
var rotLength = Math.sin((this.count + i * 3) / 300);
66-
var rotated = lastVector.rotate(90).normalize(rotLength);
67-
lastPoint += lastVector.normalize(-5 - this.vel.length / 3);
68-
segment.point = lastPoint;
69-
segment.point += rotated;
40+
this.calculateTail();
41+
this.updateItems();
42+
},
43+
44+
calculateTail: function() {
45+
var points = this.points;
46+
var speed = this.vector.length;
47+
var pieceLength = 5 + speed * 0.3;
48+
var point = points[0] = this.position.clone();
49+
var lastVector = this.vector.clone();
50+
for (var i = 1, l = points.length; i < l; i++) {
51+
this.count += speed * 15;
52+
var vector = point - points[i];
53+
var rotated = lastVector.rotate(90);
54+
rotated.length = Math.sin((this.count + i * 3) * 0.003);
55+
lastVector.length = -pieceLength;
56+
point += lastVector;
57+
points[i] = point + rotated;
7058
lastVector = vector;
7159
}
60+
},
61+
62+
createItems: function() {
63+
this.head = (project.symbols[0]
64+
? project.symbols[0]
65+
: new Symbol(new Path.Ellipse({
66+
from: [0, 0],
67+
to: [13, 8],
68+
fillColor: 'white'
69+
}))).place();
70+
this.path = new Path({
71+
strokeColor: 'white',
72+
strokeWidth: 2,
73+
strokeCap: 'round'
74+
});
75+
this.shortPath = new Path({
76+
strokeColor: 'white',
77+
strokeWidth: 4,
78+
strokeCap: 'round'
79+
});
80+
},
81+
82+
updateItems: function() {
83+
this.path.segments = this.points;
7284
this.path.smooth();
73-
this.head.position = this.loc;
74-
var vector = this.loc - this.lastLoc;
75-
var rot = vector.angle;
76-
this.head.rotate(rot - this.lastRot);
77-
this.lastRot = rot;
78-
this.shortPath.segments = segments.slice(0, 3);
85+
this.shortPath.segments = this.points.slice(0, 3);
86+
87+
this.head.position = this.position;
88+
var angle = this.vector.angle;
89+
this.head.rotate(angle - this.lastAngle);
90+
this.lastAngle = angle;
7991
},
8092

8193
// We accumulate a new acceleration each time based on three rules
8294
flock: function(boids) {
83-
var sep = this.separate(boids) * 3;
84-
var ali = this.align(boids);
85-
var coh = this.cohesion(boids);
86-
this.acc += sep + ali + coh;
95+
this.calculateDistances(boids);
96+
var separation = this.separate(boids) * 3;
97+
var alignment = this.align(boids);
98+
var cohesion = this.cohesion(boids);
99+
this.acceleration += separation + alignment + cohesion;
100+
},
101+
102+
calculateDistances: function(boids) {
103+
for (var i = 0, l = boids.length; i < l; i++) {
104+
var other = boids[i];
105+
this.distances[i] = other.position.getDistance(this.position, true);
106+
}
87107
},
88108

89109
update: function() {
90110
// Update velocity
91-
this.vel += this.acc;
111+
this.vector += this.acceleration;
92112
// Limit speed (vector#limit?)
93-
this.vel.length = Math.min(this.maxSpeed, this.vel.length);
94-
this.loc += this.vel;
113+
this.vector.length = Math.min(this.maxSpeed, this.vector.length);
114+
this.position += this.vector;
95115
// Reset acceleration to 0 each cycle
96-
this.acc.length = 0;
116+
this.acceleration = new Point();
97117
},
98118

99119
seek: function(target) {
100-
this.acc += this.steer(target, false);
120+
this.acceleration += this.steer(target, false);
101121
},
102122

103123
arrive: function(target) {
104-
this.acc += this.steer(target, true);
124+
this.acceleration += this.steer(target, true);
125+
},
126+
127+
borders: function() {
128+
var vector = new Point();
129+
var position = this.position;
130+
var radius = this.radius;
131+
var size = view.size;
132+
if (position.x < -radius) vector.x = size.width + radius;
133+
if (position.y < -radius) vector.y = size.height + radius;
134+
if (position.x > size.width + radius) vector.x = -radius;
135+
if (position.y > size.height + radius) vector.y = -radius;
136+
if (!vector.isZero())
137+
this.position += vector;
105138
},
106139

107140
// A method that calculates a steering vector towards a target
108141
// Takes a second argument, if true, it slows down as it approaches
109142
// the target
110143
steer: function(target, slowdown) {
111144
var steer,
112-
desired = target - this.loc,
113-
d = desired.length;
114-
if (d > 0) {
115-
// Two options for desired vector magnitude
116-
// (1 -- based on distance, 2 -- maxSpeed)
117-
if (slowdown && d < 100) {
118-
// This damping is somewhat arbitrary:
119-
desired.length = this.maxSpeed * (d / 100);
120-
} else {
121-
desired.length = this.maxSpeed;
122-
}
123-
steer = desired - this.vel;
124-
steer.length = Math.min(this.maxForce, steer.length);
145+
desired = target - this.position;
146+
var distance = desired.length;
147+
// Two options for desired vector magnitude
148+
// (1 -- based on distance, 2 -- maxSpeed)
149+
if (slowdown && distance < 100) {
150+
// This damping is somewhat arbitrary:
151+
desired.length = this.maxSpeed * (distance * 0.001);
125152
} else {
126-
steer = new Point(0, 0);
153+
desired.length = this.maxSpeed;
127154
}
155+
steer = desired - this.vector;
156+
steer.length = Math.min(this.maxForce, steer.length);
128157
return steer;
129158
},
130159

131-
borders: function() {
132-
var loc = this.loc;
133-
var r = this.r;
134-
var oldLoc = this.loc.clone();
135-
var width = size.width;
136-
var height = size.height;
137-
if (loc.x < -r) loc.x = width + r;
138-
if (loc.y < -r) loc.y = height + r;
139-
if (loc.x > width + r) loc.x = -r;
140-
if (loc.y > height + r) loc.y = -r;
141-
var vector = this.loc - oldLoc;
142-
if (!vector.isZero())
143-
this.path.position += vector;
144-
},
145-
146160
separate: function(boids) {
147-
var desiredSeperation = 60;
148-
var steer = new Point(0, 0);
161+
var desiredSeperation = 3600;
162+
var steer = new Point();
149163
var count = 0;
150164
// For every boid in the system, check if it's too close
151165
for (var i = 0, l = boids.length; i < l; i++) {
152-
var other = boids[i];
153-
var d = other.loc.getDistance(this.loc);
154-
if (d > 0 && d < desiredSeperation) {
166+
var distance = this.distances[i];
167+
if (distance > 0 && distance < desiredSeperation) {
155168
// Calculate vector pointing away from neighbor
156-
var diff = this.loc - other.loc;
157-
steer += diff.normalize(1 / d);
169+
var delta = this.position - boids[i].position;
170+
delta.length = 1 / distance;
171+
steer += delta;
158172
count++;
159173
}
160174
}
161175
// Average -- divide by how many
162176
if (count > 0)
163177
steer /= count;
164-
if (steer.length > 0) {
178+
if (!steer.isZero()) {
165179
// Implement Reynolds: Steering = Desired - Velocity
166180
steer.length = this.maxSpeed;
167-
steer -= this.vel;
181+
steer -= this.vector;
168182
steer.length = Math.min(steer.length, this.maxForce);
169183
}
170184
return steer;
@@ -174,29 +188,22 @@
174188
// For every nearby boid in the system, calculate the average velocity
175189
align: function(boids) {
176190
var neighborDist = 25;
177-
var steer = new Point(0, 0);
191+
var steer = new Point();
178192
var count = 0;
179-
var nearest = 999;
180-
var closestPoint;
181193
for (var i = 0, l = boids.length; i < l; i++) {
182-
var other = boids[i];
183-
var d = this.loc.getDistance(other.loc);
184-
if (d > 0 && d < nearest) {
185-
closestPoint = other.loc;
186-
nearest = d;
187-
}
188-
if (d > 0 && d < neighborDist) {
189-
steer += other.vel;
194+
var distance = this.distances[i];
195+
if (distance > 0 && distance < neighborDist) {
196+
steer += boids[i].vector;
190197
count++;
191198
}
192199
}
193200

194201
if (count > 0)
195202
steer /= count;
196-
if (steer.length > 0) {
203+
if (!steer.isZero()) {
197204
// Implement Reynolds: Steering = Desired - Velocity
198205
steer.length = this.maxSpeed;
199-
steer -= this.vel;
206+
steer -= this.vector;
200207
steer.length = Math.min(steer.length, this.maxForce);
201208
}
202209
return steer;
@@ -206,14 +213,13 @@
206213
// For the average location (i.e. center) of all nearby boids,
207214
// calculate steering vector towards that location
208215
cohesion: function(boids) {
209-
var neighborDist = 100;
216+
var neighborDist = 10000;
210217
var sum = new Point(0, 0);
211218
var count = 0;
212219
for (var i = 0, l = boids.length; i < l; i++) {
213-
var other = boids[i];
214-
var d = this.loc.getDistance(other.loc);
215-
if (d > 0 && d < neighborDist) {
216-
sum += other.loc; // Add location
220+
var distance = this.distances[i];
221+
if (distance > 0 && distance < neighborDist) {
222+
sum += boids[i].position; // Add location
217223
count++;
218224
}
219225
}
@@ -226,46 +232,45 @@
226232
}
227233
});
228234

229-
var heartPath = Project.importJson('["Path",{"pathData":"M514.69629,624.70313c-7.10205,-27.02441 -17.2373,-52.39453 -30.40576,-76.10059c-13.17383,-23.70703 -38.65137,-60.52246 -76.44434,-110.45801c-27.71631,-36.64355 -44.78174,-59.89355 -51.19189,-69.74414c-10.5376,-16.02979 -18.15527,-30.74951 -22.84717,-44.14893c-4.69727,-13.39893 -7.04297,-26.97021 -7.04297,-40.71289c0,-25.42432 8.47119,-46.72559 25.42383,-63.90381c16.94775,-17.17871 37.90527,-25.76758 62.87354,-25.76758c25.19287,0 47.06885,8.93262 65.62158,26.79834c13.96826,13.28662 25.30615,33.10059 34.01318,59.4375c7.55859,-25.88037 18.20898,-45.57666 31.95215,-59.09424c19.00879,-18.32178 40.99707,-27.48535 65.96484,-27.48535c24.7373,0 45.69531,8.53564 62.87305,25.5957c17.17871,17.06592 25.76855,37.39551 25.76855,60.98389c0,20.61377 -5.04102,42.08691 -15.11719,64.41895c-10.08203,22.33203 -29.54687,51.59521 -58.40723,87.78271c-37.56738,47.41211 -64.93457,86.35352 -82.11328,116.8125c-13.51758,24.0498 -23.82422,49.24902 -30.9209,75.58594z","strokeColor":["rgb",1,1,1],"strokeWidth":2,"strokeCap":"round"}]');
230-
heartPath.position = view.center;
231-
heartPath.strokeColor = null;
232-
heartPath.scale(1.5);
233-
234-
var groupTogether = false;
235+
var heartPath = Project.importJson('["Path",{"pathData":"M514.69629,624.70313c-7.10205,-27.02441 -17.2373,-52.39453 -30.40576,-76.10059c-13.17383,-23.70703 -38.65137,-60.52246 -76.44434,-110.45801c-27.71631,-36.64355 -44.78174,-59.89355 -51.19189,-69.74414c-10.5376,-16.02979 -18.15527,-30.74951 -22.84717,-44.14893c-4.69727,-13.39893 -7.04297,-26.97021 -7.04297,-40.71289c0,-25.42432 8.47119,-46.72559 25.42383,-63.90381c16.94775,-17.17871 37.90527,-25.76758 62.87354,-25.76758c25.19287,0 47.06885,8.93262 65.62158,26.79834c13.96826,13.28662 25.30615,33.10059 34.01318,59.4375c7.55859,-25.88037 18.20898,-45.57666 31.95215,-59.09424c19.00879,-18.32178 40.99707,-27.48535 65.96484,-27.48535c24.7373,0 45.69531,8.53564 62.87305,25.5957c17.17871,17.06592 25.76855,37.39551 25.76855,60.98389c0,20.61377 -5.04102,42.08691 -15.11719,64.41895c-10.08203,22.33203 -29.54687,51.59521 -58.40723,87.78271c-37.56738,47.41211 -64.93457,86.35352 -82.11328,116.8125c-13.51758,24.0498 -23.82422,49.24902 -30.9209,75.58594z","strokeWidth":2,"strokeCap":"round"}]');
235236
var pathLength = heartPath.length;
236-
var mouseDown = false;
237+
237238
var boids = [];
239+
var groupTogether = false;
238240

239241
// Add the boids:
240242
for (var i = 0; i < 30; i++) {
241-
var position = Point.random() * size;
243+
var position = Point.random() * view.size;
242244
boids.push(new Boid(position, 10, 0.05));
243245
}
244246

247+
245248
function onFrame(event) {
246249
for (var i = 0, l = boids.length; i < l; i++) {
247250
if (groupTogether) {
248251
var length = ((i + event.count / 30) % l) / l * pathLength;
249252
var point = heartPath.getPointAt(length);
250-
boids[i].arrive(point);
253+
if (point)
254+
boids[i].arrive(point);
251255
}
252256
boids[i].run(boids);
253257
}
254258
}
255259

256260
// Reposition the heart path whenever the window is resized:
257261
function onResize(event) {
258-
size = view.size;
259-
heartPath.position = view.center;
262+
heartPath.fitBounds(view.bounds);
263+
heartPath.scale(0.8);
264+
pathLength = heartPath.length;
260265
}
261266

262267
function onMouseDown(event) {
263268
groupTogether = !groupTogether;
264269
}
265270

266-
var layer = project.activeLayer;
267271
function onKeyDown(event) {
268272
if (event.key == 'space') {
273+
var layer = project.activeLayer;
269274
layer.selected = !layer.selected;
270275
return false;
271276
}

0 commit comments

Comments
 (0)