Skip to content

Commit

Permalink
CellularAutomaton: Refactored Ruleset data format
Browse files Browse the repository at this point in the history
  • Loading branch information
BasmanovDaniil committed Jan 10, 2021
1 parent 4992aaa commit 5cdba29
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 110 deletions.
87 changes: 32 additions & 55 deletions Editor/RulesetDrawer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Text;
using ProceduralToolkit.CellularAutomaton;
using UnityEditor;
using UnityEngine;
Expand All @@ -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[]
Expand Down Expand Up @@ -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;
}
}
}
134 changes: 81 additions & 53 deletions Runtime/CellularAutomaton/Ruleset.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections;

namespace ProceduralToolkit.CellularAutomaton
{
Expand Down Expand Up @@ -42,87 +42,115 @@ public struct Ruleset

#endregion Common rulesets

public byte[] birthRule;
public byte[] survivalRule;
/// <summary>
/// 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
/// </summary>
public int rule;

public Ruleset(byte[] birthRule, byte[] survivalRule)
private const int survivalOffset = 9;

/// <param name="birthRuleString">Birth rule numbers, for example: "3"</param>
/// <param name="survivalRuleString">Survival rule numbers, for example: "23"</param>
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<byte> birthRule, List<byte> 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<byte> ConvertRuleStringToList(string rule)
public static string GetBirthRuleString(int rule)
{
var list = new List<byte>();
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;
}
}
}
3 changes: 1 addition & 2 deletions Samples/CellularAutomata/CellularAutomata.unity
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,7 @@ MonoBehaviour:
width: 128
height: 128
ruleset:
birthRule: 03
survivalRule: 0203
rule: 6152
startNoise: 0.25
aliveBorders: 0
--- !u!4 &643499691
Expand Down

0 comments on commit 5cdba29

Please sign in to comment.