--- /dev/null
+// Copyright (C) 2006 Erik Dahlberg
+//
+// This file is part of LSystem3d.
+//
+// LSystem3D is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// LSystem3D is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with LSystem3D; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+
+
+#include <cmath>
+
+#include <map>
+#include <iostream>
+#include <string>
+
+#include "lindenmayersystem.h"
+#include "model.h"
+#include "turtle.h"
+
+using namespace std;
+
+
+
+/**
+ * Constructor
+ * @param model the model for generation
+ */
+LindenmayerSystem::LindenmayerSystem(Model *model)
+{
+ _model = model;
+ _turtle = new Turtle(model);
+
+ _axiom.clear();
+ _rules.clear();
+ _numIterations = 0;
+}
+
+
+
+/**
+ * Destructor
+ */
+LindenmayerSystem::~LindenmayerSystem()
+{
+ delete _turtle;
+}
+
+
+
+/**
+ * Set the initial rule (the axiom)
+ * @param axiom the axiom
+ */
+void LindenmayerSystem::setAxiom(string axiom)
+{
+ _axiom = verifyAndPreprocessRule(axiom);
+}
+
+
+
+/**
+ * Set one rule
+ * @param name rule name
+ * @param rule the rule
+ */
+void LindenmayerSystem::setRule(string name, string rule)
+{
+ _rules[name] = verifyAndPreprocessRule(rule);
+}
+
+
+
+/**
+ * Set the turn/pitch/roll angle
+ * @param degrees the angle, in degrees
+ */
+void LindenmayerSystem::setAngle(double degrees)
+{
+ // convert from degrees to radians
+ double radians = (degrees / 360.0) * (2.0 * M_PIl);
+
+ _turtle->setAngle(radians);
+}
+
+
+
+/**
+ * Set the number of iterations
+ * @param numIterations number of iterations
+ */
+void LindenmayerSystem::setNumIterations(int numIterations)
+{
+ _numIterations = numIterations;
+}
+
+
+
+/**
+ * Get the initial rule (the axiom)
+ * @return the axiom
+ */
+string LindenmayerSystem::getAxiom()
+{
+ return unpreprocessRule(_axiom);
+}
+
+
+
+/**
+ * Get all rules
+ * @return the rules
+ */
+map<string,string> LindenmayerSystem::getRules()
+{
+ map<string,string> theUnpreprocessedRules;
+
+ // unpreprocess all rules
+ map<string,string>::iterator currentRule;
+ for (currentRule = _rules.begin(); currentRule != _rules.end(); currentRule++)
+ {
+ theUnpreprocessedRules[currentRule->first] = unpreprocessRule(currentRule->second);
+ }
+
+ return theUnpreprocessedRules;
+}
+
+
+
+/**
+ * Get the turn/pitch/roll angle
+ * @return the angle, in degrees
+ */
+double LindenmayerSystem::getAngle()
+{
+ double radians = _turtle->getAngle();
+
+ // convert from radians to degrees
+ double degrees = 360.0 * (radians / (2.0 * M_PIl));
+
+ return degrees;
+}
+
+
+
+/**
+ * Get the number of iterations
+ * @return number of iterations
+ */
+int LindenmayerSystem::getNumIterations()
+{
+ return _numIterations;
+}
+
+
+
+/**
+ * Generate l-system data
+ */
+void LindenmayerSystem::generate()
+{
+ _model->clear();
+
+ recursiveWalk(_axiom, _numIterations);
+
+ _turtle->reset();
+}
+
+
+
+/**
+ * Walk through the rule string for specified number of levels
+ * @param rule the rule
+ * @param level current iteration level
+ */
+void LindenmayerSystem::recursiveWalk(string rule, int level)
+{
+ // Process every element in the rule string
+ for (int i = 0; i < rule.size(); i++)
+ {
+ switch (rule[i])
+ {
+ // walk
+ case '@':
+
+ // ignore marker, i.e. the "@"
+ i++;
+
+ // recursion
+ if (level == 0)
+ {
+ // at lowest level, start rendering
+
+ _turtle->walk();
+ }
+ else
+ {
+ // more levels to go
+
+ char ruleName[] = {rule[i], '\0'};
+ recursiveWalk(_rules[ruleName], level - 1);
+ }
+
+ break;
+
+
+ // turn left
+ case '+':
+
+ _turtle->turnLeft();
+ break;
+
+
+ // turn right
+ case '-':
+
+ _turtle->turnRight();
+ break;
+
+
+ // pitch down
+ case '&':
+
+ _turtle->pitchDown();
+ break;
+
+
+ // pitch up
+ case '^':
+
+ _turtle->pitchUp();
+ break;
+
+
+ // roll left
+ case '\\':
+
+ _turtle->rollLeft();
+ break;
+
+
+ // roll right
+ case '/':
+
+ _turtle->rollRight();
+ break;
+
+
+ // turn around 180 degrees
+ case '|':
+
+ _turtle->turnAround();
+ break;
+
+
+ // save state to stack
+ case '[':
+
+ _turtle->push();
+ break;
+
+
+ // restore state from stack
+ case ']':
+
+ _turtle->pop();
+ break;
+
+
+ // first vertex in a filled surface
+ case '{':
+
+ _turtle->fenceInBegin();
+ break;
+
+
+ // last vertex in a filled surface
+ case '}':
+
+ _turtle->fenceInEnd();
+ break;
+
+
+ // one vertex in a filled surface
+ case 'f':
+
+ _turtle->walk();
+ break;
+
+
+ // decrement diameter of segment
+ case '!':
+
+ _turtle->decrementDiameter();
+ break;
+
+
+ // unknown operation
+ default :
+
+ // TODO: filter these out when preprocessing
+ cerr << "Unknown operator in rule string:" << rule[i] << endl;
+ break;
+ }
+ }
+}
+
+
+
+/**
+ * Verify and preprocess one rule string
+ * @param ruleOrAxiom the rule
+ * @return the preprocessed rule
+ */
+string LindenmayerSystem::verifyAndPreprocessRule(string ruleOrAxiom)
+{
+ // for now, simply put "@" before every rewriting operator
+ // TODO: verifying
+
+ string preprocessedRule;
+
+ // check every element in rule
+ for (int i = 0; i < ruleOrAxiom.size(); i++)
+ {
+ // TODO: remove the A-Z limit: use strings, not single chars
+ if (ruleOrAxiom[i] >= 'A' && ruleOrAxiom[i] <= 'Z')
+ {
+ // mark as rewriting operator
+ preprocessedRule += '@';
+ }
+ preprocessedRule += ruleOrAxiom[i];
+ }
+
+ return preprocessedRule;
+}
+
+
+
+/**
+ * Unpreprocess one rule string
+ * @param ruleOrAxiom the rule
+ * @return the unpreprocessed rule
+ */
+string LindenmayerSystem::unpreprocessRule(string ruleOrAxiom)
+{
+ string unpreprocessedRule;
+
+ // check every element in rule
+ for (int i = 0; i < ruleOrAxiom.size(); i++)
+ {
+ // ignore rewriting marker
+ if (ruleOrAxiom[i] != '@')
+ {
+ unpreprocessedRule += ruleOrAxiom[i];
+ }
+ }
+
+ return unpreprocessedRule;
+}