Click here to Skip to main content
6,630,901 members and growing! (18,899 online)
Email Password   helpLost your password?
Multimedia » General Graphics » Graphics     Beginner License: The Code Project Open License (CPOL)

3D Pie

By dasjoedf

3D pie Charts and Trigonometry
VB, .NET
Posted:3 Oct 2008
Updated:11 Oct 2008
Views:10,091
Bookmarked:25 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
10 votes for this article.
Popularity: 3.27 Rating: 3.27 out of 5
1 vote, 10.0%
1
3 votes, 30.0%
2
1 vote, 10.0%
3
2 votes, 20.0%
4
3 votes, 30.0%
5

Introduction

A 3D pie charts with transparency 

Background

I needed a pie chart in a small application without the need or the complications of 3rd party software. At first I thought it would be an easy task till I realized that my math’s needed a little refreshing and that took a while, though the math’s content is fairly simple with limited trigonometry,  it did initially put a stain on the gray matter.

Fig 1

 

A 2D pie chart is very straight forward, knowing the Start and Sweep angle, DrawPie does this well, but as soon as you apply some perspective to the chart, you soon realize there's a bit of math’s involved here. The reason for this is that as perspective is introduced, the length of the arch of particular slices change according to the perspective tilt.

Fig 2

You can see from Fig 2 that the lengths of the arches particularly on the left and right side have changed from that of Fig 1 yet they are both the same pie chart.  This is where that stuff we did back at school comes into play.  So how do we do it?  Well the method is called Cartesian Coordinates’  named after a French guy (Well a Mathematician) Descartes. Mind you this was back in 1637.  Pretty smart when you think about, I mean back in 1637.

Well Microsoft didn’t make things easy as I will point out soon.

Firstly back to the Cartesian coordinate system.

Figure 3

As you can see in Fig 3 a point on a graph can be plotted in two ways, the first, knowing the angle and length (Radius--Polar Coordinates) or using the X and Y coordinates (Rectangular Coordinates). Note the zero reference in the centre; this is important because when we write code using system drawing, references are from the top left corner of a binding rectangle. To compensate for this (In other words, move the 0 reference) it’s only a matter of subtracting the radius (rectangle width/2) from X and adding the radius to Y.

   The top left corner is +Y because its up from the Cartesian zero Coordinate and it is –X because it’s left of the Cartesian zero coordinate.  You will also notice in Fig 3 that there are 4 quadrants in the Cartesian Plane, knowing this is also important because, for example, if say we look at the coordinate X= -5 and Y=5  in the second quadrant compare  this to X= 5 and Y= -5  in the forth quadrant, although the figure values are the same, the fact that the negative and positive signs are different, puts us in a different quadrant, hence it gives us a different angle,  for e.g. if we use +X as the base line,  and we call this zero degrees, as shown in fig 3 then if we look at anti clock rotation then any angle that lies in the first quadrant is between 0 and 90 (X = +X) and (Y= +Y)  and any angle that lies in the second quadrant we be in the range of 90 (Y= +Y)  to 180 (X = -X) and angles the third will be between 180 (X= -X) and 270 (Y= -Y) and so on .The importance of this is that when trigonometry is used to calculate X and Y coordinate from a given angle, take the following

Trig formula X = cos(angle)

                 Y = sin (angle)

Sounds easy enough, but take two angles for instance 0 degrees and 180 degrees, find Y

Y =  Sin (0)      = 0

Y =  Sin (180)  = 0

Ha,  Both equal = 0   Terrific! how do we tell the difference ?? to answer that we now look at the X coordinate given the same angle.

 

Treat for angle = 0 degrees

        X = cos(0) = 1

Now treat for angle = 180 degrees

        X = cos(180) = - 1

The difference is X = -1 for 180 degrees and X = 1 for 0 degrees

The reason for this is that the Trig formula is for right angle triangles or to put it into angle perspective 0 to 90 degrees so given a circle has 4 right angle triangles hence 4 quadrants. So back to fig 3 you can now see that when X is negative and Y is positive we are in the second quadrant and if X is negative and Y is negative then we are in the third quadrant

and so on. So by testing for X and Y polarity we can then determine which quadrant the angle lies, and by knowing this we can the add multiples of 90 as required.

        An important thing to remember is that basic Trig formulas require the angle to be radians, E.g. 360 degrees = 6.2831853  radians or 2 x pi  therefore 180 degrees = pi  so hence when you see a formula that includes  Math.PI / 180) what this does effectivelly is convert degress to radians.

