Minor cleanup.
[lsystem3d.git] / src / lindenmayersystem.cpp
index 9a660233f29d9043f846c80a00b5ae83e78165a3..18339122df83c5e9b8938c7f8bdd73d5568e21ce 100644 (file)
@@ -1,6 +1,6 @@
 // Copyright (C) 2006 Erik Dahlberg
 // 
-// This file is part of LSystem3d.
+// 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
 
 #include <cmath>
 
-#include <map>
-#include <iostream>
 #include <string>
 
 #include "lindenmayersystem.h"
 #include "model.h"
+#include "rule.h"
+#include "ruleset.h"
 #include "turtle.h"
 
 using namespace std;
@@ -35,16 +35,14 @@ using namespace std;
 
 /**
  * Constructor
- * @param model the model for generation
+ * @param model empty model
  */
 LindenmayerSystem::LindenmayerSystem(Model *model)
 {
     _model = model;
     _turtle = new Turtle(model);
     
-    _axiom.clear();
-    _rules.clear();
-    _numIterations = 0;
+    clear();
 }
 
 
@@ -59,25 +57,64 @@ LindenmayerSystem::~LindenmayerSystem()
 
 
 
+/**
+ * Clear all parameters
+ */
+void LindenmayerSystem::clear()
+{
+    _axiom.clear();
+    _rules.clear();
+    _turtle->setAngle(0.0);
+    _depth = 0;
+    
+    _turtle->reset();
+    _model->clear();
+}
+
+
+
+/**
+ * Generate L-system data
+ */
+void LindenmayerSystem::generate()
+{
+    // model session
+    _model->begin();
+        _model->setColorIndex(0);
+        recursiveWalk(_axiom.getPreprocessedContent(), _depth);
+    _model->end();
+}
+
+
+
+/**
+ * Render L-system data
+ */
+void LindenmayerSystem::render()
+{
+    _model->draw();
+}
+
+
+
 /**
  * Set the initial rule (the axiom)
  * @param axiom the axiom
  */
-void LindenmayerSystem::setAxiom(string axiom)
+void LindenmayerSystem::setAxiom(Rule axiom)
 {
-    _axiom = verifyAndPreprocessRule(axiom);
+    _axiom = axiom;
 }
 
 
 
 /**
  * Set one rule
- * @param name rule name
  * @param rule the rule
  */
-void LindenmayerSystem::setRule(string name, string rule)
+void LindenmayerSystem::setRule(Rule rule)
 {
-    _rules[name] = verifyAndPreprocessRule(rule);
+    _rules.addRule(rule);
 }
 
 
@@ -97,12 +134,35 @@ void LindenmayerSystem::setAngle(double degrees)
 
 
 /**
- * Set the number of iterations
- * @param numIterations number of iterations
+ * Set recursion depth
+ * @param depth depth of recursion
  */
-void LindenmayerSystem::setNumIterations(int numIterations)
+void LindenmayerSystem::setDepth(int depth)
 {
-    _numIterations = numIterations;
+    _depth = depth;
+}
+
+
+
+/**
+ * Set initial segment diameter
+ * @param diameter the diameter
+ */
+void LindenmayerSystem::setDiameter(double diameter)
+{
+    _segmentDiameter = diameter;
+    _model->setDiameter(diameter);
+}
+
+
+
+/**
+ * Set segment diameter factor
+ * @param diameterFactor the diameter factor
+ */
+void LindenmayerSystem::setDiameterFactor(double diameterFactor)
+{
+    _model->setDiameterFactor(diameterFactor);
 }
 
 
@@ -111,9 +171,9 @@ void LindenmayerSystem::setNumIterations(int numIterations)
  * Get the initial rule (the axiom)
  * @return the axiom
  */
-string LindenmayerSystem::getAxiom()
+Rule LindenmayerSystem::getAxiom()
 {
-    return unpreprocessRule(_axiom);
+    return _axiom;
 }
 
 
