Initial revision
[lsystem3d.git] / lsystem3d / src / lindenmayersystem.cpp
diff --git a/lsystem3d/src/lindenmayersystem.cpp b/lsystem3d/src/lindenmayersystem.cpp
new file mode 100644 (file)
index 0000000..9a66023
--- /dev/null
@@ -0,0 +1,370 @@
+// 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;
+}