Click here to Skip to main content
Click here to Skip to main content

Spheres in GDI+

, 4 Feb 2003
Rate this:
Please Sign up or sign in to vote.
Drawing spheres on a Panel using conversion between pixel and logical metric units.

Sample Image - SpheresGDI+.gif

Introduction

During the development of a videogame skeleton, one of my biggest concerns was the drawing of a large interstellar map. Since it was just a skeleton, implementing 3D graphics with OpenGL or Direct3D was not an option. I soon discovered that GDI+ could very well fit to my needs. Next I had to face mainly two issues:

  • While .NET panels reason in terms of "pixels", my map was based on some "light years" metrical units.
  • I had to imagine an algorithm to easily paint a sphere (or spheroid) on that map

The included code shows how I solved those problems. It could be helpful to show how easy is to add graphics and shapes using GDI+ and .NET.

Conversion between pixel geometry and logical-units geometry

When we want to display something using GDI+, we must reason in terms of pixels. The Graphics object can be obtained by calling the CreateGraphics method from any inherited WinForm control. So, for instance, a Panel is an optimal choice as a drawing surface, much better than the form itself. When a Graphics object is initialized, it's geometry is based on the pixel displayed, being (0,0) the left-upper corner of it.

If I must draw a line, say, from the point (10,10) to the point (30,30) I must call:

//
// The main Graphics object
//
Graphics panelGraphics = this.panel.CreateGraphics();
panelGraphics.DrawLine(new Pen(Color.Black,0.2F),10,10,30,30);

Since every method and property of GDI+ is based on pixels, drawing items in terms of some other units of measure implies a conversion. Also, when we use "real world" formulae, they must be adapted to the "pixel" logic. Instead of spreading the conversions throughout the code, a class is what is needed.

We could use the .NET library class Graphics.PageScale to do so, since there are overloaded Graphics methods that accept float numbers (measure units) as input instead of integer numbers (pixel). But I preferred to write a class that has the same functions of PageScale, but it's slightly different in concept. You do not have to compute the PageScale constant, since it does that for you. Plus, you may use double precision.

UML Diagram for class GridView

The GridView class provides the methods to pass from a "pixel" geometry to some other "logical units" geometry. So, when you have to draw a rectangle with a width of 4.5 cm and a height of 3.0 cm to a 400x500 panel, all you have to do is:

  1. Initialize GridView with desired width of panel in logical units (if you want to display the 4.5 wide rectangle, a logicalW parameter of 10.0 seems perfect)
  2. Pass your panel size in pixel

In the case above the GridView will be constructed with:

//
// GridView initialized for a 400x500 pixel panel of 10.0 cm wide
//
GridView gv = new GridView(10.0,400,500);

Now, if you want to know how many pixels in this panel, a rectangle of 4.5 cm wide is, or if you want to know where the point (1.2cm,2.4cm) is, you may use:

//
// Conversions
//
int pixelWidth = gv.getPhysicalWidth(4.5);
int pX = gv.getPhysicalX(1.2);
int pY = gv.getPhysicalY(2.4);

Drawing spheres

When the GridView is initialized, the panel has now 2D cartesian axis (in the downloadable example, the length of them is 5.0 and the coordinates are displayed on top right).

An object of the class Sphere can be initialized like below:

//
// Initializing Sphere object
//
Sphere s1 = new Sphere(gv,r,this.centerX,this.centerY);
s1.SphereColor=this.txtColor.BackColor;

where

  • gv is the initialized GridView object
  • r is the sphere radius
  • centerX is the X coordinate of the center (in logical units)
  • centerY is the Y coordinate of the center (in logical units)

The algorithm for drawing the sphere is very simple, and it is based on drawing arcs in a rectangle whose dimensions shrink. The algorithm draws the arcs first from right to left, then from top to bottom.

private void drawArcs(Graphics g, Pen color, Rectangle r)
{

    int x1=r.Left+r.Width/2;
    int y1=r.Top;
    int x2=x1;
    int y2=r.Top+r.Height;

    int x3=r.Left;
    int y3=r.Top+r.Height/2;
    int x4=r.Left+r.Width;
    int y4=y3;

    // draw axis
    g.DrawLine(color,x1,y1,x2,y2);
    g.DrawLine(color,x3,y3,x4,y4);

    // right-left arcs
    for (int j=r.Width; j>0; j-=10)
    {
        int left = r.Left+(r.Width-j)/2;
        Rectangle rc = new Rectangle(left,r.Top,j,r.Height);
        g.DrawArc(color,rc,0.0F,180.0F); // 0-180 degrees
        g.DrawArc(color,rc,180.0F,360.0F); // 180-360 degrees
    }
    // top-bottom arcs
    for (int j=r.Height; j>0; j-=10)
    {
        int top = r.Top+(r.Height-j)/2;
        Rectangle rc = new Rectangle(r.Left,top,r.Width,j);
        g.DrawArc(color,rc,270.0F,450.0F); // 270-90 degrees
        g.DrawArc(color,rc,90.0F,270.0F); // 90-270 degrees
    }

}

Modifying the number of loops (j-=10) you can obtain finer wires. You can also modify the width of the Pen in order to get different results.

Point of Interest

When you draw something on a Control, on a Panel in this case, if you cover the control with another control (or another window), the contents just drawn disappears. For this reason, every new sphere that you draw is collected into an ArrayList collection. Whenever the Paint event is raised, I call the paint method for every shape in the collection.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Alessio Saltarin
Software Developer (Senior)
Italy Italy
Alessio Saltarin is Certified IT Architect and Senior Software Developer. He writes articles and book reviews for some italian magazines. He is the author of "Ruby e Rails" book.
Follow on   Twitter   Google+

Comments and Discussions

 
GeneralHello Pinmembermrlaunched20-Apr-10 3:02 
GeneralConversions and Scaling PinmemberPhilip Fitzsimons7-Feb-03 0:35 
GeneralRe: Conversions and Scaling PinmemberAlessio Saltarin7-Feb-03 0:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 5 Feb 2003
Article Copyright 2003 by Alessio Saltarin
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid