Lindenmayer System
A Simple L-system Image Generator Featuring Turtle Graphics
Lsystem.cpp
Go to the documentation of this file.
1 
4 // MIT License
5 //
6 // Copyright (c) 2020 Ian Parberry
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to
10 // deal in the Software without restriction, including without limitation the
11 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
12 // sell copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
24 // IN THE SOFTWARE.
25 
26 #include "Types.h"
27 #include <sstream>
28 
29 #include "Lsystem.h"
30 
32 // LProduction: Rule data structure for Lindenmayer Systems
33 
34 #pragma region LProduction
35 
39 
40 LProduction::LProduction(char lhs, const std::wstring rhs, float fProb):
41  m_chLHS(lhs), m_wstrRHS(rhs), m_fProb(fProb){
42 } //constructor
43 
44 #pragma endregion LProduction
45 
47 // LSystem: A Lindenmayer System
48 
49 // Settings functions (functions that change LSystem's state)
50 
51 #pragma region Settings functions
52 
58 
59 void LSystem::AddRule(const LProduction& rule){
60  if(rule.m_fProb < 1)m_bStochastic = true; //stochastic rule detected
61 
62  auto p = m_mapRules.find(rule.m_chLHS); //find the rules with the same lhs
63 
64  if(p == m_mapRules.end()){ //if no rule with the same lhs in map
65  std::vector<LProduction> v; //for new map entry
66  v.push_back(rule); //add new rule
67  m_mapRules.insert(std::make_pair(rule.m_chLHS, v)); //add a new map entry
68  } //if
69 
70  else p->second.push_back(rule); //add to vector of rules with the same lhs
71 
72  //add rule to rule string for display
73 
74  m_wstrRuleString += rule.m_chLHS;
75  m_wstrRuleString += L" \u2192 " + (std::wstring)rule.m_wstrRHS; //\u2192 is an arrow
76 
77  //a bit of fuss here to get the probability with only 2 digits precision
78 
79  if(m_bStochastic){
80  std::wstringstream wstream; //weird but necessary
81  wstream.precision(2); //set to 2 digits after the decimal point
82  wstream << std::fixed << rule.m_fProb; //apply stream
83  m_wstrRuleString += L" (" + wstream.str() + L")"; //append to rule string
84  } //if
85 
86  m_wstrRuleString += L"\n"; //end of new rule in rule string
87 } //AddRule
88 
92 
93 void LSystem::SetRoot(const std::wstring& omega){
94  m_wstrRoot = omega; //set the root
95  m_wstrRuleString = L"Root is " + omega + L"\n" + m_wstrRuleString; //prepend
96 } //SetRoot
97 
100 
102  m_mapRules.clear(); //no rules
103  m_wstrRuleString.clear(); //no rule string
104  m_wstrRoot.clear(); //no root string
105  m_wstrBuffer[0].clear(); //nothing in buffer 0
106  m_wstrBuffer[1].clear(); //nothing in buffer 1
107  m_bStochastic = false; //no stochastic rules
108 } //Clear
109 
110 #pragma endregion Settings functions
111 
113 // Generate
114 
115 #pragma region Generate
116 
124 
125 void LSystem::Generate(const UINT n){
126  m_nGenerations = n;
127 
128  std::wstring* pSrc = &m_wstrBuffer[0]; //source buffer
129  std::wstring* pDest = &m_wstrBuffer[1]; //destination buffer
130 
131  *pSrc = m_wstrRoot; //copy root string to source buffer
132 
133  for(UINT i=0; i<n; i++){ //for each generation
134  pDest->clear();
135 
136  for(size_t i=0; i<pSrc->size(); i++){ //for each char in source
137  bool bRuleApplied = false; //whether a rule has been applied yet
138 
139  auto p = m_mapRules.find((*pSrc)[i]);
140 
141  if(p != m_mapRules.end()){
142  float fProb = 0; //cumulative probability
143  const float fRand = m_cRandom.randf(); //random value in [0, 1]
144 
145  for(auto rule: p->second){ //for each production that applies
146  fProb += rule.m_fProb; //accumulate probability
147 
148  if(fRand <= fProb){ //use the current rule
149  *pDest += rule.m_wstrRHS; //apply rule
150  bRuleApplied = true; //record that a rule was applied
151  break; //no need to try more rules
152  } //if
153  } //for
154  } //if
155 
156  if(!bRuleApplied) //no rule was applied to current symbol
157  *pDest += (*pSrc)[i]; //just copy over the current symbol
158  } //for
159 
160  std::swap(pSrc, pDest); //swap generation buffers
161  } //for
162 
163  m_pResult = pDest; //copy the latest string to the result string
164 } //Generate
165 
166 #pragma endregion Generate
167 
169 // Reader functions
170 
171 #pragma region Reader functions
172 
175 
176 const std::wstring& LSystem::GetString() const{
177  return *m_pResult;
178 } //GetString
179 
182 
183 const std::wstring& LSystem::GetRuleString() const{
184  return m_wstrRuleString;
185 } //GetRuleString
186 
189 
190 const UINT LSystem::GetGenerations() const{
191  return m_nGenerations;
192 } //GetGenerations
193 
196 
197 const bool LSystem::IsStochastic() const{
198  return m_bStochastic;
199 } //IsStochastic
200 
201 #pragma endregion Reader functions
std::wstring m_wstrRHS
Right-hand side of production.
Definition: Lsystem.h:46
std::map< wchar_t, std::vector< LProduction > > m_mapRules
Productions.
Definition: Lsystem.h:76
Useful types and structures.
void Clear()
Clear the rules, buffers, and settings.
Definition: Lsystem.cpp:101
LProduction(char lhs, const std::wstring rhs, float fProb=1)
Constructor.
Definition: Lsystem.cpp:40
float randf()
Get random floating point number.
Definition: Random.cpp:104
std::wstring * m_pResult
Pointer to generated string.
Definition: Lsystem.h:80
const bool IsStochastic() const
Is a stochastic L-system.
Definition: Lsystem.cpp:197
std::wstring m_wstrBuffer[2]
Generation buffers.
Definition: Lsystem.h:79
Interface for LProduction and LSystem.
Stochastic context-free production.
Definition: Lsystem.h:43
const std::wstring & GetRuleString() const
Get rule string.
Definition: Lsystem.cpp:183
CRandom m_cRandom
PRNG.
Definition: Lsystem.h:72
std::wstring m_wstrRuleString
Rule string.
Definition: Lsystem.h:77
char m_chLHS
Left-hand side of production.
Definition: Lsystem.h:45
std::wstring m_wstrRoot
Root string.
Definition: Lsystem.h:74
const std::wstring & GetString() const
Get generated string.
Definition: Lsystem.cpp:176
const UINT GetGenerations() const
Get number of generations.
Definition: Lsystem.cpp:190
void AddRule(const LProduction &rule)
AddRule rule.
Definition: Lsystem.cpp:59
void Generate(const UINT n)
Generate L-system from stored root and rules.
Definition: Lsystem.cpp:125
float m_fProb
Probability of production applying.
Definition: Lsystem.h:47
bool m_bStochastic
Includes a stochastic rule.
Definition: Lsystem.h:82
void SetRoot(const std::wstring &omega)
Set the root string.
Definition: Lsystem.cpp:93
UINT m_nGenerations
Number of generations.
Definition: Lsystem.h:83