2018-01-28 23:30:54 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2018-01-28 23:45:43 +00:00
|
|
|
|
using System.Linq;
|
2018-01-28 23:30:54 +00:00
|
|
|
|
|
|
|
|
|
namespace SBRL.Algorithms.LSystem
|
|
|
|
|
{
|
|
|
|
|
public class Rule
|
|
|
|
|
{
|
|
|
|
|
public string Find;
|
|
|
|
|
public string Replace;
|
|
|
|
|
|
|
|
|
|
public Rule(string inFind, string inReplace)
|
|
|
|
|
{
|
|
|
|
|
Find = inFind;
|
|
|
|
|
Replace = inReplace;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Simulates an L-System.
|
|
|
|
|
/// Implemented according to http://www.cs.unm.edu/~joel/PaperFoldingFractal/L-system-rules.html
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class LSystemEngine
|
|
|
|
|
{
|
|
|
|
|
public readonly string Root;
|
|
|
|
|
private List<Rule> rules = new List<Rule>();
|
|
|
|
|
|
|
|
|
|
public int GenerationCount { get; private set; }
|
|
|
|
|
public string CurrentGeneration { get; private set; }
|
|
|
|
|
|
|
|
|
|
public Dictionary<string, string> Definitions { get; private set; }
|
|
|
|
|
|
|
|
|
|
public LSystemEngine(string inRoot)
|
|
|
|
|
{
|
|
|
|
|
CurrentGeneration = Root = inRoot;
|
|
|
|
|
Definitions = new Dictionary<string, string>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void AddRule(string find, string replace)
|
|
|
|
|
{
|
|
|
|
|
rules.Add(new Rule(find, replace));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Simulate()
|
|
|
|
|
{
|
|
|
|
|
List<KeyValuePair<int, Rule>> rulePositions = new List<KeyValuePair<int, Rule>>();
|
|
|
|
|
// Find all the current positions
|
|
|
|
|
foreach(Rule rule in rules)
|
|
|
|
|
{
|
2018-01-28 23:45:43 +00:00
|
|
|
|
IEnumerable<int> positions = AllIndexesOf(CurrentGeneration, rule.Find);
|
2018-01-28 23:30:54 +00:00
|
|
|
|
foreach (int pos in positions)
|
|
|
|
|
rulePositions.Add(new KeyValuePair<int, Rule>(pos, rule));
|
|
|
|
|
}
|
|
|
|
|
rulePositions.Sort(compareRulePairs);
|
|
|
|
|
|
|
|
|
|
string nextGeneration = CurrentGeneration;
|
|
|
|
|
int replaceOffset = 0;
|
|
|
|
|
foreach(KeyValuePair<int, Rule> rulePos in rulePositions)
|
|
|
|
|
{
|
|
|
|
|
int nextPos = rulePos.Key + replaceOffset;
|
|
|
|
|
nextGeneration = nextGeneration.Substring(0, nextPos) + rulePos.Value.Replace + nextGeneration.Substring(nextPos + rulePos.Value.Find.Length);
|
|
|
|
|
replaceOffset += rulePos.Value.Replace.Length - rulePos.Value.Find.Length;
|
|
|
|
|
}
|
|
|
|
|
CurrentGeneration = nextGeneration;
|
|
|
|
|
GenerationCount++;
|
|
|
|
|
return CurrentGeneration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int compareRulePairs(KeyValuePair<int, Rule> a, KeyValuePair<int, Rule> b)
|
|
|
|
|
{
|
|
|
|
|
return a.Key - b.Key;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// From http://stackoverflow.com/a/2641383/1460422
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="str"></param>
|
|
|
|
|
/// <param name="value"></param>
|
|
|
|
|
/// <returns></returns>
|
2018-01-28 23:45:43 +00:00
|
|
|
|
private IEnumerable<int> AllIndexesOf(string str, string value) {
|
2018-01-28 23:30:54 +00:00
|
|
|
|
if (String.IsNullOrEmpty(value))
|
2018-01-28 23:45:43 +00:00
|
|
|
|
throw new ArgumentException("the string to find may not be empty", nameof(value));
|
|
|
|
|
for (int index = 0; ; index += value.Length) {
|
2018-01-28 23:30:54 +00:00
|
|
|
|
index = str.IndexOf(value, index);
|
|
|
|
|
if (index == -1)
|
2018-01-28 23:45:43 +00:00
|
|
|
|
break;
|
|
|
|
|
yield return index;
|
2018-01-28 23:30:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static LSystemEngine FromFile(string filename)
|
|
|
|
|
{
|
|
|
|
|
return FromStream(new StreamReader(filename));
|
|
|
|
|
}
|
|
|
|
|
public static LSystemEngine FromStream(StreamReader source)
|
|
|
|
|
{
|
|
|
|
|
LSystemEngine resultSystem = new LSystemEngine(source.ReadLine());
|
|
|
|
|
|
|
|
|
|
string nextLine = string.Empty;
|
|
|
|
|
while (true) {
|
|
|
|
|
nextLine = source.ReadLine();
|
|
|
|
|
if (nextLine == null)
|
|
|
|
|
break;
|
|
|
|
|
if (!nextLine.Contains("=") || nextLine.StartsWith("#") || nextLine.Trim().Length == 0)
|
|
|
|
|
continue;
|
|
|
|
|
string[] parts = nextLine.Split(new char[]{'='}, 2);
|
|
|
|
|
|
|
|
|
|
if(parts[0].StartsWith("!"))
|
|
|
|
|
{
|
|
|
|
|
// This is a definition
|
|
|
|
|
resultSystem.Definitions.Add(parts[0].Trim('!'), parts[1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
resultSystem.AddRule(parts[0].Trim(), parts[1].Trim());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return resultSystem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|