Link to home
Start Free TrialLog in
Avatar of rkulp
rkulpFlag for United States of America

asked on

Need Help With GDI+ in VB.Net

I am trying to do a graph in VB.Net using GDI+ and am having trouble with the scaling of the axes. I would like to do two things, but number 1 is not as critical as number 2:
  1. Move the origin to the lower left corner with X axis values increasing to the right and Y axis value increasing upwards.
  2. Scale the graph so the X axis goes from 0 to a calculated value, say 1000 and the Y axis goes from 0 to another calculated value, say 500.
  3. How would I get the mousemove to show values from 0 to 1000 in X and 0 to 500 in Y, if that is not automatically accomplished? The object is to allow the user to move the mouse to a point and see the coordinates of that point in the scale of the chart.

If G is my graphics object, what would be the proper values for G.TranslateTransform and  G.ScaleTransform?  More importantly, are G.TranslateTransform and  G.ScaleTransform the correct way to proceed?   I have failed to get these things to work and have been unable to find any examples that can help. Unfortunately, what I did over a decade ago in VB6 does not seem to help here.

I would appreciate any suggestions or example code that anyone might have.
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of rkulp

ASKER

Mike,

Absolutely fantastic. Thank you very much.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Nice, Ark!
Thanks, Mark :) One note though - transformation affects on DrawString and Pen styles as well. In this case string will be flipped vertically and scaled, dashed lines became dotted. So, to draw strings and dahsed/dotted lines one's need resettransform. Here is working example:
Public Class Form1
    Private szViewPort As New Size(1000, 500), clientWidth, clientHeight As Single

    Private Sub DrawAxis(g As Graphics)
        g.ResetTransform()
        Dim scaleX = clientWidth / szViewPort.Width,
            scaleY = clientHeight / szViewPort.Height
        Using p As New Pen(Color.Black) With {.DashStyle = Drawing2D.DashStyle.Dash}
            g.DrawRectangle(p, 0.0F, 0.0F, clientWidth - 1, clientHeight - 1)
            Dim sf_x = New StringFormat With {.Alignment = StringAlignment.Near,
                                              .LineAlignment = StringAlignment.Center}
            Dim sf_y = New StringFormat With {.Alignment = StringAlignment.Center,
                                              .LineAlignment = StringAlignment.Far}
            For i = 1 To 9
                Dim x As Single = i * 100 * scaleX,
                    y As Single = clientHeight - i * 100 * scaleY
                If i < 5 Then
                    g.DrawLine(p, 30.0F, y, clientWidth, y)
                    g.DrawString(i * 100, New Font("Arial", 12), Brushes.Black, 0, y, sf_x)
                End If
                g.DrawLine(p, x, 0.0F, x, clientHeight - 20)
                g.DrawString(i * 100, New Font("Arial", 12), Brushes.Black, x, clientHeight, sf_y)
            Next
        End Using
    End Sub
    Private Sub DrawScaled(g As Graphics)
        Dim scaleX = clientWidth / szViewPort.Width,
            scaleY = clientHeight / szViewPort.Height
        g.Transform = New Drawing2D.Matrix(scaleX, 0, 0, -scaleY, 0, clientHeight)
        g.DrawEllipse(Pens.Red,
            New Rectangle(szViewPort.Width / 2 - 50, szViewPort.Height / 2 - 50, 100, 100))
    End Sub

    Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        PictureBox1.Anchor = AnchorStyles.Bottom Or AnchorStyles.Left Or
                             AnchorStyles.Right Or AnchorStyles.Top
        AddHandler PictureBox1.Resize, Sub(s, arg) CType(s, PictureBox).Invalidate()
        AddHandler PictureBox1.MouseMove, Sub(s As Object, args As MouseEventArgs)
                                              Dim pt = scalePosition(args.Location)
                                              lblPos.Text = pt.X & "; " & pt.Y
                                          End Sub
        AddHandler PictureBox1.Paint, Sub(s As Object, args As PaintEventArgs)
                                          With CType(s, PictureBox).ClientSize
                                              clientWidth = .Width
                                              clientHeight = .Height
                                          End With
                                          DrawAxis(args.Graphics)
                                          DrawScaled(args.Graphics)
                                      End Sub
    End Sub
    Private Function scalePosition(pt As Point) As Point
        Dim scaleX = clientWidth / szViewPort.Width,
            scaleY = clientHeight / szViewPort.Height
        Return New Point With {.X = pt.X / scaleX,
                               .Y = szViewPort.Height - pt.Y / scaleY}
    End Function
End Class

Open in new window

Avatar of rkulp

ASKER

I selected Mike's as best since he was first. Both are great. Thanks for your help.
Glad I could help. Bonus :)
 Private Function UnscalePoint(g As Graphics, pt As PointF) As PointF

  Dim e = g.Transform.Elements
  Return New PointF With {.X = pt.X * e(0) + e(4),
                          .Y = pt.Y * e(3) + e(5)}
 End Function
 Private Sub DrawStringUnscaled(g As Graphics, s As String,
                                   f As Font, b As Brush, pt As PointF,
                                   Optional sf As StringFormat = Nothing)
  Dim gs = g.Save(),
      ptNew = UnscalePoint(g, pt)
  g.ResetTransform()
  g.DrawString(s, f, b, ptNew, sf)
  g.Restore(gs)
 End Sub
 Private Sub DrawLineUnscaled(g As Graphics, p As Pen, ptFrom As PointF, ptTo As PointF)
  Dim gs = g.Save(),
      pt1 = UnscalePoint(g, ptFrom),
      pt2 = UnscalePoint(g, ptTo)
  g.ResetTransform()
  g.DrawLine(p, pt1, pt2)
  g.Restore(gs)
 End Sub

Open in new window

Now you can draw unscaled strings and lines within transformed graphics:
 Private Sub DrawScaled(g As Graphics)
  Dim scaleX = clientWidth / szViewPort.Width,
      scaleY = clientHeight / szViewPort.Height
  g.Transform = New Drawing2D.Matrix(scaleX, 0, 0, -scaleY, 0, clientHeight)
  g.DrawEllipse(Pens.Red,
      New Rectangle(szViewPort.Width / 2 - 50, szViewPort.Height / 2 - 50, 100, 100))
  Dim sf = New StringFormat With {.Alignment = StringAlignment.Center,
                                    .LineAlignment = StringAlignment.Center}
  DrawStringUnscaled(g, "test 300x300", New Font("Arial", 12),
                     Brushes.Black, New PointF(300, 300), sf)
 End Sub

Open in new window

Avatar of rkulp

ASKER

Thanks for the great bonus!