@@ -122,18 +182,9 @@ string LindenmayerSystem::getAxiom()
  * Get all rules
  * @return the rules
  */
-map<string,string> LindenmayerSystem::getRules()
+RuleSet 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;
+    return _rules;
 }
 
 
@@ -155,66 +206,86 @@ double LindenmayerSystem::getAngle()
 
 
 /**
- * Get the number of iterations
- * @return number of iterations
+ * Get recursion depth
+ * @return depth of recursion
  */
-int LindenmayerSystem::getNumIterations()
+int LindenmayerSystem::getDepth()
 {
-    return _numIterations;
+    return _depth;
 }
 
 
 
 /**
- * Generate l-system data
+ * Get initial segment diameter
+ * @return the diameter
  */
-void LindenmayerSystem::generate()
+double LindenmayerSystem::getDiameter()
 {
-    _model->clear();
-    
-    recursiveWalk(_axiom, _numIterations);
-    
-    _turtle->reset();
+    return _segmentDiameter;
 }
 
 
 
 /**
- * Walk through the rule string for specified number of levels
+ * Get segment diameter factor
+ * @return the diameter factor
+ */
+double LindenmayerSystem::getDiameterFactor()
+{
+    return _model->getDiameterFactor();
+}
+
+
+
+/**
+ * Recursively apply the replacement rules
  * @param rule the rule
- * @param level current iteration level
+ * @param level recursion level
  */
 void LindenmayerSystem::recursiveWalk(string rule, int level)
 {
-    // Process every element in the rule string
+    // process every element in the rule string
     for (int i = 0; i < rule.size(); i++)
     {
         switch (rule[i])
         {
-            // walk
+            // rule
             case '@':
-                
+
                 // ignore marker, i.e. the "@"
                 i++;
                 
                 // recursion
-                if (level == 0)
+                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);
+                    string name = rule.substr(i, 1);
+                    recursiveWalk(_rules.findRule(name).getPreprocessedContent(), level - 1);
                 }
                     
                 break;
                 
                 
+            // walk / rule
+            case 'F':
+                
+                {
+                    // replacement rule for 'F'?
+                    Rule rule = _rules.findRule("F");
+                    if (rule.getProbability() != 0.0 && level > 0)
+                    {
+                        recursiveWalk(rule.getPreprocessedContent(), level - 1);
+                    }
+                    else
+                    {
+                        // no rule for "F"
+                        _turtle->walk();
+                    }
+                }      
+                                     
+                break;
+                
+                
             // turn left
             case '+':
                 
@@ -271,100 +342,53 @@ void LindenmayerSystem::recursiveWalk(string rule, int level)
                 break;
                 
                 
-            // restore state from stack
+            // load state from stack
             case ']':
                 
                 _turtle->pop();
                 break;
                 
                 
-            // first vertex in a filled surface
+            // create a filled surface
             case '{':
                 
-                _turtle->fenceInBegin();
+                _model->fillBegin();
                 break;
                 
                 
-            // last vertex in a filled surface
+            // close a filled surface
             case '}':
                 
-                _turtle->fenceInEnd();
+                _model->fillEnd();
                 break;
                 
                 
             // one vertex in a filled surface
             case 'f':
                 
-                _turtle->walk();
+                _turtle->fillWalk();
                 break;
                 
                 
-            // decrement diameter of segment
+            // decrement segment diameter
             case '!':
                 
-                _turtle->decrementDiameter();
+                _model->decrementDiameter();
                 break;
                 
                 
-            // unknown operation
-            default :
+            // increment current index to color table
+            case '\'':
                 
-                // TODO: filter these out when preprocessing
-                cerr << "Unknown operator in rule string:" << rule[i] << endl;
+                _model->nextColor();
+                break;
+                
+                
+            // decrement current index to color table
+            case ',':
+                
+                _model->prevColor();
                 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;
-}