Initial revision
[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 <iostream>
26 #include <string>
27
28 #include "lindenmayersystem.h"
29 #include "model.h"
30 #include "turtle.h"
31
32 using namespace std;
33
34
35
36 /**
37 * Constructor
38 * @param model the model for generation
39 */
40 LindenmayerSystem::LindenmayerSystem(Model *model)
41 {
42 _model = model;
43 _turtle = new Turtle(model);
44
45 _axiom.clear();
46 _rules.clear();
47 _numIterations = 0;
48 }
49
50
51
52 /**
53 * Destructor
54 */
55 LindenmayerSystem::~LindenmayerSystem()
56 {
57 delete _turtle;
58 }
59
60
61
62 /**
63 * Set the initial rule (the axiom)
64 * @param axiom the axiom
65 */
66 void LindenmayerSystem::setAxiom(string axiom)
67 {
68 _axiom = verifyAndPreprocessRule(axiom);
69 }
70
71
72
73 /**
74 * Set one rule
75 * @param name rule name
76 * @param rule the rule
77 */
78 void LindenmayerSystem::setRule(string name, string rule)
79 {
80 _rules[name] = verifyAndPreprocessRule(rule);
81 }
82
83
84
85 /**
86 * Set the turn/pitch/roll angle
87 * @param degrees the angle, in degrees
88 */
89 void LindenmayerSystem::setAngle(double degrees)
90 {
91 // convert from degrees to radians
92 double radians = (degrees / 360.0) * (2.0 * M_PIl);
93
94 _turtle->setAngle(radians);
95 }
96
97
98
99 /**
100 * Set the number of iterations
101 * @param numIterations number of iterations
102 */
103 void LindenmayerSystem::setNumIterations(int numIterations)
104 {
105 _numIterations = numIterations;
106 }
107
108
109
110 /**
111 * Get the initial rule (the axiom)
112 * @return the axiom
113 */
114 string LindenmayerSystem::getAxiom()
115 {
116 return unpreprocessRule(_axiom);
117 }
118
119
120
121 /**
122 * Get all rules
123 * @return the rules
124 */
125 map<string,string> LindenmayerSystem::getRules()
126 {
127 map<string,string> theUnpreprocessedRules;
128
129 // unpreprocess all rules
130 map<string,string>::iterator currentRule;
131 for (currentRule = _rules.begin(); currentRule != _rules.end(); currentRule++)
132 {
133 theUnpreprocessedRules[currentRule->first] = unpreprocessRule(currentRule->second);
134 }
135
136 return theUnpreprocessedRules;
137 }
138
139
140
141 /**
142 * Get the turn/pitch/roll angle
143 * @return the angle, in degrees
144 */
145 double LindenmayerSystem::getAngle()
146 {
147 double radians = _turtle->getAngle();
148
149 // convert from radians to degrees
150 double degrees = 360.0 * (radians / (2.0 * M_PIl));
151
152 return degrees;
153 }
154
155
156
157 /**
158 * Get the number of iterations
159 * @return number of iterations
160 */
161 int LindenmayerSystem::getNumIterations()
162 {
163 return _numIterations;
164 }
165
166
167
168 /**
169 * Generate l-system data
170 */
171 void LindenmayerSystem::generate()
172 {
173 _model->clear();
174
175 recursiveWalk(_axiom, _numIterations);
176
177 _turtle->reset();
178 }
179
180
181
182 /**
183 * Walk through the rule string for specified number of levels
184 * @param rule the rule
185 * @param level current iteration level
186 */
187 void LindenmayerSystem::recursiveWalk(string rule, int level)
188 {
189 // Process every element in the rule string
190 for (int i = 0; i < rule.size(); i++)
191 {
192 switch (rule[i])
193 {
194 // walk
195 case '@':
196
197 // ignore marker, i.e. the "@"
198 i++;
199
200 // recursion
201 if (level == 0)
202 {
203 // at lowest level, start rendering
204
205 _turtle->walk();
206 }
207 else
208 {
209 // more levels to go
210
211 char ruleName[] = {rule[i], '\0'};
212 recursiveWalk(_rules[ruleName], level - 1);
213 }
214
215 break;
216
217
218 // turn left
219 case '+':
220
221 _turtle->turnLeft();
222 break;
223
224
225 // turn right
226 case '-':
227
228 _turtle->turnRight();
229 break;
230
231
232 // pitch down
233 case '&':
234
235 _turtle->pitchDown();
236 break;
237
238
239 // pitch up
240 case '^':
241
242 _turtle->pitchUp();
243 break;
244
245
246 // roll left
247 case '\\':
248
249 _turtle->rollLeft();
250 break;
251
252
253 // roll right
254 case '/':
255
256 _turtle->rollRight();
257 break;
258
259
260 // turn around 180 degrees
261 case '|':
262
263 _turtle->turnAround();
264 break;
265
266
267 // save state to stack
268 case '[':
269
270 _turtle->push();
271 break;
272
273
274 // restore state from stack
275 case ']':
276
277 _turtle->pop();
278 break;
279
280
281 // first vertex in a filled surface
282 case '{':
283
284 _turtle->fenceInBegin();
285 break;
286
287
288 // last vertex in a filled surface
289 case '}':
290
291 _turtle->fenceInEnd();
292 break;
293
294
295 // one vertex in a filled surface
296 case 'f':
297
298 _turtle->walk();
299 break;
300
301
302 // decrement diameter of segment
303 case '!':
304
305 _turtle->decrementDiameter();
306 break;
307
308
309 // unknown operation
310 default :
311
312 // TODO: filter these out when preprocessing
313 cerr << "Unknown operator in rule string:" << rule[i] << endl;
314 break;
315 }
316 }
317 }
318
319
320
321 /**
322 * Verify and preprocess one rule string
323 * @param ruleOrAxiom the rule
324 * @return the preprocessed rule
325 */
326 string LindenmayerSystem::verifyAndPreprocessRule(string ruleOrAxiom)
327 {
328 // for now, simply put "@" before every rewriting operator
329 // TODO: verifying
330
331 string preprocessedRule;
332
333 // check every element in rule
334 for (int i = 0; i < ruleOrAxiom.size(); i++)
335 {
336 // TODO: remove the A-Z limit: use strings, not single chars
337 if (ruleOrAxiom[i] >= 'A' && ruleOrAxiom[i] <= 'Z')
338 {
339 // mark as rewriting operator
340 preprocessedRule += '@';
341 }
342 preprocessedRule += ruleOrAxiom[i];
343 }
344
345 return preprocessedRule;
346 }
347
348
349
350 /**
351 * Unpreprocess one rule string
352 * @param ruleOrAxiom the rule
353 * @return the unpreprocessed rule
354 */
355 string LindenmayerSystem::unpreprocessRule(string ruleOrAxiom)
356 {
357 string unpreprocessedRule;
358
359 // check every element in rule
360 for (int i = 0; i < ruleOrAxiom.size(); i++)
361 {
362 // ignore rewriting marker
363 if (ruleOrAxiom[i] != '@')
364 {
365 unpreprocessedRule += ruleOrAxiom[i];
366 }
367 }
368
369 return unpreprocessedRule;
370 }