Solved

How to change picturebox coordinate system?

Posted on 2011-09-23
17
2,326 Views
Last Modified: 2012-05-12
Hey everyone, so I have this graphing program that graphs regression lines. As of now it works well with small numbers, and the size of the picturebox scales with how large the points are. However when dealing with large numbers the graph lags out because it is way to large. I need to be able to scale the graphs coordinates so that it always shows the regression line no matter how big. Default origin is the top left corner (0,0). If i have plot points at like (297400, 10000, etc etc) I need to orgin to change for those numbers. Heres some pictures


This is the graph with small numbers:
 screen3up.jpg


Large Numbers - lags out from the graph size being too large
 screen4b.jpg

The large numbers being plotted are {69400, 2500, 109000, 5000, 207900, 7500, 297200, 10000, 346500, 12500}

I know those numbers are valid because oither graphing prgrams plot them perfectly, because the coordinate system scales.


Heres my code, if you could give me some tips you would make me very happy, as well as my boss!!

 
Public Class Form1

    'Declare variables
    '-----------------------------------------------------
    Private Structure PointType
        Dim X As Double
        Dim Y As Double
    End Structure


    Dim Reg As New RegressionObject
    Private LastEndPoint As New Point(0, 0)

    'Graph lines and points
    Dim regLine As New Series
    Dim points As New Series

    'The radius
    Const R = 100

    'Point
    Dim P(0 To 200) As PointType

    'Array that holds X and Y values for graphing
    Dim MyData() As Single = New Single() {69400, 2500, 109000, 5000, 207900, 7500, 297200, 10000, 346500, 12500}

    'Scale graph to stay within the picturebox
    Dim HorizontalScaleFactor As Single

    'The array for the points on the graph
    Dim DataPoints(MyData.Length - 1) As PointF

    'Points where the number labels are drawn
    Dim labels As PointF

    'The font
    Dim drawFont As New Font("Arial", 16)
    Dim cordFont As New Font("Arial", 8)

    'Regression degrees
    Dim deg As Integer

    Dim flag As Boolean
    Dim loaded As Boolean

    'FORM LOAD
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        For i As Integer = 1 To 10
            degBox.Items.Add(i)
        Next
        loaded = False
        flag = False
        degBox.Text = "3"
        Reg.Degree = deg
    End Sub



    'This updates the picturebox everytime it is redrawn
    Private Sub picboxPulseCurve_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles picboxPulseCurve.Paint

        Dim i As Integer

        e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
        'e.Graphics.DrawCure(ve(Pens.Blue, DataPoints))
        regLine.Name = "Regression Curve"
        regLine.ChartType = SeriesChartType.Spline
        regLine.Color = Color.Maroon
        With Reg
            Using pn As New Pen(Color.Red, 2)
                For i = 0 To .XYCount
                    DrawACircle(e, pn, New Point(P(i).X, P(i).Y), R / 10)
                Next i
            End Using

            LastEndPoint = New Point(0, .RegVal(CDbl(Me.ClientRectangle.Height)))
            Dim CurrentPoint As Point
            For i = 0 To Me.ClientRectangle.Width Step R
                CurrentPoint = New Point(i, .RegVal(CDbl(i)))
                Using pn As New Pen(Color.BlueViolet, 3)
                    e.Graphics.DrawLine(pn, LastEndPoint, CurrentPoint)
                End Using
                e.Graphics.DrawLine(Pens.Red, 0, MyData.Max + 150, 0, 0)
                e.Graphics.DrawLine(Pens.Red, MyData.Max + 150, 0, 0, 0)
                If (flag = False) Then
                    regLine.Points.AddXY(-CurrentPoint.X, -CurrentPoint.Y)
                End If
                LastEndPoint = CurrentPoint
            Next i
            If (flag = False) Then
                graph.Series.Add(regLine)
                graph.Series.Add(points)
            End If
        End With
    End Sub

    'Draws a circle at the given points
    Private Sub DrawACircle(ByVal e As System.Windows.Forms.PaintEventArgs, ByVal P As Pen, ByVal center As Point, ByVal radius As Integer)
        ' Create a bounding rectangle and make its center the center of our point
        ' Then make its width 2 * the radius
        ' Then draw our ellipse
        points.Name = "Points"
        points.ChartType = SeriesChartType.Point
        points.Color = Color.Black
        Dim rect As New Rectangle(center, New Size(0, 0))

        For i = -3000 To MyData.Max + 150 Step 50
            labels = New Point(i, 0)
            e.Graphics.DrawString(i.ToString(), drawFont, Brushes.Black, labels)
            e.Graphics.DrawLine(Pens.Black, i, 0, i, MyData.Max + 150)
        Next

        For i = -3000 To MyData.Max + 150 Step 50
            labels = New Point(0, i)
            e.Graphics.DrawString(i.ToString(), drawFont, Brushes.Black, labels)
            e.Graphics.DrawLine(Pens.Black, 0, i, MyData.Max + 150, i)
        Next

        rect.Inflate(radius, radius)
        e.Graphics.DrawEllipse(P, rect)
        e.Graphics.DrawString(center.X.ToString() + " " + center.Y.ToString(), cordFont, Brushes.Black, New PointF(center.X + 8, center.Y))
        If (flag = False) Then
            points.Points.AddXY(-center.X, -center.Y)
        End If
    End Sub


    'This button resets the graphs and data
    Private Sub resetBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles resetBtn.Click
        flag = False
        regLine.Points.Clear()
        points.Points.Clear()
        graph.Series.Clear()
        txtBox.ResetText()
        Reg.Init()

        MsgBox("Data has been reset!")
    End Sub


    'This populates the program with graph points that are loaded from a file
    'It also resets the graphs and data
    Private Sub PopulateDataPointsArray(ByVal PictureBoxWidth As Integer)
        'Scale the graph to fit the picture box:
        HorizontalScaleFactor = CSng(PictureBoxWidth / DataPoints.Length)
        picboxPulseCurve.Width = MyData.Max + 150
        picboxPulseCurve.Height = MyData.Max + 150

        If (flag = False) Then

            regLine.Points.Clear()
            points.Points.Clear()
            graph.Series.Clear()
        End If
        txtBox.ResetText()
        Reg.Init()

        'Populate the DataPoints array:
        For i As Integer = 0 To DataPoints.Length - 2
            DataPoints(i).X = MyData(i)
            DataPoints(i).Y = MyData(i + 1)

            txtBox.Text += ("X: " + MyData(i).ToString() + " Y: " + MyData(i + 1).ToString() + vbNewLine)


            With Reg
                .XYAdd(DataPoints(i).X, DataPoints(i).Y)
                P(.XYCount).X = DataPoints(i).X
                P(.XYCount).Y = DataPoints(i).Y
            End With

        Next

        Me.Refresh()

    End Sub

