Draw four interlocking Heighway dragons in C#

[Heighway dragon]

One of the interesting features of the Heighway dragon curve is that it can interlock with itself as shown in this picture. The four curves all start at the same point in the middle of the picture and each is rotated by a multiple of 90 degrees.

The curves also create an optical illusion. If you stare at them for a few seconds, it appears that the red curve is floating above the others. (At least it does to me.)

Because the pens used to draw the curve have some thickness, the curves tend to overlap each other slightly along their edges, particularly in their smallest filaments. For example, if you look closely at the picture, you’ll see that the black curve looks slightly puffier than the blue one.

The edges of the curve drawn last cover the edges of the others slightly. To make this effect easier to see, and to verify that the curves really do match up properly instead of the later curves covering big parts of those drawn earlier, the example lets you decide which curve should be drawn last. If you click on one of the curves, the following code executes to save the color that you clicked in the variable DrawLastColor.

// Select the clicked color as the one to draw last.
private Color DrawLastColor = Color.Black;
private void picDragon_MouseClick(object sender, MouseEventArgs e)
{
    // See which color was clicked.
    using (Bitmap bm = new Bitmap(picDragon.Image))
    {
        Color clr = bm.GetPixel(e.X, e.Y);
        if (clr == Color.FromArgb(255, 255, 0, 0))
            DrawLastColor = Color.Red;
        else if (clr == Color.FromArgb(255, 0, 255, 0))
            DrawLastColor = Color.Green;
        else if (clr == Color.FromArgb(255, 0, 0, 255))
            DrawLastColor = Color.Blue;
        else if (clr == Color.FromArgb(255, 0, 0, 0))
            DrawLastColor = Color.Black;
    }

    // Redraw.
    MakeImage();
}

The following code shows how the program draws the curves.

// Draw the dragon.
private void MakeImage()
{
    Cursor = Cursors.WaitCursor;

    Bitmap bm = new Bitmap(
        picDragon.ClientSize.Width,
        picDragon.ClientSize.Height);
    using (Graphics gr = Graphics.FromImage(bm))
    {
        gr.Clear(picDragon.BackColor);

        // Find the first control point.
        const int margin = 5;
        float dx = Math.Min(
            (picDragon.ClientSize.Width - 2 * margin) / (14 / 6f),
            (picDragon.ClientSize.Height - 2 * margin) / (14 / 6f));

        // Center it.
        float x0 = picDragon.ClientSize.Width / 2;
        float y0 = picDragon.ClientSize.Height / 2;

        // Recursively draw the lines.
        int level = (int)nudLevel.Value;
        if (DrawLastColor != Color.Red) 
            DrawDragonLine(gr, Pens.Red, level,
                Direction.Right, x0, y0, dx, 0);
        if (DrawLastColor != Color.Green)
            DrawDragonLine(gr, Pens.Green, level,
                Direction.Right, x0, y0, 0, dx);
        if (DrawLastColor != Color.Blue) 
            DrawDragonLine(gr, Pens.Blue, level,
                Direction.Right, x0, y0, -dx, 0);
        if (DrawLastColor != Color.Black)
            DrawDragonLine(gr, Pens.Black, level,
                Direction.Right, x0, y0, 0, -dx);

        // Redraw the one we should draw last.
        if (DrawLastColor == Color.Red)
            DrawDragonLine(gr, Pens.Red, level,
                Direction.Right, x0, y0, dx, 0);
        else if (DrawLastColor == Color.Green)
            DrawDragonLine(gr, Pens.Green, level,
                Direction.Right, x0, y0, 0, dx);
        else if (DrawLastColor == Color.Blue)
            DrawDragonLine(gr, Pens.Blue, level,
                Direction.Right, x0, y0, -dx, 0);
        else if (DrawLastColor == Color.Black)
            DrawDragonLine(gr, Pens.Black, level,
                Direction.Right, x0, y0, 0, -dx);
    }

    // Display the result.
    picDragon.Image = bm;

    Cursor = Cursors.Default;
}

The code prepares a bitmap and sets x0 and y0 to the midpoint of the PictureBox so the starting point for each curve is at the center of the bitmap. It then calls the DrawDragonLine method four times to draw each of the four curves. At this step the code skips drawing the curve in the color that you clicked and that is saved in the variable DrawLastColor.

The final two arguments passed to DrawDragonLine indicate the direction in which the level 0 curve should be drawn. By switching those parameters around, the code creates curves rotated by multiples of 90 degrees.

The code finishes by drawing the curve in the color stored in DrawLastColor so that curve is on top. If you click on the different curves, you can see how the clicked curve moves to the top. You should also be able to convince yourself that the curves do fit together with just a little overlap on the edges.

For more information on drawing the Heighway dragon, and in particular how the DrawDragonLine method works, see the posts:

With some additional work, you could tile an entire plane with Heighway dragons.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in algorithms, drawing, fractals, graphics, mathematics and tagged , , , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *