-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlsystem.html
203 lines (175 loc) · 5.54 KB
/
lsystem.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
<!DOCTYPE html >
<!--
By Dave Lawrence on 24 Nov 2009
L-System demo
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style>
html,body { width:100%; height:100%; margin:0; padding:20px; overflow:hidden; }
th { font-weight: bold; }
td, td { border: 1px solid black; padding: 5px; }
div#instructions { margin-top: 20px; }
div.heading { font-weight: bold; };
</style>
<title>L-System demo</title>
<script type="text/javascript">
var lsysTypes = [
{ text: 'Koch Snowflake', axiom: 'F--F--F', angle: Math.PI / 3, rules: new Array('F->F+F--F+F') },
{ text: 'Serpinski Carpet', axiom: 'F-F-F-F', angle: Math.PI / 2, rules: new Array('F->F[F]-F+F[--F]+F-F') },
{ text: 'Serpinski Triangle', axiom: 'A', angle: Math.PI / 3, rules: new Array('A->B-A-B', 'B->A+B+A') },
{ text: 'Tree', axiom: 'F', angle: Math.PI / 10,rules: new Array('F->|[3-F][3+F]|[--F][++F]|F') },
{ text: 'Plant', axiom: 'X', angle: 0.436, rules: new Array('X->F-[[X]+X]+F[+FX]-X)', 'F->FF') }
];
var width = window.innerWidth / 2;
var height = window.innerHeight / 2;
var ctx;
var selectInput;
var iterationsInput;
var output;
// write out canvas as we have to put width/height inline in the HTML, CSS will just scale pixels
document.write('<canvas id="canvas" width=' + width + ' height=' + height + '></canvas>');
function setup() {
ctx = document.getElementById("canvas").getContext("2d");
output = document.getElementById("output");
output.style.width = width + "px";
output.style.height = height / 2 + "px";
selectInput = document.getElementById('l-sys');
selectInput.options.length = 0; // clear out existing items
for (var key in lsysTypes) {
selectInput.options.add(new Option(lsysTypes[key].text, key))
}
selectInput.options[0].selected = true;
selectInput.onchange = update;
iterationsInput = document.getElementById("iterations");
iterationsInput.onchange = update;
update();
}
function update() {
var chosenoption= selectInput.options[selectInput.selectedIndex];
// populate table data
var l = lsysTypes[chosenoption.value];
var axiom = document.getElementById('axiom');
axiom.innerHTML = l.axiom;
var rule = document.getElementById('rule');
var rulesTxt = "";
for (var i=0 ; i<l.rules.length ; ++i) {
rulesTxt += l.rules[i];
if (i < l.rules.length - 1) {
rulesTxt += ", ";
}
}
rule.innerHTML = rulesTxt;
draw(l);
}
function draw(l) {
ctx.save();
drawBackground();
ctx.translate(width/10, height/10); // margins
ctx.strokeStyle = "#FFFFFF";
var lsys = new LSystem(l.axiom, l.rules);
var iterations = iterationsInput.value;
var value = lsys.iterate(iterations);
msg(value);
drawLSystem(value, 5, l.angle);
ctx.restore();
}
function drawBackground() {
ctx.fillStyle = "#000000";
ctx.fillRect(0,0, width, height);
}
function LSystem(axiom, rules) {
this.axiom = axiom;
this.ruleMap = [];
for (var i=0 ; i<rules.length ; ++i) {
var s = rules[i].split("->");
this.ruleMap[s[0]] = s[1];
}
this.iterate = function(times) {
if (!times) times = 1;
var value = axiom;
for (var i=0 ; i<times ; ++i) {
var array = stringToArray(value);
for (var key in this.ruleMap) {
// note - a regex won't work, as we don't want to alter values changed this turn.
//value = value.replace( new RegExp(key, "g"), this.ruleMap[key]);
for (var c=0 ; c<array.length ; ++c) {
var newValue = this.ruleMap[ array[c] ];
if (newValue) {
array[c] = newValue;
}
}
}
value = array.join("");
}
return value;
}
}
function stringToArray(s) {
var array = [];
for (var i=0 ; i<s.length ; ++i) {
array[i] = s.charAt(i);
}
return array;
}
function drawLSystem(str, distance, angle) {
var depth = 0;
for(var i=0 ; i<str.length ; ++i) {
var dist = distance;
var c = str.charAt(i);
switch (c) {
case '+': // turn to right
ctx.rotate(angle);
break;
case '-': // turn to left
ctx.rotate(-angle);
break;
case '[': // push state
ctx.save();
++depth;
break;
case ']': // pop state
ctx.restore();
--depth;
break;
case '|': // Draw forward by length computed by recursion depth
dist /= Math.pow(2.0, depth);
// fallthrough
case 'F': // Draw forward
case 'A':
case 'B':
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, dist);
ctx.stroke();
// fallthrough to also perform translation
case 'G': // Go forward (without drawing)
ctx.translate(0, dist);
break;
}
}
}
function msg(s) {
output.innerHTML = s;
}
</script>
</head>
<body onload="setup()">
<div id="info">
<table>
<tr><th>Name</th><th>Axiom</th><th>Rule</th><th>Iterations</th></tr>
<tr>
<td><select id="l-sys"></select></td>
<td><div id="axiom"></div></td>
<td><div id="rule"></div></td>
<td><input id="iterations" value="3" /></td>
</tr>
</table>
</div>
<div>
<div class="heading">Generated value:</div>
<textarea id="output">
</textarea>
</div>
</body>
</html>