ARTICLE

Creating Irregularly Shaped Forms in GDI+

Posted by Mickey Marshall Articles | GDI+ in VB.NET October 18, 2010
I came across a Windows game that should up on the screen looking very similar to a painter's palette. That is to say, the main form for the game had a shape reminiscent of a painter's palette.
Download Files:
 
Reader Level:

Long ago in a lifetime far away, I came across a Windows game that should up on the screen looking very similar to a painter's palette. That is to say, the main form for the game had a shape reminiscent of a painter's palette.
 
 That idea intrigued me so I started a little digging. The answer was so simple that I had a hard time getting behind it.
 
 In a nutshell:
 
 1: Visualize the shape or even draw it on paper.
 
 2: Create a set of points (x & y Cartesian coordinates) that minimally define the outline of the intended shape.
 (At this point in the process there are two ways of handling the last step)
 
 For .net enthusiasts
 
 3a: Declare an instance of System.Drawing.Drawing2D.GraphicsPath.
 
 3b: Use an appropriate GraphicsPath function to add the points to the path.
 
 3c: Set the form's Region to a new instance of Region while passing the GaphicsPath to the constructor of Region.
 
 For old school die hards (like me)
  


SetWindowRgn (ByVal hWnd As Integer, ByVal hRgn As Integer, ByVal bRedraw As Boolean) As Integer
CreatePolygonRgn (ByRef lpPoint As Point, ByVal nCount As Integer, ByVal nPolyFillMode As Integer) As Integer
SetWindowRgn(Handle.ToInt32, CreatePolygonRgn(points(0), 5, 1), True)
4a: Call windows the API SetWindowRgn.

4b: For the hRgn parameter, call CreatePolygonRgn using the points defined earlier as the lpPoint parameter.

5: Sit back and let windows do the heavy lifting.

In this particular example we are going to create a Stopwatch application that actually looks like a stopwatch. First I found a simple picture of a stopwatch on the internet. Then, with a photo editor, I simply erased the face of the watch.

Stopwatch1.jpg

The essence of a stopwatch for me is the round body, a stem winder and a click button. For the sake of simplicity, I will 'move' the click button up onto the winder stem.

That leaves three reasonably simple geometric shapes to outline our stopwatch:

  1. A small square defining the winder.
  2. A small rectangle defining the click button / stem.
  3. A large circle defining the body of the watch.

Stopwatch2.jpg


Now we need to come up with a set of points the minimally describes the outline of these three shapes combined. We can do this geometrically in four areas:

1: The curve swept out from 0 degress to 85 degress (marked green below from 3 o'clock to 12 o'clock). I had to play with the focal point and radius of the circle to closely match the size of the wath face. This uses 85 points to more closely approximate a smooth curve. Windows draws straight lines between points.

For i=0 to 85
{
Point(i).X = FocalpointX + Cos(i * (pi / 180)) * Radius
Point(i).Y = FocalpointY - Sin(i * (pi / 180)) * Radius
}


Stopwatch3.jpg

2: The next point (86) is up (down in windows coordinats) 25 units (in screen y coordinat units). The right side of the stem.

points(86).X = points(85).X
points(86).Y = points(85).Y - 25

3: The next point (87) is right 15 points. This brings us to the bottom right corner of the winder.

points(87).X = points(86).X + 15
points(87).Y = points(86).Y

4: The next point is up another 25 units. This brings us to the top right corner of the winder.

Now the Math gets easy but visualizing it is tricky

points(88).X = points(87).X
points(88).Y = points(87).Y - 25

5: I have set aside 360 points for this shape (360 degrees in a circle). So now I jump to points 96-360. Point 360 will close the circle and the shape. The arc swept out by the green radius from the bottom left of the stem to 3 o'clock on the face of the watch.

For i=96 to 360
{
Point(i).X = FocalpointX + Cos(i * (pi / 180)) * Radius
Point(i).Y = FocalpointY - Sin(i * (pi / 180)) * Radius
}

Now we work backwards from point 95 to point 89.
6: Point 95 is up (down in windows coordinates) 25 units. This brings us to the top left of the stem.

points(95).X = points(96).X
points(95).Y = points(96).Y - 25

7: Point 94 is left 15 point. This marks the bottom left of the winder.

points(94).X = points(95).X - 15
points(94).Y = points(95).Y

8: Point 93 is up 15 points. To the top left of the winder.

points(93).X = points(94).X
points(93).Y = points(94).Y - 25

Theoretically we have minimally defined the shape except that point 89 through 92 are still unset. So we just set all of them to either point 93 or point 82 (I use point 82).

For i = 89 To 92
{
points(i).X = points(88).X
points(i).Y = points(88).Y
}

Now just add these points to a graphics path and set the forms region to that path, add some timer functionality and:

Stopwatch3.jpg


VB Code

        Dim points(360) As Point
        Dim Radius As Integer
        Dim X As Integer
        Dim Y As Integer
        Dim i As Short
        X = 160
        Y = 245
        Radius = 125

        ' remeber that the X, Y coordinates of a circle can be found using the sine and cosine functions from high school trigonometry
        ' Start the circle at the zero angle
        ' and place the stem at the top say 85 degrees
        ' so draw that arc
        For i = 0 To 85
            points(i).X = X + System.Math.Cos(i * (pi / 180)) * Radius ' pi/180 converts degrees to radians
            points(i).Y = Y - System.Math.Sin(i * (pi / 180)) * Radius
        Next
 
        ' define the corners of the rectangles that define the stem
        points(86).X = points(85).X
        points(86).Y = points(85).Y - 25
 
        points(87).X = points(86).X + 15
        points(87).Y = points(86).Y
 
        points(88).X = points(87).X
        points(88).Y = points(87).Y - 25

        ' finish the circle
        For i = 96 To 360
            points(i).X = X + System.Math.Cos(i * (pi / 180)) * Radius ' pi/180 converts degrees to radians
            points(i).Y = Y - System.Math.Sin(i * (pi / 180)) * Radius
        Next

        ' finish the stem
        points(95).X = points(96).X
        points(95).Y = points(96).Y - 25
 
        points(94).X = points(95).X - 15
        points(94).Y = points(95).Y
 
        points(93).X = points(94).X
        points(93).Y = points(94).Y - 25

        For i = 89 To 92
            points(i).X = points(88).X
            points(i).Y = points(88).Y
        Next

        ' Old school - Windows API
        'SetWindowRgn(Handle.ToInt32, CreatePolygonRgn(points(0), 360, 1), True)

        'New school - .NET
        Dim gPath As GraphicsPath = New GraphicsPath()
        gPath.AddCurve(points)
        Me.Region = New Region(gPath) 

Final Output

Clip1.jpg

Login to add your contents and source code to this article
share this article :
post comment
 

Thanks for sharing.

Posted by Mahesh Chand Oct 19, 2010
Become a Sponsor
PREMIUM SPONSORS
  • ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
    Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites - Click Here!
Nevron Diagram
Become a Sponsor