Open in new window

0
Comment
Question by:ezdrt
  • 7
  • 7
  • 3
17 Comments
 
LVL 13

Expert Comment

by:themrrobert
ID: 36588658
You are going to need a new approach.

You will have to use some easy math to scale the graph before you draw it, and keep the size of the PictureBox constant.

Scaling the ever growing picturebox is a hugely inefficient use of RAM, and as you found out, it doesn't work with large sets.
0
 

Author Comment

by:ezdrt
ID: 36588674
Yeah I didnt realize that until after I made the picture box size scale the way it does. Ive been searching for a way to just change the picturebox orgin. Was way easier in VB6 :(
0
 
LVL 13

Expert Comment

by:themrrobert
ID: 36588680
I may have jumped the gun on my answer, it seems you are already doing this to an extent. Let me actually comb through this now and I will try and give you a better response
0
 
LVL 13

Expert Comment

by:themrrobert
ID: 36588684
Yes I remember vb6, was my personal favorite... .NET cluttered everything up!
0
 

Author Comment

by:ezdrt
ID: 36588690
Thanks Id appreciate it:) Im the only programmer working on this project so a new set of eyes would help a lot.
0
 
LVL 13

Expert Comment

by:themrrobert
ID: 36588719
Btw, my initial response was correct ;), I was just working too quickly, I saw your scale factor and my brain assumed you were applying it differently, .... anyway.

Lets see, if you keep the picturebox costant at 1000x1000 for example, the math should be:

plotx = 64000

plotwidth = 75000

actualwidth = 1000

scalefactor = actualwidth / 75000

drawx = plotx * scalefactor

do the same for y, and this will give you multipliers that you apply to the variables before you draw them. this way, as far as vb is concerned, you are drawing withing the box, all the math happens BEFORE translation.

oh ya, and as far as the tick marks, you simply draw whatever number you want on the graph, and have it match the MATH numbers, not the pixel numbers.

(question, does it always start at 0,0?, if not, you will need to find the distance away from 0, multiplied by the scale factor, and then REMOVE this amount from the actual graphing of the reg line)

Lot of information here, let me know what you come back with :)
0
 
