diff --git a/Editor/RulesetDrawer.cs b/Editor/RulesetDrawer.cs
index cc9c6a8..105108f 100644
--- a/Editor/RulesetDrawer.cs
+++ b/Editor/RulesetDrawer.cs
@@ -1,4 +1,3 @@
-using System.Text;
using ProceduralToolkit.CellularAutomaton;
using UnityEditor;
using UnityEngine;
@@ -15,8 +14,7 @@ public class RulesetDrawer : PropertyDrawer
private const float labelSpacing = 1;
private const float dropdownWidth = 13;
private const float dropdownSpacing = 2;
- private const string birthRuleName = "birthRule";
- private const string survivalRuleName = "survivalRule";
+ private const string ruleName = "rule";
private readonly GUIStyle dropdownStyle = (GUIStyle) "StaticDropdown";
private readonly GUIContent[] options = new GUIContent[]
@@ -86,78 +84,57 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten
{
EditorGUI.BeginProperty(position, label, property);
- var birthRule = property.FindPropertyRelative(birthRuleName);
- var survivalRule = property.FindPropertyRelative(survivalRuleName);
+ var ruleProperty = property.FindPropertyRelative(ruleName);
position = EditorGUI.PrefixLabel(position, label);
- float ruleWidth = (position.width - dropdownWidth)/2;
- Rect ruleRect = new Rect(position.x - labelSpacing, position.y, ruleWidth, position.height);
- DrawRule(birthRule, ruleRect, "B");
+ position = DrawRule(ruleProperty, position);
- ruleRect.x += ruleWidth + labelSpacing;
- DrawRule(survivalRule, ruleRect, "S");
-
- int oldIndentLevel = EditorGUI.indentLevel;
- EditorGUI.indentLevel = 0;
- {
- Rect dropdownRect = new Rect(ruleRect.xMax + dropdownSpacing, position.y, dropdownWidth, position.height);
- int selected = EditorGUI.Popup(dropdownRect, -1, options, dropdownStyle);
- if (selected >= 0)
- {
- SelectRuleset(birthRule, survivalRule, selected);
- }
- }
- EditorGUI.indentLevel = oldIndentLevel;
+ DrawDropdown(ruleProperty, position);
EditorGUI.EndProperty();
}
- private void SelectRuleset(SerializedProperty birthRule, SerializedProperty survivalRule, int selected)
+ private Rect DrawRule(SerializedProperty ruleProperty, Rect position)
{
- var ruleset = rulesets[selected];
+ string birthRuleString = Ruleset.GetBirthRuleString(ruleProperty.intValue);
+ string survivalRuleString = Ruleset.GetSurvivalRuleString(ruleProperty.intValue);
- birthRule.ClearArray();
- for (int i = 0; i < ruleset.birthRule.Length; i++)
- {
- birthRule.InsertArrayElementAtIndex(i);
- var element = birthRule.GetArrayElementAtIndex(i);
- element.intValue = ruleset.birthRule[i];
- }
-
- survivalRule.ClearArray();
- for (int i = 0; i < ruleset.survivalRule.Length; i++)
- {
- survivalRule.InsertArrayElementAtIndex(i);
- var element = survivalRule.GetArrayElementAtIndex(i);
- element.intValue = ruleset.survivalRule[i];
- }
- }
-
- private void DrawRule(SerializedProperty rule, Rect position, string label)
- {
- var stringBuilder = new StringBuilder();
- for (int i = 0; i < rule.arraySize; i++)
- {
- stringBuilder.Append(rule.GetArrayElementAtIndex(i).intValue);
- }
+ float ruleWidth = (position.width - dropdownWidth)/2;
+ var ruleRect = new Rect(position.x - labelSpacing, position.y, ruleWidth, position.height);
float oldLabelWidth = EditorGUIUtility.labelWidth;
int oldIndentLevel = EditorGUI.indentLevel;
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.indentLevel = 0;
- string ruleString = EditorGUI.TextField(position, label, stringBuilder.ToString());
+ {
+ birthRuleString = EditorGUI.TextField(ruleRect, "B", birthRuleString);
+
+ ruleRect.x += ruleWidth + labelSpacing;
+
+ survivalRuleString = EditorGUI.TextField(ruleRect, "S", survivalRuleString);
+ }
EditorGUIUtility.labelWidth = oldLabelWidth;
EditorGUI.indentLevel = oldIndentLevel;
- var ruleList = Ruleset.ConvertRuleStringToList(ruleString);
- rule.ClearArray();
- for (int i = 0; i < ruleList.Count; i++)
+ ruleProperty.intValue = Ruleset.ConvertRuleString(birthRuleString, survivalRuleString);
+
+ return ruleRect;
+ }
+
+ private void DrawDropdown(SerializedProperty ruleProperty, Rect position)
+ {
+ int oldIndentLevel = EditorGUI.indentLevel;
+ EditorGUI.indentLevel = 0;
{
- rule.InsertArrayElementAtIndex(i);
- var element = rule.GetArrayElementAtIndex(i);
- element.intValue = ruleList[i];
+ Rect dropdownRect = new Rect(position.xMax + dropdownSpacing, position.y, dropdownWidth, position.height);
+ int selected = EditorGUI.Popup(dropdownRect, -1, options, dropdownStyle);
+ if (selected >= 0)
+ {
+ ruleProperty.intValue = rulesets[selected].rule;
+ }
}
+ EditorGUI.indentLevel = oldIndentLevel;
}
}
}
diff --git a/Runtime/CellularAutomaton/Ruleset.cs b/Runtime/CellularAutomaton/Ruleset.cs
index 6ccb0cb..2894f57 100644
--- a/Runtime/CellularAutomaton/Ruleset.cs
+++ b/Runtime/CellularAutomaton/Ruleset.cs
@@ -1,5 +1,5 @@
using System;
-using System.Collections.Generic;
+using System.Collections;
namespace ProceduralToolkit.CellularAutomaton
{
@@ -42,87 +42,115 @@ public struct Ruleset
#endregion Common rulesets
- public byte[] birthRule;
- public byte[] survivalRule;
+ ///
+ /// Rule integer
+ ///
+ /// Life: B3/S23
+ /// S8 S7 S6 S5 S4 S3 S2 S1 S0 B8 B7 B6 B5 B4 B3 B2 B1 B0
+ /// 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 - 000001100 000001000
+ ///
+ /// B0 = 2 ^ 0 = 1
+ /// B1 = 2 ^ 1 = 2
+ /// B2 = 2 ^ 2 = 4
+ /// B3 = 2 ^ 3 = 8
+ /// B4 = 2 ^ 4 = 16
+ /// B5 = 2 ^ 5 = 32
+ /// B6 = 2 ^ 6 = 64
+ /// B7 = 2 ^ 7 = 128
+ /// B8 = 2 ^ 8 = 256
+ ///
+ /// S0 = 2 ^ 9 = 512
+ /// S1 = 2 ^ 10 = 1024
+ /// S2 = 2 ^ 11 = 2048
+ /// S3 = 2 ^ 12 = 4096
+ /// S4 = 2 ^ 13 = 8192
+ /// S5 = 2 ^ 14 = 16384
+ /// S6 = 2 ^ 15 = 32768
+ /// S7 = 2 ^ 16 = 65536
+ /// S8 = 2 ^ 17 = 131072
+ ///
+ public int rule;
- public Ruleset(byte[] birthRule, byte[] survivalRule)
+ private const int survivalOffset = 9;
+
+ /// Birth rule numbers, for example: "3"
+ /// Survival rule numbers, for example: "23"
+ public Ruleset(string birthRuleString = null, string survivalRuleString = null)
{
- this.birthRule = new byte[birthRule.Length];
- for (int i = 0; i < birthRule.Length; i++)
- {
- this.birthRule[i] = birthRule[i];
- }
- this.survivalRule = new byte[survivalRule.Length];
- for (int i = 0; i < survivalRule.Length; i++)
- {
- this.survivalRule[i] = survivalRule[i];
- }
+ rule = ConvertRuleString(birthRuleString, survivalRuleString);
}
- public Ruleset(List birthRule, List survivalRule)
+ public bool CanSpawn(int aliveCells)
{
- this.birthRule = birthRule.ToArray();
- this.survivalRule = survivalRule.ToArray();
+ int pow = 1 << aliveCells;
+ return (rule & pow) == pow;
}
- public Ruleset(string birthRule = null, string survivalRule = null)
+ public bool CanSurvive(int aliveCells)
{
- this.birthRule = ConvertRuleStringToList(birthRule).ToArray();
- this.survivalRule = ConvertRuleStringToList(survivalRule).ToArray();
+ int pow = 1 << (aliveCells + survivalOffset);
+ return (rule & pow) == pow;
}
- public bool CanSpawn(int aliveCells)
+ public static int ConvertRuleString(string birthRuleString, string survivalRuleString)
{
- foreach (byte number in birthRule)
+ int rule = 0;
+ if (!string.IsNullOrEmpty(birthRuleString))
{
- if (number == aliveCells) return true;
+ foreach (char c in birthRuleString)
+ {
+ if (!char.IsDigit(c)) continue;
+
+ byte digit = (byte) char.GetNumericValue(c);
+ int pow = 1 << digit;
+ rule |= pow;
+ }
}
- return false;
+ if (!string.IsNullOrEmpty(survivalRuleString))
+ {
+ foreach (char c in survivalRuleString)
+ {
+ if (!char.IsDigit(c)) continue;
+
+ byte digit = (byte) char.GetNumericValue(c);
+ int pow = 1 << (digit + survivalOffset);
+ rule |= pow;
+ }
+ }
+ return rule;
}
- public bool CanSurvive(int aliveCells)
+ public override string ToString()
{
- foreach (byte number in survivalRule)
- {
- if (number == aliveCells) return true;
- }
- return false;
+ return $"B{GetBirthRuleString(rule)}/S{GetSurvivalRuleString(rule)}";
}
- public static List ConvertRuleStringToList(string rule)
+ public static string GetBirthRuleString(int rule)
{
- var list = new List();
- if (!string.IsNullOrEmpty(rule))
+ string b = "";
+ var bitArray = new BitArray(new[] {rule});
+ for (int i = 0; i < survivalOffset; i++)
{
- foreach (char c in rule)
+ if (bitArray[i])
{
- if (char.IsDigit(c))
- {
- byte digit = (byte) char.GetNumericValue(c);
- if (!list.Contains(digit))
- {
- list.Add(digit);
- }
- }
+ b += i;
}
- list.Sort();
}
- return list;
+ return b;
}
- public override string ToString()
+ public static string GetSurvivalRuleString(int rule)
{
- string b = "";
- foreach (var digit in birthRule)
- {
- b += digit;
- }
string s = "";
- foreach (var digit in survivalRule)
+ var bitArray = new BitArray(new[] {rule});
+ for (int i = survivalOffset; i < bitArray.Length; i++)
{
- s += digit;
+ if (bitArray[i])
+ {
+ s += i - survivalOffset;
+ }
}
- return string.Format("B{0}/S{1}", b, s);
+ return s;
}
}
}
diff --git a/Samples/CellularAutomata/CellularAutomata.unity b/Samples/CellularAutomata/CellularAutomata.unity
index cfb40e6..1228836 100644
--- a/Samples/CellularAutomata/CellularAutomata.unity
+++ b/Samples/CellularAutomata/CellularAutomata.unity
@@ -223,8 +223,7 @@ MonoBehaviour:
width: 128
height: 128
ruleset:
- birthRule: 03
- survivalRule: 0203
+ rule: 6152
startNoise: 0.25
aliveBorders: 0
--- !u!4 &643499691