-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.html
282 lines (218 loc) · 11.6 KB
/
index.html
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
<!doctype html>
<html>
<head>
<title>Fractals</title>
<meta charset="UTF-8">
<meta name="description" content="Fractals are patterns that are generated by repeating themselves. Watch the animated process of their creation, drawn in Javascript: The Sierpiński Triangle & The Sierpiński Arrowhead Curve & some Pythagoras Trees">
<meta name="author" content="Alex Berke | aberke">
<link rel="shortcut icon" href="img/sierpinski-triangle.png" type="image/x-icon">
<meta property="og:image" content="https://aberke.github.io/fractals/img/pythagoras-tree-1.gif" />
<!-- vendor assets -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.2.7/raphael.min.js"></script>
<script type="text/javascript" src="js/util.js"></script>
<script type="text/javascript" src="js/pythagoras-tree.js"></script>
<script type="text/javascript" src="js/sierpinski-triangle.js"></script>
<script type="text/javascript" src="js/sierpinski-arrowhead-curve.js"></script>
<link rel="stylesheet" href="css/fractals.css">
<!-- Google fonts -->
<link href="https://fonts.googleapis.com/css?family=Raleway:400,400i,500,600,700,800" rel="stylesheet">
</head>
<body>
<div id="main-container">
<h1>Fractals</h1>
<div id="main-description" class="description">
<p>Fractals are patterns that are generated by repeating themselves.</p>
<br/>
<p>They are <a target="_blank" href="https://en.wikipedia.org/wiki/Recursion" class="bold">recursive</a>: <br/>They start with a simple rule that is replicated at every level of the fractal. <br/>Fractals can be infinitely complex, yet created from a simple rule.</p>
</div>
<section id="sierpinski-triangle" class="fractal-section">
<h2>Sierpiński Triangle</h2>
<div id="canvas-1" class="canvas"></div>
<button class="redraw-btn" id="sierpinski-triangle-redraw-btn">Redraw</button>
<p>The Sierpiński Triangle can be generated by following the rule that every triangle has a smaller triangle drawn on each of its sides.</p>
<p>At each level, triangles are drawn with half the side length of the triangles drawn at the previous level.</p>
<br/>
<a href="https://github.com/aberke/fractals/blob/master/js/sierpinski-triangle.js" class="code-link">See the code that made this.</a>
<br/>
<p>Another fractal that draws along the outline of the Sierpiński Triangle is the Sierpiński Arrowhead Curve</p>
</section>
<section id="sierpinski-arrowhead-curve" class="fractal-section">
<h2>Sierpiński Arrowhead Curve</h2>
<div id="canvas-2" class="canvas"></div>
<button class="redraw-btn" id="sierpinski-arrowhead-curve-redraw-btn">Redraw</button>
<p>The Sierpiński Arrowhead Curve is created by a series of 60° turns. At each level, a line that would be drawn is replaced by 3 lines at 120° angles to each other. Although these 3 lines turn, they end where the line they replaced would have ended. Each level is drawn with lines half the length of the previous level's lines.</p>
<br/>
<p>The curve can be drawn with 2 substitution rules:
<br/>
A → B-A-B
<br/>
B → A+B+A
<br/>
<br/>
A and B are functions that call each other recursively until the number of calls reaches the number of levels, at which point a line is drawn.
<br/>
+ means "turn left 60°", and - means "turn right 60°".
</p>
<br/>
<br/>
<p>The curve can be defined more formally with a <a class="bold" target="_blank" href="http://mathworld.wolfram.com/StringRewritingSystem.html">rewrite system</a>.</p>
<br/>
<a href="https://github.com/aberke/fractals/blob/master/js/sierpinski-arrowhead-curve.js" class="code-link">See the code that made this.</a>
</section>
<section class="fractal-section">
<h2>Pythagoras Tree</h2>
<div id="canvas-3" class="canvas">
<!-- Update the 'Level' number as the tree grows in animation -->
<p class="level-text-p">Level: <span class="level-text"></span></p>
<button class="redraw-btn">Redraw</button>
</div>
<p>The Pythagoras Tree is a fractal created by squares. Each level of squares has 2 smaller squares on the following level with which it encloses a 45°-45°-90° triangle. It is because of these right triangles that the fractal was named after Pythagoras.
<br/>
<br/>
The rule to generate each new level is to take each square at the preceding level, scale down its side length by ½√2 and create 2 new squares of that smaller size at its top left and right corners that meet at a 90° angle.
<br/>
Each level will then have 2 times as many squares as the previous level, and each level's squares will have side lengths that are ½√2 times the size of the previous level's.
<br/>
<br/>
<a href="https://github.com/aberke/fractals/blob/master/js/pythagoras-tree.js" class="code-link">See the code that made this.</a>
<br/>
Now consider a Pythagoras Tree where the lines that make up the squares of the tree are not lines, but replaced with other edges. These edges may curve, but they still end up at the same corners the square's lines would have ended.
<br/>
I drew some for you.
</p>
<div id="canvas-4" class="canvas">
<!-- Update the 'Level' number as the tree grows in animation -->
<p class="level-text-p">Level: <span class="level-text"></span></p>
<button class="redraw-btn">Redraw</button>
</div>
<p>
The rules that generated these trees were exactly the same, except instead of squares building the tree, other shapes built the tree.
</p>
<br/>
<p>
Another way to think of the tree is as an acyclic directed graph, where each square is a node with two child node squares that have side lengths of ½√2 its own side length.
</p>
</section>
</div>
<footer>
<p>Made with ♡ in NYC by <a href="http://aberke.com" target="_blank">Alex Berke</a></p>
<br/>
<a target="_blank" href="https://github.com/aberke/fractals">Check it out on Github</a>
<br/>
<br/>
<p>Why did you make this? <br/>I love fractals. I also love math and am working on <a target="_blank" href="http://www.beautifulsymmetry.onl">a coloring book</a> that teaches its audience <a target="_blank" href="https://en.wikipedia.org/wiki/Group_theory">group theory</a>, and needed to draw some fractals in javascript. It was harder than I thought, and led down a rabbit hole that seemed worthy of a webpage. I wanted to be able to share the javascript code I wrote so that others did not later have the same troubles as I.</p>
</footer>
<script>
'use strict';
// draw a row of triangles
// how many drawings in a row
var TRIANGLE_ROW_COUNT = 5;
var TRIANGLE_CANVAS_HEIGHT = 300; // deal with mobile case later
var paper1 = new Raphael("canvas-1", "100%", TRIANGLE_CANVAS_HEIGHT);
var paper2 = new Raphael("canvas-2", "100%", TRIANGLE_CANVAS_HEIGHT);
var maxLevel = 6;
var baseFractalFunction = sierpinskiTriangle.getSierpinskiTriangle;
// draw the Sierpinski Triangle with a 'redraw button'
var redrawSierpinskiTriangleFractals = function() {
paper1.clear();
var sierpinskiTriangleFractals = fractalsUtil.drawFractalRow(paper1, TRIANGLE_ROW_COUNT, maxLevel, sierpinskiTriangle.getSierpinskiTriangle, baseFractalFunction);
}
var redrawSierpinskiTriangleFractalsButton = document.getElementById("sierpinski-triangle-redraw-btn");
redrawSierpinskiTriangleFractalsButton.onclick = redrawSierpinskiTriangleFractals;
redrawSierpinskiTriangleFractals();
// draw the arrow head curves with a 'redraw' button
var redrawSierpinskiArrowheadCurveFractals = function() {
paper2.clear();
var sierpinskiArrowheadCurveFractals = fractalsUtil.drawFractalRow(paper2, TRIANGLE_ROW_COUNT, 4, sierpinskiArrowheadCurve.getSierpinskiArrowheadCurve, baseFractalFunction);
}
var redrawSierpinskiArrowheadCurveFractalsButton = document.getElementById("sierpinski-arrowhead-curve-redraw-btn");
redrawSierpinskiArrowheadCurveFractalsButton.onclick = redrawSierpinskiArrowheadCurveFractals;
redrawSierpinskiArrowheadCurveFractals();
// draw one Pythagoras Tree
function handlePythagorasCanvas(containerElt, edgePathFunction) {
var levelTextElt = containerElt.getElementsByClassName("level-text")[0];
var redrawButtonElt = containerElt.getElementsByClassName("redraw-btn")[0];
var paper = new Raphael(containerElt, "100%", 400);
var paperWidth = paper.getSize().width;
var paperHeight = paper.getSize().height;
var maxLevel = 7;
var sideLength = (5/3)*Math.min(paperWidth, paperHeight)/maxLevel;
var centerPoint = {X: paperWidth/2, Y: paperHeight - (1/2)*sideLength};
var pythagorasTreePathList = pythagorasTree.getPythagorasTree(centerPoint, sideLength, {
levels: maxLevel,
edgePathFunction: edgePathFunction,
});
// Update inner text of this element as the level/level being drawn grows
var currentDrawLevel = 0;
function drawPythagorasTreeCallback(level) {
if (currentDrawLevel < level) {
currentDrawLevel = level;
levelTextElt.innerText = String(currentDrawLevel);
}
}
// set up button for redrawing
function redrawPythagorasTree() {
paper.clear();
currentDrawLevel = 0;
pythagorasTree.drawPythagorasTree(paper, pythagorasTreePathList, 1000, drawPythagorasTreeCallback);
}
redrawButtonElt.onclick = redrawPythagorasTree;
// draw the tree for the first time
redrawPythagorasTree();
}
var containerElt3 = document.getElementById("canvas-3");
handlePythagorasCanvas(containerElt3);
// Draw a row of Pythagoras Trees
function drawPythagorasTreeMultiple(paper, edgePathFunctions, drawCallback) {
// draw trees of this level
var maxLevel = 7;
var paperWidth = paper.getSize().width;
var paperHeight = paper.getSize().height;
var treeCount = edgePathFunctions.length;
var treeSize = paperWidth/treeCount;
var sideLength = treeSize/6;
for (var i=0; i < edgePathFunctions.length; i++) {
var centerPointX = (1/2)*treeSize + i*treeSize;
var centerPointY = paperHeight - (1/2)*sideLength;
var centerPoint = {X: centerPointX, Y: centerPointY};
var pythagorasTreePathList = pythagorasTree.getPythagorasTree(centerPoint, sideLength, {
levels: maxLevel,
edgePathFunction: edgePathFunctions[i],
});
pythagorasTree.drawPythagorasTree(paper, pythagorasTreePathList, 1000, drawCallback);
}
}
var containerElt4 = document.getElementById("canvas-4");
var levelTextElt4 = containerElt4.getElementsByClassName("level-text")[0];
var redrawButtonElt4 = containerElt4.getElementsByClassName("redraw-btn")[0];
var paper4 = new Raphael(containerElt4, "100%", 250);
function redrawPythagorasTreeMultiple() {
paper4.clear();
var randomMultiplier = Math.random();
var randomEdgePathFunction = function(fromPoint, toPoint, centerPoint) {
return pythagorasTree.getCatmullRomPath(fromPoint, toPoint, centerPoint, randomMultiplier);
};
var edgePathFunctions = [pythagorasTree.getEllipsePath, pythagorasTree.getCurvedPath, randomEdgePathFunction];
// update level text element as the level increments
var currentDrawLevel = 0;
function drawPythagorasTreeCallback(level) {
if (currentDrawLevel < level) {
currentDrawLevel = level;
levelTextElt4.innerText = String(currentDrawLevel);
}
}
drawPythagorasTreeMultiple(paper4, edgePathFunctions, drawPythagorasTreeCallback);
}
redrawButtonElt4.onclick = redrawPythagorasTreeMultiple;
redrawPythagorasTreeMultiple();
</script>
<!-- Google analytics tracking code -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-43101146-5', 'auto');
ga('send', 'pageview');
</script>
</body>