LVL 13

Accepted Solution

by:
themrrobert earned 334 total points
ID: 36588768
Sorry, I don't think I'll be able to write you a working code snippet, I no longer have vb installed.

However, I can help get you there. For starters, you will want to have (in your Window.onResize event)

Ok, at the top, and a new Private variable PB_WIDTH and PB_HEIGHT, this amount will be updated in the routine i'm about to describe:
a route which shrinks or grows the picturebox to match the amount of room left. This should be relatively easy, you can find the new width by taking Form.width - PictureBox.Left (and the same for Top/Height)

Since this is your code, you are best prepared to modify it. With your recently populated PB_WIDTH and PB_HEIGHT variables, you can get the scale as follows (i'm not sure how you want to define the max, perhaps the highest value rounded up to nearest 10,000, you can fill in that yourself)

so you have SCALEY = PB_HEIGHT / SCALEYMAX and SCALEX = PB_WIDTH / SCALEXMAX
 (the numbers i just had you find)

this gives you the multiplier you will apply to all numbers to be plotted BEFORE drawing.

hack out all of the code that scales the picturebox, and instead, keep the PB constant, and add in new code in form of (SCALEX * VARTOBEDRAWN) or similar.

Hopefully this gets you where you need. I will check back later to see if you need anymore help :)
0
 

Author Comment

by:ezdrt
ID: 36588791
ahh ok I see where youre going there, let me try some stuff out and ill get back to you:)

Oh and that scale factor I had in there, wasnt working at all. It would only work if you were plotting only y coordinates for some reason
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 13

Expert Comment

by:themrrobert
ID: 36588970
Yea ;) well i think you were on the right track with that, it just got lost among the easier automatic pb scaling :D

I'm sure you're well on your way to a much more powerful and robust graphing program, with a much smaller memory footprint.

I'll be back
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 36589027
In DrawACircle(), lines #108 thru #118, why are you redrawing the labels for every circle?  Those should be drawn ONCE.  Move those lines to line #71.5 (yes...71 and a Half).  DrawACircle() should, well, just "draw a circle".  Making it also draw the labels violates the "make every sub/function do ONE thing and do it WELL" rule.  ;)

At lines #81 thru #83, you're creating a new pen instance for every single line drawn!  The Pen color/width isn't being changed for any particular line so you should create ONE Pen outside the loop and re-use it the entire time.  Move line #81 to #78.5 and line#83 to #90.5.

Make those changes first and see if the performance increases.

0
 

Author Comment

by:ezdrt
ID: 36589361
Yeah that did help performance a little bit, thanks Idle ;)! Sometimes I overlook stuff like that would ive been staring at this screen for 8 hours haha.

Still working on that scaling using the tips themrrobert gave me. No luck yet :(
0
 
LVL 85

Assisted Solution

by:Mike Tomlinson
Mike Tomlinson earned 166 total points
ID: 36589679
Another approach is to do the scaling math as proposed by themrrobert but then use the Graphics.ScaleTransform() method and plot still using the raw data:
http://msdn.microsoft.com/en-us/library/zhc2xxtx.aspx

*Leave the scaling at 1:1 to draw the grid and labels first, then scale afterwards to draw the rest of the graph.
0
 

Author Comment

by:ezdrt
ID: 36590464
hmmm

so scale transform seems to be working on the line :)

Now i just gotta use themrroberts logic on the pictureBox to scale that accordingly...same with the circles, they need to appear in the right spots.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 36590510
If they are in the same scale, then the line and circles should all be in the correct spots.  You may need to draw them bigger, though, so they are visible to the user.
0
 

Author Comment

by:ezdrt
ID: 36590533
Yep I just moved the line of code up a little further, now everything scales correctly. Multiplied them by a large number so you can actuall see them now.


 screen5rx.jpg

0
 
LVL 13

Assisted Solution

by:themrrobert
themrrobert earned 334 total points
ID: 36590692
excellent! You are nearly there.

It seems you still need to adjust the the labels on the X and Y axis to match the scale, but other than that it looks good. is there anything else that you need?
0
 

Author Closing Comment

by:ezdrt
ID: 36590709
Nope I think I got it from here, thank both of you for your help:) A+!
0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

The ECB site provides FX rates for major currencies since its inception in 1999 in the form of an XML feed. The files have the following format (reducted for brevity) (CODE) There are three files available HERE (http://www.ecb.europa.eu/stats/exch…
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
Internet Business Fax to Email Made Easy - With eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, fr…
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now