-
Notifications
You must be signed in to change notification settings - Fork 0
/
lsystem4.js
161 lines (139 loc) · 4.72 KB
/
lsystem4.js
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
/* By Dave Lawrence on 11/10/2011
* LSystem 4
* http://proceduralgraphics.blogspost.com
*/
var lsysTypes = {
'Plant with leaves': { axiom: 'S--X', angle: 0.436, rules: ['L->', 'X->FL-[[X]+X]+FL[+FLX]-X', 'F->FF'],
funcs: {
// Setup (from axiom, will just sit as the first letter)
'S': function(args) {
args.ctx.lineWidth = 2;
args.ctx.strokeStyle = '#644a35';
},
// Leaves
'L': function(args) {
args.ctx.fillStyle = '#00ff00';
args.ctx.beginPath();
args.ctx.arc(0, 0, 2, 0, 2 * Math.PI, true);
args.ctx.fill();
}
}
},
'Plant': { axiom: '--X', angle: 0.436, rules: ['X->F-[[X]+X]+F[+FX]-X', 'F->FF']},
'Koch Snowflake': { axiom: 'F--F--F', angle: Math.PI / 3, rules: ['F->F+F--F+F']},
'Serpinski Carpet': { axiom: 'F-F-F-F', angle: Math.PI / 2, rules: ['F->F[F]-F+F[--F]+F-F']},
'Serpinski Carpet (color)': { axiom: 'F-F-F-F', angle: Math.PI / 2, rules: ['F->F[CF]-F+F[C--F]+F-F'],
// A 'C' is put inside each push '[' so color is changed according to depth
funcs: {
'C': function(args) {
var colors = ['#FFFFFF', '#39de60', '#fffc22', '#ff0000', '#0000FF'];
args.ctx.strokeStyle = colors[ Math.min(colors.length-1, args.depth) ];
}
}
},
'Serpinski Square': { axiom: 'F-F-F-F', angle: Math.PI / 2, rules: ['F->FF[-F-F-F]F'] },
'Serpinski Triangle': { axiom: 'A', angle: Math.PI / 3, rules: ['A->B-A-B', 'B->A+B+A']},
'Serpinski Gasket': { axiom: 'F--F--F', angle: Math.PI / 3, rules: ['F->F--F--F--GG', 'G->GG'] }
};
// All of the interesting l-sys drawing code is in lsystem.js, the code below is mostly boring HTML stuff
var viewPortWidth = window.innerWidth - 40;
var viewPortHeight = window.innerHeight * .7;
var width = 4000;
var height = 3000;
var DEFAULT_ITERATIONS = 4;
var ctx;
var selectLsys;
var input = {}; // convenience map that holds inputs
var zoom = 1;
var zoom_increment = 0.2;
function setup() {
var canvas = newCanvas(width, height);
document.getElementById("canvas").appendChild(canvas);
ctx = canvas.getContext("2d");
input = {
axiom: document.getElementById('axiom'),
rules: document.getElementById('rules'),
angle: document.getElementById('angle'),
iterations: document.getElementById('iterations')
};
selectLsys = document.getElementById('l-sys');
selectLsys.options.length = 0; // clear out existing items
for (var key in lsysTypes) {
selectLsys.options.add(new Option(key, key))
}
selectLsys.options[0].selected = true;
selectLsys.onchange = updateSelect;
input.iterations.value = DEFAULT_ITERATIONS;
document.getElementById('zoom_out').onclick = function() { zoom -= zoom_increment; update() };
document.getElementById('zoom_in').onclick = zoomIn;
document.getElementById('inc').onclick = function() { input.iterations.value++; };
document.getElementById('dec').onclick = function() { input.iterations.value--; };
document.getElementById('update').onclick = update;
initMouseGrabScrolling();
updateSelect();
}
function zoomIn() {
zoom += zoom_increment;
update();
}
function initMouseGrabScrolling() {
var overscroll = document.getElementById("overscroll");
overscroll.style.overflow = 'hidden';
overscroll.style.width = viewPortWidth + "px";
overscroll.style.height = viewPortHeight + "px";
$(function(o){
o = $("#overscroll").overscroll({
cancelOn: '.no-drag',
showThumbs: true
});
o.dblclick(zoomIn);
});
}
function updateSelect() {
var chosenOption= selectLsys.options[selectLsys.selectedIndex];
var l = lsysTypes[chosenOption.value];
input.axiom.value = l.axiom;
input.rules.value = l.rules.join(',');
input.angle.value = l.angle;
update();
}
function update() {
var l = {};
l.axiom = input.axiom.value;
l.rules = input.rules.value.split(',');
l.angle = input.angle.value;
// TODO: Make funcs editable
var chosenOption= selectLsys.options[selectLsys.selectedIndex];
l.funcs = lsysTypes[chosenOption.value].funcs;
draw(l);
}
function draw(l) {
ctx.save();
drawBackground();
ctx.scale(zoom, zoom);
ctx.translate(width/10, height/10); // hack margin, until I make proper alignment/zooming etc
ctx.strokeStyle = "#FFFFFF";
var lsys = new LSystem(l.axiom, l.rules);
var iterations = input.iterations.value;
var turtleInput = lsys.iterate(iterations);
var args = {};
args.ctx = ctx;
args.str = turtleInput;
args.distance = 5;
args.angle = l.angle;
args.angleVariance = 0.0;
args.iterations = iterations;
var handler = getDefaultLSystemHandler();
// Add extra drawing funcs
if (l.funcs) {
for (var f in l.funcs) {
handler[f] = l.funcs[f];
}
}
drawLSystem(args, handler);
ctx.restore();
}
function drawBackground() {
ctx.fillStyle = "#000000";
ctx.fillRect(0,0, width, height);
}