*** empty log message ***
[lsystem3d.git] / src / lindenmayersystem.cpp
CommitLineData
15c82487 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>
15c82487 25#include <string>
26
27#include "lindenmayersystem.h"
28#include "model.h"
29#include "turtle.h"
30
15c82487 31
32
33/**
34 * Constructor
526db675 35 * @param model empty model
15c82487 36 */
37LindenmayerSystem::LindenmayerSystem(Model *model)
38{
39 _model = model;
40 _turtle = new Turtle(model);
41
526db675 42 clear();
15c82487 43}
44
45
46
47/**
48 * Destructor
49 */
50LindenmayerSystem::~LindenmayerSystem()
51{
52 delete _turtle;
53}
54
55
56
526db675 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
15c82487 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
526db675 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
15c82487 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/**
526db675 216 * Get the generated model
217 * @return generated model
15c82487 218 */
526db675 219Model *LindenmayerSystem::getModel()
15c82487 220{
526db675 221 return _model;
15c82487 222}
223
224
225
226/**
526db675 227 * Recursively apply the replacement rules
228 * @param rule rule
229 * @param level recursion level
15c82487 230 */
231void LindenmayerSystem::recursiveWalk(string rule, int level)
232{
526db675 233 // process every element in the rule string
15c82487 234 for (int i = 0; i < rule.size(); i++)
235 {
236 switch (rule[i])
237 {
526db675 238 // rule
15c82487 239 case '@':
526db675 240
15c82487 241 // ignore marker, i.e. the "@"
242 i++;
243
244 // recursion
526db675 245 if (level > 0)
15c82487 246 {
526db675 247 char ruleName[] = {rule[i], '\0'};
248 recursiveWalk(_rules[ruleName], level - 1);
249 }
15c82487 250
526db675 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);
15c82487 261 }
262 else
263 {
526db675 264 // no rule for "F"
265 _turtle->walk();
15c82487 266 }
526db675 267
15c82487 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
526db675 334 // create a filled surface
15c82487 335 case '{':
336
526db675 337 _model->fillBegin();
15c82487 338 break;
339
340
526db675 341 // close a filled surface
15c82487 342 case '}':
343
526db675 344 _model->fillEnd();
15c82487 345 break;
346
347
348 // one vertex in a filled surface
349 case 'f':
350
526db675 351 _turtle->fillWalk();
15c82487 352 break;
353
354
355 // decrement diameter of segment
356 case '!':
357
526db675 358 _model->decrementDiameter();
15c82487 359 break;
360
361
526db675 362 // increment current index to color table
363 case '\'':
15c82487 364
526db675 365 _model->nextColor();
366 break;
367
368
369 // decrement current index to color table
e017b5ae 370 case ',':
526db675 371
372 _model->prevColor();
15c82487 373 break;
374 }
375 }
376}
377
378
379
380/**
381 * Verify and preprocess one rule string
526db675 382 * @param rule the rule
15c82487 383 * @return the preprocessed rule
384 */
526db675 385string LindenmayerSystem::verifyAndPreprocessRule(string rule)
15c82487 386{
15c82487 387 // TODO: verifying
388
389 string preprocessedRule;
390
391 // check every element in rule
526db675 392 for (int i = 0; i < rule.size(); i++)
15c82487 393 {
526db675 394 // TODO: allow strings as names
395 if (rule[i] >= 'A' && rule[i] <= 'Z' && rule[i] != 'F')
15c82487 396 {
526db675 397 // add rule operator
15c82487 398 preprocessedRule += '@';
399 }
526db675 400 preprocessedRule += rule[i];
15c82487 401 }
402
403 return preprocessedRule;
404}
405
406
407
408/**
409 * Unpreprocess one rule string
526db675 410 * @param rule the rule
15c82487 411 * @return the unpreprocessed rule
412 */
526db675 413string LindenmayerSystem::unpreprocessRule(string rule)
15c82487 414{
415 string unpreprocessedRule;
416
417 // check every element in rule
526db675 418 for (int i = 0; i < rule.size(); i++)
15c82487 419 {
526db675 420 // ignore rule operator
421 if (rule[i] != '@')
15c82487 422 {
526db675 423 unpreprocessedRule += rule[i];
15c82487 424 }
425 }
426
427 return unpreprocessedRule;
428}