rkulp
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:
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.
- Move the origin to the lower left corner with X axis values increasing to the right and Y axis value increasing upwards.
- 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.
- 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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
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
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
ASKER
Thanks for the great bonus!
ASKER
Absolutely fantastic. Thank you very much.