Silverlight Spirograph
I've recently started reading the book Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages by Jacob J. Sanford and skipped ahead to Appendix B: An Introduction to Microsoft Silverlight. This chapter provides an excellent introduction to Silverlight projects using Visual Studio 2008 and the Silverlight 1.1 Alpha SDK. Unfortunately, the Wrox web site did not have a download for this chapter and I had to type in all the code and come up with one image. I learned that XAML tags are case sensitive! I finally got the analog clock example working. Clocks seem to be a popular demonstration project for Silverlight!
I learned enough to tackle my own project. A long time ago I found some sample code for generating spirograph designs and it is been my favorite algorithm ever since. I've used this code wherever a language permits one to draw lines. I've used Perl and the Tk GUI library to draw spirograph designs in Linux and I've even used it on my Palm handheld device. Since the analog clock example demonstrated drawing lines on the canvas dynamically that was all I needed to know to use my favorite graphic trick.
The canvas does not require any objects because all of the lines will be drawn through code:
<Canvas x:Name="parentCanvas"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="Page_Loaded"
x:Class="Spirograph.Page;assembly=ClientBin/Spirograph.dll"
Width="590"
Height="590"
Background="White"
>
</Canvas>
You can create a variety of interesting patterns just by changing the radius of the fixed circle or the offset of the moving circle. Usually I would add textboxes so you can change these values and then redraw the screen but I have not learned how to do this yet in XAML.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents; using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;
namespace Spirograph
{
public partial class Page : Canvas
{
public void Page_Loaded(object o, EventArgs e)
{
// Required to initialize variables
InitializeComponent();
int x = 0;
int y = 0;
int prevx = 0;
int prevy = 0;
int t;
int R = 35; // Radius fixed circle
int I = 400; // Iterations
int r = 1; // Radius moving circle
int O = 150; // Offset in moving circle
string hex = "#0000FF";
string xaml = "";
Line line = new Line();
Canvas parentCanvas = (Canvas)this.FindName("parentCanvas");
for (t = 0; t <= I; t++)
{
prevx = x;
prevy = y;
x = (int)((R + r) * Math.Cos((double)t) - (r + O) * Math.Cos((double)(((R + r) / r) * t)));
y = (int)((R + r) * Math.Sin((double)t) - (r + O) * Math.Sin((double)(((R + r) / r) * t)));
if (t > 0)
{
if (hex == "#00FF00")
{
hex = "#FF0000";
}
else if (hex == "#0000FF")
{
hex = "#00FF00";
}
else if (hex == "#FF0000")
{
hex = "#0000FF";
}
xaml = "<Line X1=\"" + x + "\" Y1=\"" + y + "\" X2=\"" + x + "\" Y2=\"" + y + "\" Stroke=\"" + hex + "\" StrokeThickness=\"1\" Canvas.Left=\"289\" Canvas.Top=\"289\"></Line>";
Debug.WriteLine(xaml, "xaml");
line = (Line)XamlReader.Load(xaml);
parentCanvas.Children.Add(line);
xaml = "<Line X1=\"" + prevx + "\" Y1=\"" + prevy + "\" X2=\"" + x + "\" Y2=\"" + y + "\" Stroke=\"" + hex + "\" StrokeThickness=\"1\" Canvas.Left=\"289\" Canvas.Top=\"289\"></Line>";
Debug.WriteLine(xaml, "xaml");
line = (Line)XamlReader.Load(xaml);
parentCanvas.Children.Add(line);
}
}
}
}