Draw an image circle in C#


[image circle]

This example is somewhat similar to the post Draw an image spiral in C# except it draws an image circle instead of an image spiral.

The key is the DrawImageCircle method, which draws a smaller image in a circle on top of a background color or image. Before I describe the method’s body, I want to explain its parameters.

DrawImageCircle Parameters

The following code shows the DrawImageCircle method’s signature.

// Draw the circle of images.
private void DrawImageCircle(Graphics gr,
    Bitmap image,
    int pic_width, int pic_height,
    int img_width, int img_height,
    double offset_multiple,
    double initial_rotation,
    int num_images, double radius)
{
    ...
}

The method’s gr parameter gives the Graphics object on which the method should draw. You can draw a background on the object before you pass it into this method. You can also pass the same Graphics object into the method several times to draw multiple image circles on the same picture.

The pic_width and pic_height parameters indicate how big the final picture should be.

The img_width and img_height parameters give a desired size for the smaller image that should be drawn repeatedly in an image circle. The method draws the image as large as possible without distorting it and making it fit in the indicated width and height.

The offset_multiple value indicates the number of multiples of the angle between images that should be added to the first image’s position. For example, suppose you want to draw six ships in an image circle. Then there are 360 / 6 = 30 degrees between each ship. If offset_multiple is 0.5, then the first ship is drawn 0.5 * 60 = 30 degrees around the circle. This is mostly useful if you want to draw two (or more) different alternating images around the circle. For example, you could draw six spaceships with offset_multiple = 0 and then six asteroids with offset_multiple = 0.5. The result will alternate between spaceships and asteroid around the circle.


[image circle]

The initial_rotation parameter indicates the amount by which the first image should be rotated. The first image is the one on the right side of the circle. The initial_rotation value represents rotation clockwise. For example, the spaceship picture shows a vertical spaceship. Setting initial_rotation to zero makes the rightmost image show a vertical spaceship. If you set initial_rotation to 45, you get the picture shown on the right.

The num_images parameter indicates the number of times the smaller image should appear spaced equally around the image circle.

Finally, radius gives the distance from the center of the picture to the center of the smaller images.

DrawImageCircle Body

Now that you understand the DrawImageCircle method’s parameters, here’s the entire method.

// Draw the circle of images.
private void DrawImageCircle(Graphics gr, Bitmap image,
    int pic_width, int pic_height,
    int img_width, int img_height,
    double offset_multiple, double initial_rotation,
    int num_images, double radius)
{
    GraphicsState state = gr.Save();

    // Get the picture's center.
    float cx = pic_width / 2f;
    float cy = pic_height / 2f;

    // Adjust the image size to preserve aspect ratio.
    double scale_x = (double)img_width / image.Width;
    double scale_y = (double)img_width / image.Height;
    double scale = Math.Min(scale_x, scale_y);
    img_width = (int)(image.Width * scale);
    img_height = (int)(image.Height * scale);

    // Get the image's source rectangle.
    RectangleF src_rect = new RectangleF(
        0, 0, image.Width, image.Height);

    // Make destination points to center the image at the origin.
    PointF[] dest_points =
    {
        new PointF(-img_width / 2f, -img_height / 2f),
        new PointF( img_width / 2f, -img_height / 2f),
        new PointF(-img_width / 2f,  img_height / 2f),
    };

    // Loop through the images.
    double dtheta = 360 / num_images;
    double theta = dtheta * offset_multiple;
    double angle = initial_rotation + theta;
    for (int i = 0; i < num_images; i++)
    {
        // Get the point where the image's center should be drawn.
        double x = cx + radius * Math.Cos(theta * Math.PI / 180);
        double y = cy + radius * Math.Sin(theta * Math.PI / 180);
        PointF point = new PointF((float)x,(float)y);

        // Rotate and then translate to (x, y).
        gr.ResetTransform();
        gr.RotateTransform((float)angle);
        gr.TranslateTransform((float)x, (float)y, MatrixOrder.Append);

        // Draw the image.
        gr.DrawImage(image, dest_points, src_rect, GraphicsUnit.Pixel);

        theta += dtheta;
        angle += dtheta;
    }

    gr.Restore(state);
}

The method first saves the state of the Graphics object so it can restore it later. It then finds the image’s center.

Next the code calculates horizontal and vertical scales that would map the smaller image to the indicated img_width and img_height values. It uses the smaller of the two scales to make the image fit within the desired dimensions without distorting the image. The method uses the scale to recalculate the image’s img_width and img_height.

Later the code will draw the image centered at the origin and use rotations and translations to move the image to its correct position on the image circle. To make drawing the image at the origin easier, it calculates the necessary source rectangle and destination points. The source rectangle includes the image’s entire area. The destination points define the upper left, upper right, and lower left corners of the image (at its scaled size) when it is drawn centered at the origin.

Now the code enters a loop to draw the desired number of images. The value dtheta is the angle in degrees between the images as measured from the center of the picture. The variable theta gives each image’s angle with respect to the center. The program initializes theta to the initial_rotation value times dtheta.

The variable angle indicates the angle by which an image should be rotated. Initially this value is set to the indicated initial_rotation value plus the initial value of theta. That rotates the image appropriately if initial_rotation is not zero.

Inside the loop the program uses sines and cosines (with angles measured in radians) to determine where the next instance of the smaller image should be drawn. The method resets the Graphics object’s transformations. It then rotates the image by angle degrees and translates the origin to the image’s desired position.

Next the code draws the image centered at the origin. The rotation and translation transformations move the result onto the image circle.

After it has drawn the image, the code adds dtheta to theta and angle so the next image will be correctly rotated and positioned.

After the loop ends, the method restores the Graphics object’s state to the way it was before the method installed the various rotations and translations.

Conclusion

As usual the example program has many details that are not described here. Loading the background color or picture, loading the smaller picture, saving the resulting picture into a file, and validating the parameters that you enter on the form are all important tasks, but they are relatively straightforward so they aren’t explained here.

Download the example to see additional details and to experiment with the program. By calling the method multiple times with different images and parameters, you can make some fairly complex images such as the one below.


[image circle]


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, graphics, image processing, mathematics and tagged , , , , , , , , , . Bookmark the permalink.

1 Response to Draw an image circle in C#

  1. Mercy says:

    Really very very nice tutorial.

    Thank U

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.