RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Use Transformations to Draw Your Own Great Graphs : Page 5

Use .NET to build your own graphing control that displays bar, line, and point data either on its own surface, in a printout, or in an image file.

Drawing Labels
The code divides the previous and next vectors' components by their lengths to get vectors of length 1. It then averages these unit vectors to get a new vector pointing halfway between the original vectors. To complete the operation, the code divides this vector's components by its length and multiplies by half the desired tick mark width. The result is a vector that the program can use to draw a tick mark at this point.

The DrawLabels method shown in the following code also uses GetTickVector; however this code draws text, so it has some special needs. The Graphics object uses transformations to make drawing the data relatively straightforward. In particular, it scales the drawing to flip Y coordinates. That allows world coordinates to start with (0, 0) in the lower left corner and increase upwards as you normally expect on a graph. Unfortunately that means that any text you draw on the Graphics object appears flipped upside down. To fix this, the DrawLabels method adds another transformation to flip the text right-side up:

   Private Sub DrawLabels(ByVal gr As Graphics)
      If Labels Is Nothing Then Exit Sub
      ' Save the original transformation.
      Dim old_transform As Matrix = gr.Transform
      ' Flip the transformation vertically.
      gr.ScaleTransform(1, -1, MatrixOrder.Prepend)
      ' Draw the labels.
      For i As Integer = 0 To Points.Length - 1
         ' Get the tick mark direction vector.
         Dim tx, ty As Single
         GetTickVector(i, tx, ty)
         ' Lengthen the tick mark vector to 
         ' add extra room for the text.
         Dim lbl_size As SizeF = _
            gr.MeasureString(Labels(i), LabelFont)
         Dim extra_len As Single = 0.375 * _
            Sqrt(lbl_size.Width * lbl_size.Width + _
            lbl_size.Height * lbl_size.Height)
         Dim tick_len As Single = Sqrt(tx * tx + ty * ty)
         tx *= (1 + extra_len / tick_len)
         ty *= (1 + extra_len / tick_len)
         ' Draw the label.
         Using sf As New StringFormat()
            sf.Alignment = StringAlignment.Center
            sf.LineAlignment = StringAlignment.Center
            Dim x, y As Single
            If LabelsOnLeft Then
               x = Points(i).X + tx
               y = -(Points(i).Y + ty)
               x = Points(i).X - tx
               y = -(Points(i).Y - ty)
            End If
            gr.DrawString(Labels(i), _
               LabelFont, LabelBrush, x, y, sf)
         End Using
      Next i
      ' Restore the original transformation.
      gr.Transform = old_transform
   End Sub
The method starts by saving the Graphics object's current transformation matrix in the variable old_transform. It then adds a scaling transformation at the beginning of the Graphics object's sequence of transformations to flip all Y coordinates. Now, when the code draws text, this transformation flips the text upside down. Then the original transformations that map world coordinates to device coordinates flip the text right-side up again.

After adding the new transformation, the code loops through the object's data points. For each point, it calls GetTickVector to find the tick mark vector for the point. It measures the point's label text and uses the size to add some extra length to the vector to make room for the label.

The code then draws the label. To draw on the right side of the data, the code switches the sign of the lengthened tick mark vector. Because the newly added scaling transformation flips Y coordinates, the code also switches the sign of the label's target Y position so it appears in its correct location. The new transformation flips the Y coordinate, inverting the string and placing it at the desired location in world coordinates. Then the Graphics object's original transformations map the new point to its correct final destination in device coordinates, flipping the text back in the process.

Finally DrawLabels restores the Graphics object's original transformation so other DataSeries objects can correctly map from world to device coordinates when drawing their data.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date