// 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;
/**
* 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();
}
+/**
+ * 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);
}
/**
- * 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);
}
* Get the initial rule (the axiom)
* @return the axiom
*/
-string LindenmayerSystem::getAxiom()
+Rule LindenmayerSystem::getAxiom()
{
- return unpreprocessRule(_axiom);
+ return _axiom;
}
* 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;
}
/**
- * 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 '+':
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;
-}