xxxxxxxxxx
import java.util.ArrayList;
Web w;
float speed = .25;
float spiderSize = 20;
float lineWeight = .5;
int spokeCount, ringCount;
float spokeRandomness, ringRandomness;
void setup() {
size(800, 800);
imageMode(CENTER);
strokeWeight(lineWeight);
stroke(220);
background(50);
fill(150, 0, 0);
smooth();
randomize();
w = new Web();
}
void draw() {
background(50);
if (!w.complete) {
w.update();
w.draw();
} else {
w.draw();
//ellipse(w.last.x, w.last.y, spiderSize, spiderSize*1.4);
}
}
// Clicking mouse generates new random web
void mouseClicked() {
w.reset();
}
// Set some random spoke and ring variables
void randomize() {
spokeCount = 22; // int(random(12, 20));
ringCount = int(random(7,10));
ringRandomness = random(.009, .02);
spokeRandomness = 0; //random(.05, .5);
}
// Besides defining the endpoints of a segment,
// also keeps track of how much of that segment is drawn.
// Animation of drawing depends on this class.
class Segment {
float x1, y1, x2, y2;
float percent; // 0.0 to 1.0
Segment( float x1, float y1, float x2, float y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.percent = 0.0;
}
Segment(Point p1, Point p2) {
this(p1.x, p1.y, p2.x, p2.y);
}
void tick() {
if (percent < 1.0)
percent += speed;
}
void draw() {
float xEnd = x1 + percent * (x2 - x1);
float yEnd = y1 + percent * (y2 - y1);
line(x1, y1, xEnd, yEnd);
ellipse(xEnd, yEnd, spiderSize, spiderSize);
}
boolean complete() {
return percent >= 1.0;
}
}
class Point {
float x, y;
Point (float x, float y) {
this.x = x;
this.y = y;
}
}
// The path the spider will follow:
class Web {
ArrayList<Point> points;
ArrayList<Float> angles;
ArrayList<Segment> completed;
Segment segment;
boolean complete;
Point last;
Web() {
reset();
}
void reset() {
segment = null;
points = new ArrayList<Point>();
completed = new ArrayList<Segment>();
angles = new ArrayList<Float>();
complete = false;
randomize();
build();
}
void addPoint(Point p) {
points.add(p);
update();
}
void update() {
if (segment != null && segment.complete()) {
completed.add(segment);
segment = null;
}
if (segment == null) {
if (points.size() >= 2) {
segment = new Segment(points.get(0), points.get(1));
points.remove(0);
} else {
segment = null;
complete = false;
}
}
}
void addPoint(float x, float y) {
Point p = new Point(x, y);
addPoint(p);
last = points.get(points.size() - 1);
}
void draw() {
// Draw completed segments quickly:
for (Segment s : completed) {
line(s.x1, s.y1, s.x2, s.y2);
}
// Animate current segment
if (segment != null) {
segment.draw();
segment.tick();
} else {
complete = true;
}
}
void build() {
buildSpokes(2*PI / spokeCount);
for (int i = 0; i < ringCount; i++) {
buildRing(1.0 - .25 * i / ringCount);
}
loadPath();
last = points.get(points.size() - 1);
}
void loadPath() {
int[] path = {
402, 406, 155, 293, 87, 329, 158, 297, 128, 312,
148, 437, 134, 315, 159, 294, 179, 303, 169, 371, 179, 372, 195,
374, 169, 370, 178, 334, 221, 327, 180, 339, 180, 305, 243, 270,
184, 309, 256, 338, 286, 227, 342, 216, 358, 259, 325, 280, 276,
279, 296, 355, 275, 278, 325, 282, 358, 261, 343, 221, 288, 232,
258, 335, 397, 401, 401, 333, 400, 225, 457, 209, 442, 217, 448,
243, 438, 217, 401, 229, 401, 318, 404, 274, 447, 247, 406, 270,
419, 267, 428, 304, 416, 264, 404, 276, 400, 397, 457, 357, 482,
274, 491, 274, 473, 341, 465, 352, 458, 376, 471, 370, 477, 340,
465, 347, 406, 398, 495, 389, 521, 298, 581, 321, 526, 297, 513,
339, 539, 341, 516, 345, 503, 386, 504, 357, 501, 385, 590, 376,
608, 311, 620, 305, 599, 371, 585, 375, 630, 370, 644, 296, 672,
283, 704, 294, 672, 281, 641, 291, 637, 344, 668, 362, 637, 350,
639, 300
};
for (int i = 0; i < path.length; i+=2) {
addPoint(path[i], path[i+1]);
}
}
// percent refers to the percent of the distance from the center
// to the outer edge. 1.0 means the ring should be drawn at the
// outer edge. 0.0 means it should be drawn at the center (which
// we would never do).
void buildRing(float percent) {
// put a point on each spoke that is
// the same distance from the center:
float rad = height/2 * percent;
for (float a : angles) {
float randRad = rad * random(1-ringRandomness, 1+ringRandomness);
addPoint(width/2 + randRad * sin(a), height/2 - randRad * cos(a));
}
}
void buildSpokes(float spokeAngle) {
float angleSoFar = 0;
while (spokeAngle < 2*PI - angleSoFar) {
float nextAngle = spokeAngle + spokeAngle * random(-spokeRandomness, spokeRandomness);
angleSoFar += nextAngle;
angles.add(angleSoFar);
}
angles.add(angles.get(0));
// Now we have the angles for the spokes...create the corresponding endpoints
// each radiating from/to the center:
float maxRad = dist(width/2, height/2, 0, 0);
for (float a : angles) {
addPoint(width/2, height/2);
float x, y;
x = y = 0;
if (a > 7 * PI/4 || a <= PI/4) {
y = 0;
x = width/2 + (height/2) * tan(a);
} else if (a <= 3 * PI/4) {
x = width;
y = height/2 - tan(PI/2 - a) * width/2;
} else if (a <= 5 * PI/4) {
y = height;
x = width/2 - (height/2)/tan(PI/2 - a);
} else { // if (angleSoFar <= 7 * PI/4)
x = 0;
y = height/2 + tan(PI/2 - a) * width/2;
}
addPoint(x, y);
}
}
}