* Adaptation to new classes Rule, RuleSet
[lsystem3d.git] / src / lindenmayersystem.cpp
1 // Copyright (C) 2006 Erik Dahlberg
2 //
3 // This file is part of LSystem3D.
4 //
5 // LSystem3D is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // LSystem3D is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with LSystem3D; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
20
21
22 #include <cmath>
23
24 #include <string>
25
26 #include "lindenmayersystem.h"
27 #include "model.h"
28 #include "rule.h"
29 #include "ruleset.h"
30 #include "turtle.h"
31
32 using namespace std;
33
34
35
36 /**
37 * Constructor
38 * @param model empty model
39 */
40 LindenmayerSystem::LindenmayerSystem(Model *model)
41 {
42 _model = model;
43 _turtle = new Turtle(model);
44
45 clear();
46 }
47
48
49
50 /**
51 * Destructor
52 */
53 LindenmayerSystem::~LindenmayerSystem()
54 {
55 delete _turtle;
56 }
57
58
59
60 /**
61 * Clear all parameters
62 */
63 void LindenmayerSystem::clear()
64 {
65 _axiom.clear();
66 _rules.clear();
67 _turtle->setAngle(0.0);
68 _depth = 0;
69
70 _turtle->reset();
71 _model->clear();
72 }
73
74
75
76 /**
77 * Generate L-system data
78 */
79 void LindenmayerSystem::generate()
80 {
81 // model session
82 _model->begin();
83 _model->setColorIndex(0);
84 recursiveWalk(_axiom.getPreprocessedContent(), _depth);
85 _model->end();
86 }
87
88
89
90 /**
91 * Set the initial rule (the axiom)
92 * @param axiom the axiom
93 */
94 void LindenmayerSystem::setAxiom(Rule axiom)
95 {
96 _axiom = axiom;
97 }
98
99
100
101 /**
102 * Set one rule
103 * @param rule the rule
104 */
105 void LindenmayerSystem::setRule(Rule rule)
106 {
107 _rules.addRule(rule);
108 }
109
110
111
112 /**
113 * Set the turn/pitch/roll angle
114 * @param degrees the angle, in degrees
115 */
116 void LindenmayerSystem::setAngle(double degrees)
117 {
118 // convert from degrees to radians
119 double radians = (degrees / 360.0) * (2.0 * M_PIl);
120
121 _turtle->setAngle(radians);
122 }
123
124
125
126 /**
127 * Set depth of recursion
128 * @param depth depth of recursion
129 */
130 void LindenmayerSystem::setDepth(int depth)
131 {
132 _depth = depth;
133 }
134
135
136
137 /**
138 * Set diameter of segment
139 * @param diameter the diameter
140 */
141 void LindenmayerSystem::setDiameter(double diameter)
142 {
143 _model->setDiameter(diameter);
144 }
145
146
147
148 /**
149 * Set diameter factor
150 * @param diameterFactor the diameter factor
151 */
152 void LindenmayerSystem::setDiameterFactor(double diameterFactor)
153 {
154 _model->setDiameterFactor(diameterFactor);
155 }
156
157
158
159 /**
160 * Get the initial rule (the axiom)
161 * @return the axiom
162 */
163 Rule LindenmayerSystem::getAxiom()
164 {
165 return _axiom;
166 }
167
168
169
170 /**
171 * Get all rules
172 * @return the rules
173 */
174 RuleSet LindenmayerSystem::getRules()
175 {
176 return _rules;
177 }
178
179
180
181 /**
182 * Get the turn/pitch/roll angle
183 * @return the angle, in degrees
184 */
185 double LindenmayerSystem::getAngle()
186 {
187 double radians = _turtle->getAngle();
188
189 // convert from radians to degrees
190 double degrees = 360.0 * (radians / (2.0 * M_PIl));
191
192 return degrees;
193 }
194
195
196
197 /**
198 * Get depth of recursion
199 * @return depth of recursion
200 */
201 int LindenmayerSystem::getDepth()
202 {
203 return _depth;
204 }
205
206
207
208 /**
209 * Get the generated model
210 * @return generated model
211 */
212 Model *LindenmayerSystem::getModel()
213 {
214 return _model;
215 }
216
217
218
219 /**
220 * Recursively apply the replacement rules
221 * @param rule rule
222 * @param level recursion level
223 */
224 void LindenmayerSystem::recursiveWalk(string rule, int level)
225 {
226 // process every element in the rule string
227 for (int i = 0; i < rule.size(); i++)
228 {
229 switch (rule[i])
230 {
231 // rule
232 case '@':
233
234 // ignore marker, i.e. the "@"
235 i++;
236
237 // recursion
238 if (level > 0)
239 {
240 string name = rule.substr(i, 1);
241 recursiveWalk(_rules.findRule(name).getPreprocessedContent(), level - 1);
242 }
243
244 break;
245
246
247 // walk / rule
248 case 'F':
249
250 {
251 // replacement rule for 'F'?
252 Rule rule = _rules.findRule("F");
253 if (rule.getProbability() != 0.0 && level > 0)
254 {
255 recursiveWalk(rule.getPreprocessedContent(), level - 1);
256 }
257 else
258 {
259 // no rule for "F"
260 _turtle->walk();
261 }
262 }
263
264 break;
265
266
267 // turn left
268 case '+':
269
270 _turtle->turnLeft();
271 break;
272
273
274 // turn right
275 case '-':
276
277 _turtle->turnRight();
278 break;
279
280
281 // pitch down
282 case '&':
283
284 _turtle->pitchDown();
285 break;
286
287
288 // pitch up
289 case '^':
290
291 _turtle->pitchUp();
292 break;
293
294
295 // roll left
296 case '\\':
297
298 _turtle->rollLeft();
299 break;
300
301
302 // roll right
303 case '/':
304
305 _turtle->rollRight();
306 break;
307
308
309 // turn around 180 degrees
310 case '|':
311
312 _turtle->turnAround();
313 break;
314
315
316 // save state to stack
317 case '[':
318
319 _turtle->push();
320 break;
321
322
323 // load state from stack
324 case ']':
325
326 _turtle->pop();
327 break;
328
329
330 // create a filled surface
331 case '{':
332
333 _model->fillBegin();
334 break;
335
336
337 // close a filled surface
338 case '}':
339
340 _model->fillEnd();
341 break;
342
343
344 // one vertex in a filled surface
345 case 'f':
346
347 _turtle->fillWalk();
348 break;
349
350
351 // decrement diameter of segment
352 case '!':
353
354 _model->decrementDiameter();
355 break;
356
357
358 // increment current index to color table
359 case '\'':
360
361 _model->nextColor();
362 break;
363
364
365 // decrement current index to color table
366 case ',':
367
368 _model->prevColor();
369 break;
370 }
371 }
372 }