Search Unity

Simple L-System

Discussion in 'Scripting' started by Artpen, Oct 21, 2020.

  1. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Hi guys, Simple question :) I am working on a L-system.
    And I saw some examples of using Dictionaries for storing rules.
    I am trying to make it work with separate Rule class. It is easy to iterate through Dictionary but I can't figure out how to loop through all the rules (as my rules in List)? to get my L system work correctly?

    Thank you

    Code (CSharp):
    1. using System.Text;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5.  
    6. public class SimpleLSystem : MonoBehaviour
    7. {
    8.     public string axiom;
    9.     public Rule[] rules;
    10.     [Range(0,5)]
    11.     public int iterations;
    12.  
    13.  
    14.     private void Update()
    15.     {
    16.         if (Input.GetKeyDown(KeyCode.Space))
    17.         {
    18.             Generate();
    19.         }
    20.     }
    21.  
    22.     private void Generate()
    23.     {
    24.         string currentString = axiom;
    25.         StringBuilder sb = new StringBuilder();
    26.  
    27.         for (int i = 0; i < iterations; i++)
    28.         {
    29.             foreach (var c in currentString)
    30.             {
    31.                 if (c == rule.predecessor)
    32.                 {
    33.                     sb.Append(rule.successor);
    34.                 }
    35.                 else
    36.                 {
    37.                     sb.Append(c);
    38.                 }
    39.             }
    40.             currentString = sb.ToString();
    41.             Debug.Log("Generation " + i + ": " + currentString);
    42.             sb = new StringBuilder();
    43.         }
    44.  
    45.     }
    46.  
    47. }
    48.  
    49.  
    50.  
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,108
    you iterate through dictionaries with foreach, and the element you get is a special construct called KeyValuePair. it's a struct that has two properties, Key and Value.
    Code (csharp):
    1. foreach(var item in myDictionary) {
    2.   if(item.Key == someKey) Debug.Log(item.Value);
    3. }
    foreach is usable because dictionaries implement IEnumerable
    the order with dictionaries isn't specified so you cannot rely on any particular ordering
    also make sure you don't modify the dictionary inside the foreach loop, because it's explicitly forbidden
    that's the usage out of box, obviously you can extend their featureset, or save the modifications and then apply them outside the foreach loop etc.
     
  3. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,108
    here's a crash course for dictionaries I wrote for someone else, maybe you'll find it useful.
     
  4. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Hi @orionsyndrome Yep I know how to do it with dictionary.
    I was wondering to use a custom Class Rule which holds two variables. And use it instead.
    But can't figure out how to iterate though it. Not in terms of syntax it terms of logic...
    Current code works with only one rule... but if I have 2 or 3 ? I need to iterate though each rule.predes

    Code (CSharp):
    1. private void Generate()
    2.     {
    3.         string currentString = axiom;
    4.         StringBuilder sb = new StringBuilder();
    5.         for (int i = 0; i < iterations; i++) // iteration
    6.         {
    7.             foreach (var c in currentString) // iteration through characters in string
    8.             {
    9.                 if (c == rule.predecessor) // rule.predecessor is from rule class
    10.                 {
    11.                     sb.Append(rule.successor);
    12.                 }
    13.                 else
    14.                 {
    15.                     sb.Append(c);
    16.                 }
    17.             }
    18.             currentString = sb.ToString();
    19.             Debug.Log("Generation " + i + ": " + currentString);
    20.             sb = new StringBuilder();
    21.         }
     
  5. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,108
    I see. Okay I need a bit more of explanation. What is the exact structure you're having?
    In the original code you had rules as an array, how a dictionary fits into that?
    I thought you were going to replace it or something.

    Can't bother learning about L-systems in depth, though I know what they are.
    If you explain the structure precisely, I can work out this class for you.
     
    Artpen likes this.
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,998
    Yes it would be possible to use a Dictionary for simple L-Systems. However if you want to have parametric or conditional replacement rules you could have multiple rules which match the same symbol but might differ in the parametric condition. Yes you said you want a simple L-System so you could use a dictionary. However you don't have to. Dictionaries are great since they can make the lookup faster if you have many rules. However in most cases you don't have many rules. Dictionaries are not automatically faster in all cases. Especially when you deal with single letters or short words.

    So you can simply iterate through all your rules in order to find a match. Something like this:

    Code (CSharp):
    1.  
    2. foreach (var c in currentString) // iteration through characters in string
    3. {
    4.     Rule foundRule = null;
    5.     foreach(var rule in rules)
    6.     {
    7.         if (c == rule.predecessor)
    8.         {
    9.             foundRule = rule;
    10.             break;
    11.         }
    12.     }
    13.     if (foundRule != null)
    14.         sb.Append(foundRule.successor);
    15.     else
    16.         sb.Append(c);
    17. }
    I've written a general purpose parametric string based L-system for a UnityAnswers question some years ago. The code is on github (MIT license). The LSystem itself isn't the most complicated thing about this. The LogicExpressionParser is way longer ^^.

    Note my L-System does not support context sensitive replacement rules. So more advanced systems could specify certain replacement rules based on what symbols preceed or follow the actual symbol you're currently working on. A bit like what regex allows you to do.
     
    Last edited: Oct 22, 2020
    Artpen likes this.
  7. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291
    Thank you. Ok I am not using dictionaries.
    I have one custom class Rule (see attached) and I create a list of Rules.

    I have a Generate method which iterates through current character in string and if this character is == to rule.predecessor (A) it will change it to rule.successor (AB). (A becomes AB).

    This is a basic logic. And it work only if I have one rule.

    I want to incorporate my Rules list to go through all rules. Say I have rule[0] A becomes AB and rule[1] B becomes A
     
  8. Artpen

    Artpen

    Joined:
    Jan 24, 2015
    Posts:
    291