




The Koch Curve  Snowflake! / Graphics / C# 
A merry Christmas to all CPians! IntroductionThis program is written in VS2005, however, it would be very easy to convert to VS2003 and .NET 1.1. The Koch curve is a simple fractal that creates a pretty snowflakelike object. The iteration algorithm is very simple:
The programThe program is very simple. There are some constants that define the screen area, scaling and the offset of the initial snowflake, and the angle that we use to construct the equilateral triangle out of the middle segment: private const int width = 400;
private const int height = 300;
private const double scale = 0.5;
private const int xoffset = width / 4;
private const int yoffset = height / 6;
private const double rangle = 60 * Math.PI / 180.0;
private const int maxFlakes=2;
While running, the program draws several snowflakes in random positions and in random colors. After a set limit (which in the demo I have set to 2), the first snowflake in the list is removed (rendering starts to really bog down if we keep all the snowflakes). Initializationpublic Form1()
{
InitializeComponent();
rand=new Random((int)DateTime.Now.Ticks);
FormBorderStyle = FormBorderStyle.None;
StartPosition = FormStartPosition.CenterScreen;
ClientSize = new Size(width, height);
backBrush = new SolidBrush(Color.LightBlue);
pens = new List<Pen>(new Pen[] {new Pen(Color.White),
new Pen(Color.Red), new Pen(Color.Green)});
segPen = pens[0];
windowRect = new Rectangle(0, 0, width, height);
SetStyle(ControlStyles.DoubleBuffer  ControlStyles.UserPaint 
ControlStyles.AllPaintingInWmPaint, true);
segments = new List<Segment>();
flakes = new List<Flake>();
segments.Add(new Segment(0, height, width/2, 0));
segments.Add(new Segment(width/2, 0, width, height));
segments.Add(new Segment(width, height, 0, height));
timer = new Timer();
timer.Interval = 100;
timer.Tick += new EventHandler(OnTick);
timer.Start();
}
Three pens are initialized and the starting equilateral triangle is constructed. The timer event will generate the next iteration. Also, the "working" snowflake segment collection is initialized, as well as the completed snowflake collection. You'll also note the PaintingDuring painting, both the "working" snowflake and the completed snowflakes are drawn: protected override void OnPaint(PaintEventArgs e)
{
foreach (Segment seg in segments)
{
e.Graphics.DrawLine(segPen, Adjust(seg.P1), Adjust(seg.P2));
}
foreach (Flake flake in flakes)
{
flake.Draw(e.Graphics);
}
}
You'll note that the IterationWhen the timer event fires, the snowflake iterates to the next step, following the algorithm I described above: void OnTick(object sender, EventArgs e)
{
List<Segment> newSegments = new List<Segment>();
foreach (Segment seg in segments)
{
double length=seg.Length/3;
double a = Math.Atan2(seg.Height, seg.Width);
a = a + rangle;
Point p1 = new Point(seg.P1.X + seg.Width / 3,
seg.P1.Y + seg.Height / 3);
Point p2 = new Point(seg.P1.X + seg.Width * 2 / 3,
seg.P1.Y + seg.Height * 2 / 3);
Segment cutSeg = new Segment(p1.X, p1.Y, p2.X, p2.Y);
Point p = new Point((int)(cutSeg.P1.X + length * Math.Cos(a)),
(int)(cutSeg.P1.Y + length * Math.Sin(a)));
newSegments.Add(new Segment(seg.P1.X, seg.P1.Y, p1.X, p1.Y));
newSegments.Add(new Segment(p1.X, p1.Y, p.X, p.Y));
newSegments.Add(new Segment(p.X, p.Y, p2.X, p2.Y));
newSegments.Add(new Segment(p2.X, p2.Y, seg.P2.X, seg.P2.Y));
}
The segment is split into three pieces, where Next: if (segments[0].Length <= 1)
{
foreach (Segment seg in newSegments)
{
seg.P1 = Adjust(seg.P1);
seg.P2 = Adjust(seg.P2);
}
flakes.Add(new Flake(segPen, newSegments));
If the first segment's length is 1 or less, then any further iteration is pointless, so the "working" snowflake is added to the completed snowflake collection. The segment locations are scaled and offset one final time, since we don't need to work with them anymore. Also: if (flakes.Count == maxFlakes)
{
flakes.RemoveAt(0);
}
this code removes the first snowflake from the collection after a set number of snowflakes have been drawn. Finally: segments.Clear();
int rndX = rand.Next(width);
int rndY = rand.Next(height/2)+height/2;
int rndW = rand.Next(width  rndX);
double a = rangle;
Point p = new Point((int)(rndX + rndW * Math.Cos(a)),
(int)(rndY + rndW * Math.Sin(a)));
segments.Add(new Segment(rndX, rndY, p.X, p.Y));
segments.Add(new Segment(p.X, p.Y, rndX + rndW, rndY));
segments.Add(new Segment(rndX + rndW, rndY, rndX, rndY));
segPen = pens[rand.Next(3)];
}
else
{
segments = newSegments;
}
Invalidate();
}
The working snowflake segment collection is cleared and a new pen and starting point for the next snowflake is randomly determined. At the very end of the timer, the form is invalidated so that the ConclusionThe Koch curve is fun because you can do many things with it, such as creating random but interesting looking coastlines and continents. If you're ever in San Diego, I have (well, last time I looked) a permanent exhibit on fractals at the Reuben H. Fleet Science Center which illustrates this. Further reading 