So Math.Pi = 3.14159 plus a few more decimal places.

Now put it into practice.

 

First let’s look at the Form load event.

Private Sub GraphForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim Total As Double = 0

        Dim StartAngle As Single, FinishAngle As Single

        Me.Width = 340

        Me.Height = 340

        'Locate close button in centre of pie chart

         btnClose.Top = Margin + (Radius - btnClose.Height / 2)

        btnClose.Left = Margin + (Radius - btnClose.Width / 2)

       'Temp values for test purposes only

        For I = 0 To Values.Length - 1

        Values(I) = CSng(40)

        Next

'Tempory values added for test purposes only. This is the Text that appears on each slice

        Dim CurrentYear As Single = Year(Now)

        For I = 0 To Values.Length - 1

        Strings(I) = CurrentYear  'Temp values for test purposes only

        CurrentYear = CurrentYear - 1

         Next

        'Temp values for test purposes only. This is the Tooltip Text

        For I = 0 To Values.Length - 1

        StringsToolTip(i) = "Fiscal Year " & Strings(i)

        Next

        'Add all the values in the array to find a total

        For I = 0 To Values.Length - 1

       Total = Total + Values(I)

        Next

      'Give each slice a proportionate value in the values array . What happens here  we take

 360 being a full circle and then a proportionate fraction is allocated to each angle in the Angles array

        For I As Integer = 0 To Values.Length - 1 

        Angles(I + 1) = 360 * Values(I) / Total

        Next

        Angles(0) = 0 ' Set 1st Angle in array = 0

 ' Increment Angle(n) by the value before it

        For I As Integer = 1 To Values.Length

        Angles(I) = Angles(I) + Angles(I - 1)

        Next

        'Set the margins for 1st slice displacement

        StartAngle = GetAngle(Angle:=Angles(0) + m_Rotation)

        FinishAngle = GetAngle(Angle:=Angles(1) + m_Rotation)

        SetDisplacement(StartAngle, FinishAngle)

        Call MakePieChart()

       Timer1.Enabled = True ‘Start timer of slice animation

       End Sub

      Now in the load event we placed values into an array, for convienience they are all equal

angles,40 degrees. It is done here purely as a test exercise, you would load the array elsewhere based on values suited to your application. The array could be a variable lengh.

 Now lets examine the actual drawing of the pie chart

This is done in the MakePieChart routine. Firstly because I provided for semi transparent pie charts (Set m_TransparencyLevel and m_Transparency2Level variables in the declaration section. Note 255 is totally opaque, and lessor becomes more transparent. m_Transparency2Level is the top of the pie and m_TransparencyLevel is the body) Different effects can be achieved by changing the alpha values, eg set Body to 255 and top 55 

    The order in which slices are draw is important, this is commented in the MakePieChart routine. If the order is not correct then a slice that should be behind may appear in front, to demonstrate this just swap some of the blocks of code in the MakePieChart routine and set m_Transparency2Level accordingly.

    In the MakePieChart routine, before each angle in Angles(n) is used to drawPie it is first sent to the GetAngle routine, this is where trigonometry is applied. Note that when 3D is applied the Y radius is different to the X radius, in other words an ellipse has its width radius  (Major) larger than its height radius(Minor) So finding the Y coordinant requires the radius be multiplied by the aspect ratio.

    All angles contained in the angles array are sent to GetAngle before drawing  each pie slice. What happens in the GetAngle routine is first the X and Y coordinants are found Y having the radius adjusted by the aspect ratio. Once this is done we have a X and Y coordinant for a given angle on and an ellipse (it is an ellipse because we corrected the radius for Y by the aspect ratio) Now this is great but but drawPie in .Net requires angles (polar coordinants) so we need to translate the X and Y coordinant to an angle. So effectively we are doing a rectangular to Polar conversion.

Here’s the formular

GetAngle = ( Math.Atan((x)/Y) * (180/Math.PI))+90    '90 is added to change the base line

Now check the whole routine below.....

Private Function GetAngle(ByVal Angle As Single) As Single

    Dim X as single, Y as single

 'Now find the X and Y coordinance on the Cartesian plane

    X = Radius + Math.Cos((Angle) * Math.PI / 180) * Radius  'Major Radius

    Y = Radius - Math.Sin((Angle) * Math.PI / 180) * Radius _

    * m_AspectRatio 'Multiply by m_AspectRatio to give us Minor Radius

        'Subract radius to bring coordinances relative to the centre of the pie radius. We mentioned above why this is neccessary

        X = X - Radius

        Y = Y – Radius

