*** empty log message ***
[lsystem3d.git] / src / lindenmayersystem.cpp
... / ...
CommitLineData
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 */
37LindenmayerSystem::LindenmayerSystem(Model *model)
38{
39 _model = model;
40 _turtle = new Turtle(model);
41
42 clear();
43}
44
45
46
47/**
48 * Destructor
49 */
50LindenmayerSystem::~LindenmayerSystem()
51{
52 delete _turtle;
53}
54
55
56
57/**
58 * Clear all parameters
59 */
60void 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 */
76void 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 */
91void 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 */
103void 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 */
114void 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 */
128void LindenmayerSystem::setNumIterations(int numIterations)
129{
130 _numIterations = numIterations;
131}
132
133
134
135/**
136 * Set diameter of segment
137 * @param diameter the diameter
138 */
139void 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 */
150void 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 */
161string LindenmayerSystem::getAxiom()
162{
163 return unpreprocessRule(_axiom);
164}
165
166
167
168/**
169 * Get all rules
170 * @return the rules
171 */
172map<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 */
192double 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 */
208int LindenmayerSystem::getNumIterations()
209{
210 return _numIterations;
211}
212
213
214
215/**
216 * Get the generated model
217 * @return generated model
218 */
219Model *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 */
231void 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 */
385string 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 */
413string 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}