*** empty log message ***
[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 <map>
25 #include <string>
26
27 #include "lindenmayersystem.h"
28 #include "model.h"
29 #include "turtle.h"
30
31
32
33 /**
34 * Constructor
35 * @param model empty model
36 */
37 LindenmayerSystem::LindenmayerSystem(Model *model)
38 {
39 _model = model;
40 _turtle = new Turtle(model);
41
42 clear();
43 }
44
45
46
47 /**
48 * Destructor
49 */
50 LindenmayerSystem::~LindenmayerSystem()
51 {
52 delete _turtle;
53 }
54
55
56
57 /**
58 * Clear all parameters
59 */
60 void LindenmayerSystem::clear()
61 {
62 _axiom.clear();
63 _rules.clear();
64 _turtle->setAngle(0.0);
65 _numIterations = 0;
66
67 _turtle->reset();
68 _model->clear();
69 }
70
71
72
73 /**
74 * Generate L-system data
75 */
76 void LindenmayerSystem::generate()
77 {
78 // model session
79 _model->begin();
80 _model->setColorIndex(0);
81 recursiveWalk(_axiom, _numIterations);
82 _model->end();
83 }
84
85
86
87 /**
88 * Set the initial rule (the axiom)
89 * @param axiom the axiom
90 */
91 void LindenmayerSystem::setAxiom(string axiom)
92 {
93 _axiom = verifyAndPreprocessRule(axiom);
94 }
95
96
97
98 /**
99 * Set one rule
100 * @param name rule name
101 * @param rule the rule
102 */
103 void LindenmayerSystem::setRule(string name, string rule)
104 {
105 _rules[name] = verifyAndPreprocessRule(rule);
106 }
107
108
109
110 /**
111 * Set the turn/pitch/roll angle
112 * @param degrees the angle, in degrees
113 */
114 void LindenmayerSystem::setAngle(double degrees)
115 {
116 // convert from degrees to radians
117 double radians = (degrees / 360.0) * (2.0 * M_PIl);
118
119 _turtle->setAngle(radians);
120 }
121
122
123
124 /**
125 * Set the number of iterations
126 * @param numIterations number of iterations
127 */
128 void LindenmayerSystem::setNumIterations(int numIterations)
129 {
130 _numIterations = numIterations;
131 }
132
133
134
135 /**
136 * Set diameter of segment
137 * @param diameter the diameter
138 */
139 void LindenmayerSystem::setDiameter(double diameter)
140 {
141 _model->setDiameter(diameter);
142 }
143
144
145
146 /**
147 * Set diameter factor
148 * @param diameterFactor the diameter factor
149 */
150 void LindenmayerSystem::setDiameterFactor(double diameterFactor)
151 {
152 _model->setDiameterFactor(diameterFactor);
153 }
154
155
156
157 /**
158 * Get the initial rule (the axiom)
159 * @return the axiom
160 */
161 string LindenmayerSystem::getAxiom()
162 {
163 return unpreprocessRule(_axiom);
164 }
165
166
167
168 /**
169 * Get all rules
170 * @return the rules
171 */
172 map<string,string> LindenmayerSystem::getRules()
173 {
174 map<string,string> theUnpreprocessedRules;
175
176 // unpreprocess all rules
177 map<string,string>::iterator currentRule;
178 for (currentRule = _rules.begin(); currentRule != _rules.end(); currentRule++)
179 {
180 theUnpreprocessedRules[currentRule->first] = unpreprocessRule(currentRule->second);
181 }
182
183 return theUnpreprocessedRules;
184 }
185
186
187
188 /**
189 * Get the turn/pitch/roll angle
190 * @return the angle, in degrees
191 */
192 double LindenmayerSystem::getAngle()
193 {
194 double radians = _turtle->getAngle();
195
196 // convert from radians to degrees
197 double degrees = 360.0 * (radians / (2.0 * M_PIl));
198
199 return degrees;
200 }
201
202
203
204 /**
205 * Get the number of iterations
206 * @return number of iterations
207 */
208 int LindenmayerSystem::getNumIterations()
209 {
210 return _numIterations;
211 }
212
213
214
215 /**
216 * Get the generated model
217 * @return generated model
218 */
219 Model *LindenmayerSystem::getModel()
220 {
221 return _model;
222 }
223
224
225
226 /**
227 * Recursively apply the replacement rules
228 * @param rule rule
229 * @param level recursion level
230 */
231 void LindenmayerSystem::recursiveWalk(string rule, int level)
232 {
233 // process every element in the rule string
234 for (int i = 0; i < rule.size(); i++)
235 {
236 switch (rule[i])
237 {
238 // rule
239 case '@':
240
241 // ignore marker, i.e. the "@"
242 i++;
243
244 // recursion
245 if (level > 0)
246 {
247 char ruleName[] = {rule[i], '\0'};
248 recursiveWalk(_rules[ruleName], level - 1);
249 }
250
251 break;
252
253
254 // walk / rule
255 case 'F':
256
257 // replacement rule for 'F'?
258 if (_rules.count("F") != 0 && level > 0)
259 {
260 recursiveWalk(_rules["F"], level - 1);
261 }
262 else
263 {
264 // no rule for "F"
265 _turtle->walk();
266 }
267
268 break;
269
270
271 // turn left
272 case '+':
273
274 _turtle->turnLeft();
275 break;
276
277
278 // turn right
279 case '-':
280
281 _turtle->turnRight();
282 break;
283
284
285 // pitch down
286 case '&':
287
288 _turtle->pitchDown();
289 break;
290
291
292 // pitch up
293 case '^':
294
295 _turtle->pitchUp();
296 break;
297
298
299 // roll left
300 case '\\':
301
302 _turtle->rollLeft();
303 break;
304
305
306 // roll right
307 case '/':
308
309 _turtle->rollRight();
310 break;
311
312
313 // turn around 180 degrees
314 case '|':
315
316 _turtle->turnAround();
317 break;
318
319
320 // save state to stack
321 case '[':
322
323 _turtle->push();
324 break;
325
326
327 // restore state from stack
328 case ']':
329
330 _turtle->pop();
331 break;
332
333
334 // create a filled surface
335 case '{':
336
337 _model->fillBegin();
338 break;
339
340
341 // close a filled surface
342 case '}':
343
344 _model->fillEnd();
345 break;
346
347
348 // one vertex in a filled surface
349 case 'f':
350
351 _turtle->fillWalk();
352 break;
353
354
355 // decrement diameter of segment
356 case '!':
357
358 _model->decrementDiameter();
359 break;
360
361
362 // increment current index to color table
363 case '\'':
364
365 _model->nextColor();
366 break;
367
368
369 // decrement current index to color table
370 case ',':
371
372 _model->prevColor();
373 break;
374 }
375 }
376 }
377
378
379
380 /**
381 * Verify and preprocess one rule string
382 * @param rule the rule
383 * @return the preprocessed rule
384 */
385 string LindenmayerSystem::verifyAndPreprocessRule(string rule)
386 {
387 // TODO: verifying
388
389 string preprocessedRule;
390
391 // check every element in rule
392 for (int i = 0; i < rule.size(); i++)
393 {
394 // TODO: allow strings as names
395 if (rule[i] >= 'A' && rule[i] <= 'Z' && rule[i] != 'F')
396 {
397 // add rule operator
398 preprocessedRule += '@';
399 }
400 preprocessedRule += rule[i];
401 }
402
403 return preprocessedRule;
404 }
405
406
407
408 /**
409 * Unpreprocess one rule string
410 * @param rule the rule
411 * @return the unpreprocessed rule
412 */
413 string LindenmayerSystem::unpreprocessRule(string rule)
414 {
415 string unpreprocessedRule;
416
417 // check every element in rule
418 for (int i = 0; i < rule.size(); i++)
419 {
420 // ignore rule operator
421 if (rule[i] != '@')
422 {
423 unpreprocessedRule += rule[i];
424 }
425 }
426
427 return unpreprocessedRule;
428 }