mike-obrien.net Resume Blog Labs
Sunday, September 03, 2006

The following function allows you to obtain (x,y) coordinates within a 3d perspective pie slice by specifying the pie dimensions, the radius (Distance from the pie origin out) and the inner angle (Degrees from the pie start angle, clockwise). The first step is to inverse our angle since our trig functions assume that degrees increase counter-clockwise from the x axis. The .NET pie function works in the opposite way, with degrees increasing clockwise from the x axis. Next we convert the degrees into radians. Then calulate the absolute coordinates of the pie origin so we can transform the relative corrdinates returned by the trig functions. Next we do the math; the the cosine of the target radian multipled by the radius gives us the X coordinate and the sine of the same gives us the Y coordinate. We need to inverse the Y coordinate since the trig functions assume a grid with an ascending Y axis above the X axis and Windows is the oppisite; ascending Y axis below the X axis. Note: This method does not work for the default pie behavior. This method only works when angles are transformed to a 3d perspective. 

The Get3dPiePoint and support functions:

Private Function Get3dPiePoint(ByVal Bounds As System.Drawing.Rectangle, _
   ByVal Angle As Single, _
   ByVal SweepAngle As Single, _
   ByVal XRadius As Integer, ByVal YRadius As Integer, _
   ByVal InnerAngle As Single) As Point

   Dim MidRadian As Single = ToRadian(GetInverseAngle(Angle, InnerAngle))
   Dim TransformX As Integer = Bounds.X + (Bounds.Width / 2)
   Dim TransformY As Integer = Bounds.Y + (Bounds.Height / 2)

   Return New Point((Math.Cos(MidRadian) * XRadius) + TransformX, (Math.Sin(MidRadian) * YRadius * -1) + TransformY)

End Function

Private Function GetInverseAngle(ByVal StartAngle As Single, _
   ByVal InnerAngle As Single) As Single

   Return 360 - (StartAngle + InnerAngle)

End Function

Private Function ToRadian(ByVal Angle As Single) As Single

   Return (Math.PI * Angle) / 180

End Function

The following code snippet demonstrates how to draw a text label in the center of a 3d perspective pie slice.

Private Sub Form1_Paint(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

   Dim PieBounds As New Rectangle(20, 20, 400, 100)
   Dim PieAngle As Single = 150
   Dim PieSweepAngle As Single = 100
   Dim PieText As String = "Strongbadia (28%)"
   Dim PieTextFont As New Font("Arial", 10, FontStyle.Bold)
   Dim PieTextPoint As Point
   Dim PieTextXRadius As Single = PieBounds.Width / 4
   Dim PieTextYRadius As Single = PieBounds.Height / 4
   Dim PieTextAngle As Single = PieSweepAngle / 2
   Dim PieTextBounds As SizeF

   '--> Grab the centerline of the pie slice (Sweep angle div 2)
   '--> and half the radius (Diameter div 4)
   PieTextPoint = Get3dPiePoint(PieBounds, PieAngle, PieSweepAngle, PieTextXRadius, PieTextYRadius, PieTextAngle)

   '--> Grab the dimensions of the text
   PieTextBounds = e.Graphics.MeasureString(PieText, PieTextFont)

   '--> Determine the 3d angles
   PieSweepAngle = To3dSweepAngle(PieBounds, PieAngle, PieSweepAngle)
   PieAngle = To3dAngle(PieBounds, PieAngle)

   '--> Draw the pie
   e.Graphics.FillPie(Brushes.SteelBlue, PieBounds, PieAngle, PieSweepAngle)

   '--> Center the text over the mid point
   PieTextPoint.X -= PieTextBounds.Width / 2
   PieTextPoint.Y -= PieTextBounds.Height / 2

   '--> Draw the text
   e.Graphics.DrawString(PieText, PieTextFont, Brushes.White, PieTextPoint)

End Sub

Private Function To3dSweepAngle(ByVal Bounds As Rectangle, _
   ByVal Angle As Single, _
   ByVal SweepAngle As Single) As Single

   If SweepAngle Mod 180 <> 0 Then

      Dim Angle3d As Single = To3dAngle(Bounds, Angle)
      SweepAngle = To3dAngle(Bounds, Angle + SweepAngle) - Angle3d

   End If

   If SweepAngle < 0 Then

      SweepAngle += 360

   End If

   Return SweepAngle

End Function

Private Function To3dAngle(ByVal Bounds As System.Drawing.Rectangle, _
   ByVal Angle As Single) As Single

   Dim Radians As Single = ToRadian(Angle)
   Dim X As Double = Bounds.Width * Math.Cos(Radians)
   Dim Y As Double = Bounds.Height * Math.Sin(Radians)
   Dim Angle3D As Single = Math.Atan2(Y, X) * 180 / Math.PI

   If Angle3D < 0 Then

      Return Angle3D + 360

   Else

      Return Angle3D

   End If

End Function

 

Sunday, September 03, 2006 6:21:53 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Saturday, September 02, 2006

The default pie drawing behavior in .NET does not create a pie slice that has 3d perspective when the bounds are rectangular. In order to give the pie slice 3d perspective you need to transform the angles using a little trig. I'm not a math expert so I'm not going to attempt to explan why/how the formulas work... ;-) Kudos to a ton of math sites and Julijan Sribar for his 3d pie chart for helping me figure this out. The following functions allow you to compute the angles for a 3d perspective:

