|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThis article shows a WPF application that takes a set of simple rules, some input data to be processed by those rules, and then allows us to visualize the result of applying those rules to the input data. The rules and input data are stored in XML files, allowing you to have various configurations with which to experiment. BackgroundAt work the other day, I was talking with a good friend of mine, Grant Hinkson. Grant mentioned that he has been reading Stephen Wolfram’s book “A New Kind of Science.” He explained the idea of Cellular Automaton to me, which caught my interest and got my imagination going. The idea of having a simple set of rules that apply to basic building blocks, and then seeing what large-scale phenomena can arise out of applying those rules repeatedly is very intriguing to me. Now, before going one step further, I must make something very clear. I have not studied Wolfram’s work, I do not claim to understand his ideas, nor is my program in any way trying to demonstrate his concepts. His Mathematica product does an excellent job of that. I was simply inspired by a discussion about Cellular Automaton, and built a simple program based on that inspiration. The Idea of a Binary Rule SystemThe idea is simple: take a sequence of numbers, apply some rules to those numbers, and get a new sequence of numbers. Each number in the input sequence results in one number in the output sequence. The rules can contain any logic that you want. The simplest rule would be to return the input value. In my program, each rule returns the same output value for a given input value, but that output value is configurable. So, why do I call it a “binary” rule system? My rule system only works with the values zero through seven. Those eight numbers can be represented by the first three binary values (i.e. 000 through 111 in binary). When we render the numbers later, we will display each number as a visualization of its binary representation. Visual ExplanationLet’s start with the simplest example. In the demo project, the simple1.xml file contains this configuration: <?xml version="1.0" encoding="utf-8" ?>
<config iterations="8">
<rules>
<rule input="0" output="1" />
<rule input="1" output="2" />
<rule input="2" output="3" />
<rule input="3" output="4" />
<rule input="4" output="5" />
<rule input="5" output="6" />
<rule input="6" output="7" />
<rule input="7" output="0" />
</rules>
<numbers>
<number value="0" />
</numbers>
</config> The configuration seen above shows the fundamentals. The Running the program using the configuration above will create a visualization of these numbers, in decimal: 0 1 2 3 4 5 6 7 You could also say that it is a visualization of those same numbers, but in binary: 000 001 010 011 100 101 110 111
<?xml version="1.0" encoding="utf-8" ?>
<config
color1="White"
color2="LightGray"
color3="Gray"
iterations="8"
>
<rules>
<rule input="0" output="1" />
<rule input="1" output="2" />
<rule input="2" output="3" />
<rule input="3" output="4" />
<rule input="4" output="5" />
<rule input="5" output="6" />
<rule input="6" output="7" />
<rule input="7" output="0" />
</rules>
<numbers>
<number value="0" />
</numbers>
</config> Running the program with that configuration file loaded looks like this:
So far, we have only seen an example with one number in the <?xml version="1.0" encoding="utf-8" ?>
<config
color1="White"
color2="LightGray"
color3="Gray"
iterations="8"
>
<rules>
<rule input="0" output="1" />
<rule input="1" output="2" />
<rule input="2" output="3" />
<rule input="3" output="4" />
<rule input="4" output="5" />
<rule input="5" output="6" />
<rule input="6" output="7" />
<rule input="7" output="0" />
</rules>
<numbers>
<number value="0" />
<number value="1" />
<number value="2" />
<number value="3" />
<number value="4" />
<number value="5" />
<number value="6" />
<number value="7" />
</numbers>
</config> The above configuration renders like this:
<?xml version="1.0" encoding="utf-8" ?>
<config
color1="DodgerBlue"
color2="Orange"
color3="Lime"
iterations="200"
>
<rules>
<rule input="0" output="1" />
<rule input="1" output="2" />
<rule input="2" output="3" />
<rule input="3" output="4" />
<rule input="4" output="5" />
<rule input="5" output="6" />
<rule input="6" output="7" />
<rule input="7" output="0" />
</rules>
<numbers>
<number value="0" />
<number value="1" />
<number value="2" />
<number value="3" />
<number value="4" />
<number value="5" />
<number value="6" />
<number value="7" />
<number value="6" />
<number value="5" />
<number value="4" />
<number value="3" />
<number value="2" />
<number value="1" />
<number value="0" />
</numbers>
</config> The configuration seen above looks like this:
How the Rule System WorksThe rule system is very simple. It consists of a class that represents a number and a class that represents a rule. Each of those two classes has a corresponding collection class. Here is the public class BinaryNumber
{
byte _value;
public BinaryNumber(byte value)
{
if (value < 0 || 7 < value)
throw new ArgumentOutOfRangeException("value");
_value = value;
}
public bool Bit1
{
get { return _value % 2 == 1; }
}
public bool Bit2
{
get { return (_value >> 1) % 2 == 1; }
}
public bool Bit4
{
get { return (_value >> 2) % 2 == 1; }
}
public byte Value
{
get { return _value; }
}
} This class is a wrapper around a We create a sequence of public class BinaryNumberCollection : ReadOnlyCollection<BinaryNumber>
{
readonly BinaryRuleCollection _rules;
public BinaryNumberCollection(IList<BinaryNumber> list, BinaryRuleCollection rules)
: base(list)
{
_rules = rules;
}
public BinaryNumberCollection OutputNumbers
{
get
{
var outputQuery =
from number in base.Items
select _rules.ApplyRule(number);
return new BinaryNumberCollection(outputQuery.ToList(), _rules);
}
}
} The public class BinaryRule
{
public BinaryRule(byte input, byte output)
{
this.Input = new BinaryNumber(input);
this.Output = new BinaryNumber(output);
}
public BinaryNumber Input { get; private set; }
public BinaryNumber Output { get; private set; }
} Rules are added to a public class BinaryRuleCollection : ReadOnlyCollection<BinaryRule>
{
public BinaryRuleCollection(IList<BinaryRule> list)
: base(list)
{
}
public BinaryNumber ApplyRule(BinaryNumber input)
{
BinaryRule rule =
base.Items.FirstOrDefault(r => r.Input.Value == input.Value);
if (rule == null)
{
Debug.Fail("Missing rule for input value " + input.Value);
return null;
}
return rule.Output;
}
} How the Visualization WorksSo far, we have not talked about how I created the visualizations. We have focused only on what a binary rule system is and how it works. Now it is time to turn our attention to the rendering aspect of this program. There are many ways that I could have implemented the rendering. I wanted to keep it simple and use data binding and data templates as much as possible. I know there are other ways that would perform marginally faster, but performance is not a priority for me. I would rather keep it simple and easy to configure in XAML. The program has two data templates. One template renders a <DataTemplate DataType="{x:Type model:BinaryNumber}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Rectangle">
<Setter Property="Height" Value="1" />
<Setter Property="Width" Value="1" />
</Style>
</StackPanel.Resources>
<Rectangle x:Name="bit4" />
<Rectangle x:Name="bit2" />
<Rectangle x:Name="bit1" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Bit1}" Value="True">
<Setter
TargetName="bit1"
Property="Fill"
Value="{DynamicResource color3}"
/>
</DataTrigger>
<DataTrigger Binding="{Binding Bit2}" Value="True">
<Setter
TargetName="bit2"
Property="Fill"
Value="{DynamicResource color2}"
/>
</DataTrigger>
<DataTrigger Binding="{Binding Bit4}" Value="True">
<Setter
TargetName="bit4"
Property="Fill"
Value="{DynamicResource color1}"
/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate> The brushes used to paint each XDocument xdoc = XDocument.Load(configFilePath);
XElement configElem = xdoc.Element("config");
BrushConverter converter = new BrushConverter();
for (int i = 1; i <= 3; ++i)
{
string colorName = "color" + i;
XAttribute attr = configElem.Attribute(colorName);
Brush brush;
if (attr == null)
brush = Brushes.White;
else
brush = converter.ConvertFromString(attr.Value) as Brush;
App.Current.Resources[colorName] = brush;
} The <DataTemplate DataType="{x:Type model:BinaryNumberCollection}">
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate> The container for all of the <Border
Background="Black"
BorderBrush="Black"
BorderThickness="3"
Margin="6"
Padding="1"
>
<Viewbox Stretch="Fill">
<ItemsControl x:Name="itemList" />
</Viewbox>
</Border> ConclusionThis might not be the most useful program or article out there, but I think it is fun and interesting. The simple, declarative way that WPF can create an appealing visualization of this data is, in itself, appealing. I hope you enjoyed this article and, perhaps, learned a thing or two along the way. Happy coding!
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||