‘Within the Cartesian plane coordinant we need a point we call zero degrees. So the +X line is select as zero degrees

        GetAngle = (Math.Atan((X) / Y) * (180 / Math.PI)) + 90 'add 90 to change base line

       if Y > 0 then that puts us in the top two quadrants so we must add 180

      ‘otherwise we zero again

      If Y >= 0 Then

      GetAngle = GetAngle + 180

      End If

        ‘If we go past 360 we start again, so make it equal zero

        If GetAngle = 360 Then GetAngle = 0

        Return GetAngle

       End Function

     A similar approach to this is required for DrawText except this argument requires rectangular coordinants X and Y So we take an angle, half way beween the start and sweep angle And using Trigonometry we obtain an X and Y coordinant, see DrawString routine.

     Depth is easy to accomplish, its just a matter of drawing the same pie several times(depending on depth) and displacing Y value each time by say one pixel (hence we have depth) An alternative would be to draw point to point. Note that depth must change as perspective is changed, so as you tilt the pie chart, the depth of the chart must change accordingly.

     I have added a little animation to the Pie chart (always looks good) though sometimes it can be over done and becomes boring.

    The pie chart once created, becomes the Background image of a form, (however, a picture box can be used) I like this approach because I can display the chart over other controls e.g. Listview, Datagridview etc using a transparent form, this approach reduces form clutter (too many forms on the screen), also, users like things simple. I also don’t like too many buttons on things, the mouse always makes a good interface, so I have added mouse wheel routines to take care of perspective and depth (less buttons, hate too many buttons).

   Use the Vscroll buttons(small, along side close button) to control luminance. This is done in the Paint Event. Values are changed within the ColorMatrix.

   Just place the mouse over the chart and the wheel controls perspective and placing the mouse over the close button and the  wheel controls depth, right clicking rotates the chart, though I just noticed I have a bug here which I shall fix when I get time.

The code is straight forward, read the comments, values are loaded on the load event, so remove and add appropriately.  Transparency for both top and body can be set by changing   m_TransparencyLevel and m_Transparency2Level  respectively to provide for different effect (play around as you wish)

I have also provided Tooltip independant for each slice,   Once again this required a bit of Trig Maths to get the angle range the mouse pointer is in relative to the pie slices. This is done in the PieChartForm_MouseMove event, the angle chosen is half way between the start and finish angle on each slice.  StringsToolTip(n) contains the text for each slice, for demo purposes this is also done in the Form load event  so in your application move accordingly.

If using the Transparent form approach ( Make PieChartForm transparent) it would pay to set the form background colour(And transparency color of coarse) the same as the  background colour of surface its going to be displayed on, this gives a better outline effect. The approach I used was to dock a form onto another form, so when the form its docked to moves around the chart moves with it, but with the feature that if it is pulled of the docked form (Using mouse down)  it is no longer anchored, this I beleive creates a good effect with versitility.

Setting  m_DepthModeTranparent to false in the declaration section will draw the pie body with a HatchStyle effect.

I will eventually update to add more  eg highlight slices on mouse enter etc a different to labeling etc.

Cheers

Joe Mifsud

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

dasjoedf


Member

Location: Australia Australia

Other popular General Graphics articles:

  • A flexible charting library for .NET
    Looking for a way to draw 2D line graphs with C#? Here's yet another charting class library with a high degree of configurability, that is also easy to use.
  • CxImage
    CxImage is a C++ class to load, save, display, transform BMP, JPEG, GIF, PNG, TIFF, MNG, ICO, PCX, TGA, WMF, WBMP, JBG, J2K images.
  • 3D Pie Chart
    A class library for drawing 3D pie charts.
  • Barcode Image Generation Library
    This library was designed to give an easy class for developers to use when they need to generate barcode images from a string of data.
  • ImageStone
    An article on a library for image manipulation.
Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 2 of 2 (Total in Forum: 2) (Refresh)FirstPrevNext
GeneralThe Univoter PinmemberIlíon2:16 6 Oct '08  
GeneralHmmmm .... PinmemberIlíon1:43 4 Oct '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 11 Oct 2008
Editor:
Copyright 2008 by dasjoedf
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project