Private Function To3dSweepAngle(ByVal Bounds As Rectangle, _
   ByVal Angle As Single, _
   ByVal SweepAngle As Single) As Single

   If SweepAngle Mod 180 <> 0 Then

      Dim Angle3d As Single = To3dAngle(Bounds, Angle)
      SweepAngle = To3dAngle(Bounds, Angle + SweepAngle) - Angle3d

   End If

   If SweepAngle < 0 Then

      SweepAngle += 360

   End If

   Return SweepAngle

End Function

Private Function To3dAngle(ByVal Bounds As System.Drawing.Rectangle, _
   ByVal Angle As Single) As Single

   Dim Radians As Single = ToRadian(Angle)
   Dim X As Double = Bounds.Width * Math.Cos(Radians)
   Dim Y As Double = Bounds.Height * Math.Sin(Radians)
   Dim Angle3D As Single = Math.Atan2(Y, X) * 180 / Math.PI

   If Angle3D < 0 Then

      Return Angle3D + 360

   Else

      Return Angle3D

   End If

End Function

Private Function ToRadian(ByVal Angle As Single) As Single

   Return (Math.PI * Angle) / 180

End Function

The following code snipet demonstrates how to draw a pie slice with 3d perspective:

Private Sub Form1_Paint(ByVal sender As Object, _
   ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

   Dim PieBounds As New Rectangle(20, 20, 400, 100)
   Dim PieAngle As Single = 150
   Dim PieSweepAngle As Single = 100

   '--> Determine the 3d angles
   PieSweepAngle = To3dSweepAngle(PieBounds, PieAngle, PieSweepAngle)
   PieAngle = To3dAngle(PieBounds, PieAngle)

   '--> Draw the pie
   e.Graphics.FillPie(Brushes.SteelBlue, PieBounds, PieAngle, PieSweepAngle)

End Sub


 

Saturday, September 02, 2006 8:05:07 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 

Recently I realized that the .NET LinearGradientBrush will not properly fill a pie slice with a sweep angle greater than 180 degrees. Instead you have to use a PathGradientBrush. It's pretty simple to do; just create a GraphicsPath which contains the path of your pie slice and pass it into the PathGradientBrush constructor. Then set your CenterPoint, CenterColor and SurroundColors, draw the slice with the PathGradientBrush and voilĂ !

Dim PieBounds As New Rectangle(20, 20, 200, 200)
Dim PieAngle As Single = 50
Dim PieSweepAngle As Single = 100
Dim PieCenterPoint As Point
Dim GradientPath As New System.Drawing.Drawing2D.GraphicsPath
Dim GradientBrush As System.Drawing.Drawing2D.PathGradientBrush

PieCenterPoint = New Point((PieBounds.Width / 2) + PieBounds.X, (PieBounds.Height / 2) + PieBounds.Y)

GradientPath.AddPie(PieBounds, PieAngle, PieSweepAngle)

GradientBrush = New System.Drawing.Drawing2D.PathGradientBrush(GradientPath)

GradientBrush.CenterPoint = PieCenterPoint

GradientBrush.CenterColor = Color.LightBlue
GradientBrush.SurroundColors = New Color() {Color.DarkBlue}

e.Graphics.FillPie(GradientBrush, PieBounds, PieAngle, PieSweepAngle)

Saturday, September 02, 2006 5:35:58 AM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Thursday, January 19, 2006

Thursday, January 19, 2006 11:39:09 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [0]  | 
Thursday, January 12, 2006

Back in '98 Adobe offered a really cool web graphics product called Image Styler. It wasent the most powerful tool on the market but it had a very streamlined and simple interface and would allow you to create professional web graphics very quickly. I have used it for years now and love it, but unfortunately Adobe mothballed it in 2000 when they released LiveMotion (It's replacement). I never really liked LiveMotion, even though the two products were similar there were some quirky differences. In any event, LiveMotion (A competing product to Macromedia's lineup) was discontinued in '03 and Adobe and Macromedia merged and the rest is history. For those of you who are looking for the full version of ImageStyler you can download it here. It's pretty difficult to find these days which is too bad since IMO it was a great product.

Thursday, January 12, 2006 10:50:58 PM (GMT Standard Time, UTC+00:00)  #   |  Comments [6]  |