Make a rotating brush in C#

[rotating brush]
This example draws a background filled with a rotating brush. It then draws text over the background.

When it starts the program uses the following code to create points that define a regular polygon.

// The polygon's points.
private PointF[] PolygonPoints;

// The path.
private GraphicsPath Path;

// The rectangle where we will draw.
private Rectangle DrawingArea;

// Offset when assigning colors.
private int ColorOffset = 0;

// Make points that define a polygon.
private void Form1_Load(object sender, EventArgs e)
{
    // Double buffer to prevent flicker.
    this.DoubleBuffered = true;

    // Make the drawing area rectangle.
    const int margin = 10;
    DrawingArea = new Rectangle(
        margin, margin,
        ClientSize.Width - 2 * margin,
        ClientSize.Height - 2 * margin);

    // Make the polygon's points.
    PolygonPoints = MakePolygon(22, DrawingArea);

    // Make the brush's path.
    Path = new GraphicsPath();
    Path.AddPolygon(DoublePoints(PolygonPoints));
}

The form’s Load event handler first sets the form’s DoubleBuffered property to true to make the form double buffer graphics output. If you don’t do this, the example produces an annoying flicker. (Comment out this line and see.)

The program then defines a rectangle that fills the form minus a 10 pixel margin. This is the area where it will draw.

It then calls MakePolygon to make an array of points defining a 22-sided polygon. It saves those points and then uses them to make a GraphicsPath that it will later use to make the brush.

The following code shows the MakePolygon method.

// Return PointFs to define a polygon.
private PointF[] MakePolygon(int num_points, Rectangle bounds)
{
    // Make room for the points.
    PointF[] pts = new PointF[num_points];

    float sqrt2 = (float)Math.Sqrt(2.0);
    float rx = bounds.Width / 2f * sqrt2;
    float ry = bounds.Height / 2f * sqrt2;
    float cx = bounds.X + bounds.Width / 2f;
    float cy = bounds.Y + bounds.Height / 2f;

    // Start at the top.
    float theta = (float)(-Math.PI / 2.0);
    float dtheta = (float)(2.0 * Math.PI / num_points);
    for (int i = 0; i < num_points; i++)
    {
        pts[i] = new PointF(
            (float)(cx + rx * Math.Cos(theta)),
            (float)(cy + ry * Math.Sin(theta)));
        theta += dtheta;
    }

    return pts;
}

This method makes a polygon that covers the indicated rectangle. First if sets rx and ry equal to half of the rectangle’s width and height multiplied by the square root of 2. That extra factor enlarges the polygon so it covers the corners of the rectangle. If you omit that factor, the polygon fits completely inside the rectangle so the rectangle’s corners are not covered.

Next the code calculates the center point for the polygon. It then uses an angle theta to generate the polygon’s points. The result is a polygon that is stretched to have the same aspect ration as the rectangle and that is big enough to cover it completely.

The following DoublePoints method takes a PointF[] and returns a new array that has extra points added between each of the existing points.

// Insert a point between each of the polygon's points.
private PointF[] DoublePoints(PointF[] points)
{
    List<PointF> new_points = new List<PointF>();
    for (int i = 0; i < points.Length - 1; i++)
    {
        new_points.Add(points[i]);
        new_points.Add(PointBetween(points[i], points[i + 1]));
    }
    new_points.Add(points[points.Length - 1]);
    new_points.Add(
        PointBetween(points[0], points[points.Length - 1]));

    // Return the new points.
    return new_points.ToArray();
}

// Return a point between two points.
private PointF PointBetween(PointF point1, PointF point2)
{
    return new PointF(
        (point1.X + point2.X) / 2,
        (point1.Y + point2.Y) / 2);
}

The DoublePoints method loops through the existing points adding them to a list. As it does that it uses the PointBetween method to add new points between the existing ones to the list. When it finishes, it converts the list into an array.

The form’s Paint event handler shown in the following code draws with the rotating brush.

// Draw the polygon.
private void Form1_Paint(object sender, PaintEventArgs e)
{
    // Make a path gradient brush.
    using (PathGradientBrush br = new PathGradientBrush(Path))
    {
        // Define edge colors.
        Color[] edge_colors = new Color[PolygonPoints.Length * 2];
        Color[] color_series = new Color[]
        {
            Color.Green,
            Color.LightGreen,
            Color.White,
            Color.LightGreen,
        };
        for (int i = 0; i < edge_colors.Length; i++)
            edge_colors[i] = color_series[
                (i + ColorOffset) % color_series.Length];
        br.SurroundColors = edge_colors;
        br.CenterColor = Color.White;
        ColorOffset++;

        // Fill the polygon.
        //e.Graphics.FillPolygon(br, PolygonPoints);
        e.Graphics.FillRectangle(br, DrawingArea);

        // Draw text over the background.
        using (Font font = new Font("Times New Roman", 50,
            FontStyle.Bold))
        {
            using (StringFormat sf = new StringFormat())
            {
                sf.Alignment = StringAlignment.Center;
                sf.LineAlignment = StringAlignment.Center;
                e.Graphics.DrawString("C# Helper", font,
                    Brushes.Blue, DrawingArea, sf);
            }
        }
    }
}

The Paint event handler defines an array to hold colors for the polygon’s edges. It then loops through that array assigning colors in turn from the color_series array. This gives the edge colors the pattern green, light green, white, light green, and then repeating.

The code adds the value ColorOffset to each color’s index so in subsequent calls the colors indexes are shifted. That makes the brush seem to rotate.

After defining the colors, the program uses them to define the brush and it fills the rectangle on the screen. It then draws text on top of the background.


Download Example   Follow me on Twitter   RSS feed   Donate




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

Leave a Reply

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