Koch Curve
Renders a simple fractal, the Koch snowflake. Each recursive level is drawn in sequence. By Daniel Shiffman
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
let k;
function setup() {
createCanvas(710, 400);
frameRate(1); // Animate slowly
k = new KochFractal();
}
function draw() {
background(0);
// Draws the snowflake!
k.render();
// Iterate
k.nextLevel();
// Let's not do it more than 5 times. . .
if (k.getCount() > 5) {
k.restart();
}
}
// A class to describe one line segment in the fractal
// Includes methods to calculate midp5.Vectors along the line according to the Koch algorithm
class KochLine {
constructor(a,b) {
// Two p5.Vectors,
// start is the "left" p5.Vector and
// end is the "right p5.Vector
this.start = a.copy();
this.end = b.copy();
}
display() {
stroke(255);
line(this.start.x, this.start.y, this.end.x, this.end.y);
}
kochA() {
return this.start.copy();
}
// This is easy, just 1/3 of the way
kochB() {
let v = p5.Vector.sub(this.end, this.start);
v.div(3);
v.add(this.start);
return v;
}
// More complicated, have to use a little trig to figure out where this p5.Vector is!
kochC() {
let a = this.start.copy(); // Start at the beginning
let v = p5.Vector.sub(this.end, this.start);
v.div(3);
a.add(v); // Move to point B
v.rotate(-PI/3); // Rotate 60 degrees
a.add(v); // Move to point C
return a;
}
// Easy, just 2/3 of the way
kochD() {
let v = p5.Vector.sub(this.end, this.start);
v.mult(2/3.0);
v.add(this.start);
return v;
}
kochE() {
return this.end.copy();
}
}
// A class to manage the list of line segments in the snowflake pattern
class KochFractal {
constructor() {
this.start = createVector(0,height-20); // A p5.Vector for the start
this.end = createVector(width,height-20); // A p5.Vector for the end
this.lines = []; // An array to keep track of all the lines
this.count = 0;
this.restart();
}
nextLevel() {
// For every line that is in the arraylist
// create 4 more lines in a new arraylist
this.lines = this.iterate(this.lines);
this.count++;
}
restart() {
this.count = 0; // Reset count
this.lines = []; // Empty the array list
this.lines.push(new KochLine(this.start,this.end)); // Add the initial line (from one end p5.Vector to the other)
}
getCount() {
return this.count;
}
// This is easy, just draw all the lines
render() {
for(let i = 0; i < this.lines.length; i++) {
this.lines[i].display();
}
}
// This is where the **MAGIC** happens
// Step 1: Create an empty arraylist
// Step 2: For every line currently in the arraylist
// - calculate 4 line segments based on Koch algorithm
// - add all 4 line segments into the new arraylist
// Step 3: Return the new arraylist and it becomes the list of line segments for the structure
// As we do this over and over again, each line gets broken into 4 lines, which gets broken into 4 lines, and so on. . .
iterate(before) {
let now = []; // Create emtpy list
for(let i = 0; i < this.lines.length; i++) {
let l = this.lines[i];
// Calculate 5 koch p5.Vectors (done for us by the line object)
let a = l.kochA();
let b = l.kochB();
let c = l.kochC();
let d = l.kochD();
let e = l.kochE();
// Make line segments between all the p5.Vectors and add them
now.push(new KochLine(a,b));
now.push(new KochLine(b,c));
now.push(new KochLine(c,d));
now.push(new KochLine(d,e));
}
return now;
}
}