[RESOLVED] Graphing a Circle in a Coordinate Plane with inputs Center and Radius
I'm trying to plot a circle on a Coordinate Plane. Now my problem is that whenever I'm trying to input something (for example, C:0,0 R:10), it doesn't plot correctly in the cartesian coordinate plane.
I've been trying to solve this for like hours but it seems like I can't fix this on my own. I think my problem lies within the Button1_Click event, I've tried changing the values of variables x, y and r but still can't get it right. Any help would be appreciated. Thanks.
My Code so far:
Code:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
g = Me.CreateGraphics()
End Sub
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint //the drawing of Cartesian Plane
e.Graphics.FillRectangle(Brushes.White, New Rectangle(300, 10, 500, 491))
e.Graphics.DrawLine(Pens.Black, New Point(555, 10), New Point(555, 500))
e.Graphics.DrawLine(Pens.Black, New Point(300, 250), New Point(800, 250))
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If TextBox1.TextLength <> 0 And TextBox2.TextLength <> 0 And TextBox3.TextLength <> 0 Then
Click = True
g.Clear(DefaultBackColor)
g.FillRectangle(Brushes.White, New Rectangle(300, 10, 500, 491))
g.DrawLine(Pens.Black, New Point(555, 10), New Point(555, 500))
g.DrawLine(Pens.Black, New Point(300, 250), New Point(800, 250))
x = CType(TextBox1.Text, Integer) * distance
y = CType(TextBox2.Text, Integer) * distance
r = CType(TextBox3.Text, Integer) * distance
g.FillEllipse(Brushes.Black, x, y, 7, 7)
g.DrawEllipse(Pens.Black, x, y, r, r)
Else
MessageBox.Show("Please Complete All Fields Before Plotting!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
Last edited by angel06; Oct 17th, 2015 at 11:07 AM.
Re: Graphing a Circle in a Coordinate Plane with inputs Center and Radius
You should do all your drawing from the Form1_Paint event handler.
If you draw using Me.CreateGraphics, that is only temporary on the screen area of the form, and is not the way you are suppose to be drawing.
In the Button click you can just set a flag indicating that the drawing is valid, and then Invalidate the form.
In the Paint event, after you draw you rectangle and line, check the flag to see if it is set, and if so, do the drawing code (moved) that you had in your button.
I haven't tested the code yet to see if there is a problem with your drawing, so I will give that a try since you haven't really given a description of what it is drawing and what you expect it to be drawing.
Code:
Public Class Form1
Private x As Integer
Private y As Integer
Private r As Integer
Private distance As Integer = 5
Private blackPen As New Pen(Color.Red, 1)
Private DrawIt as Boolean
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint //the drawing of Cartesian Plane
e.Graphics.FillRectangle(Brushes.White, New Rectangle(300, 10, 500, 491))
e.Graphics.DrawLine(Pens.Black, New Point(555, 10), New Point(555, 500))
e.Graphics.DrawLine(Pens.Black, New Point(300, 250), New Point(800, 250))
If DrawIt then
x = CType(TextBox1.Text, Integer) * distance
y = CType(TextBox2.Text, Integer) * distance
r = CType(TextBox3.Text, Integer) * distance
g.FillEllipse(Brushes.Black, x, y, 7, 7)
g.DrawEllipse(Pens.Black, x, y, r, r)
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If TextBox1.TextLength <> 0 And TextBox2.TextLength <> 0 And TextBox3.TextLength <> 0 Then
DrawIt = True
Click = True
Invalidate()
Else
MessageBox.Show("Please Complete All Fields Before Plotting!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
End Class
If your problem is just that the circle isn't centered at X,Y that is because X,Y defines the upperLeft corner of the bounding box, and width and height are the size of the bounding box.
To center the circle at X,Y offset your bounding box negatively by the radius and the width and height are the diameter, i.e. twice the radius
g.FillEllipse(Brushes.Black, x-7, y-7, 2*7, 2*7)
Last edited by passel; Oct 17th, 2015 at 07:28 AM.
Re: Graphing a Circle in a Coordinate Plane with inputs Center and Radius
Originally Posted by passel
You should do all your drawing from the Form1_Paint event handler.
If you draw using Me.CreateGraphics, that is only temporary on the screen area of the form, and is not the way you are suppose to be drawing.
In the Button click you can just set a flag indicating that the drawing is valid, and then Invalidate the form.
In the Paint event, after you draw you rectangle and line, check the flag to see if it is set, and if so, do the drawing code (moved) that you had in your button.
I haven't tested the code yet to see if there is a problem with your drawing, so I will give that a try since you haven't really given a description of what it is drawing and what you expect it to be drawing.
Code:
Public Class Form1
Private x As Integer
Private y As Integer
Private r As Integer
Private distance As Integer = 5
Private blackPen As New Pen(Color.Red, 1)
Private DrawIt as Boolean
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint //the drawing of Cartesian Plane
e.Graphics.FillRectangle(Brushes.White, New Rectangle(300, 10, 500, 491))
e.Graphics.DrawLine(Pens.Black, New Point(555, 10), New Point(555, 500))
e.Graphics.DrawLine(Pens.Black, New Point(300, 250), New Point(800, 250))
If DrawIt then
x = CType(TextBox1.Text, Integer) * distance
y = CType(TextBox2.Text, Integer) * distance
r = CType(TextBox3.Text, Integer) * distance
g.FillEllipse(Brushes.Black, x, y, 7, 7)
g.DrawEllipse(Pens.Black, x, y, r, r)
End If
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If TextBox1.TextLength <> 0 And TextBox2.TextLength <> 0 And TextBox3.TextLength <> 0 Then
DrawIt = True
Click = True
Invalidate()
Else
MessageBox.Show("Please Complete All Fields Before Plotting!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
End Class
If your problem is just that the circle isn't centered at X,Y that is because X,Y defines the upperLeft corner of the bounding box, and width and height are the size of the bounding box.
To center the circle at X,Y offset your bounding box negatively by the radius and the width and height are the diameter, i.e. twice the radius
g.FillEllipse(Brushes.Black, x-7, y-7, 2*7, 2*7)
I've tried your suggestion g.FillEllipse(Brushes.Black, x-7, y-7, 2*7, 2*7) and it worked. Now the problem is the circle is not plotted correctly on the coordinate plane that I drew. For example I input its center as (10,0) and its radius as 15, this graph of circle should be in the first quadrant. But in my case, it doesn't plot on the first quadrant.
Re: Graphing a Circle in a Coordinate Plane with inputs Center and Radius
Which quadrant to you consider the First (Upper/Left, Upper/Right, ?)
In any case, what I would normally expect for a graph is that 0,0 be in the center where your lines cross, and that +,+ coordinates be in the upper right.
My modification of your code to have that orientation would look like this. If you plot 0,0,20 then 0,0,20 shows up centered on the two axis and 100,100,20 shows up in the upper right.
Code:
Public Class Form1
Private x As Integer
Private y As Integer
Private r As Integer
Private DrawIt As Boolean
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint ' //the drawing of Cartesian Plane
Dim g As Graphics = e.Graphics
g.TranslateTransform(500, 310) 'Put 0,0 of the plot at pixel 500,310
g.ScaleTransform(1, -1) 'Reverse the Y axis so +Y is above 0,0
g.FillRectangle(Brushes.White, New Rectangle(-250, -250, 500, 500)) 'a 500x500 area centered on the 0,0 coordinate
g.DrawLine(Pens.Black, 0, -250, 0, 250)
g.DrawLine(Pens.Black, -250, 0, 250, 0)
If DrawIt Then
x = CType(TextBox1.Text, Integer)
y = CType(TextBox2.Text, Integer)
r = CType(TextBox3.Text, Integer)
FillCircle(g, x, y, 7, Brushes.Black)
DrawCircle(g, x, y, r, Pens.Red)
End If
End Sub
Private Sub DrawCircle(g As Graphics, x As Single, y As Single, r As Single, p As Pen)
g.DrawEllipse(p, x - r, y - r, r * 2, r * 2)
End Sub
Private Sub FillCircle(g As Graphics, x As Single, y As Single, r As Single, b As Brush)
g.FillEllipse(b, x - r, y - r, r * 2, r * 2)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
If TextBox1.TextLength <> 0 And TextBox2.TextLength <> 0 And TextBox3.TextLength <> 0 Then
DrawIt = True
Invalidate()
Else
MessageBox.Show("Please Complete All Fields Before Plotting!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
End Class
Re: Graphing a Circle in a Coordinate Plane with inputs Center and Radius
Okay big thanks for helping and guiding me Sir. Now I'm trying to add a button to clear the fields. I ended up something like this but it throws an InvalidCastException starting on the line x = CType(TextBox1.Text, Integer)
Code:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Click = False
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
g.Clear(DefaultBackColor)
g.TranslateTransform(500, 310) 'Put 0,0 of the plot at pixel 500,310
g.ScaleTransform(1, -1)
g.FillRectangle(Brushes.White, New Rectangle(-250, -250, 500, 500)) 'a 500x500 area centered on the 0,0 coordinate
g.DrawLine(Pens.Black, 0, -250, 0, 250)
g.DrawLine(Pens.Black, -250, 0, 250, 0)
x = CType(TextBox1.Text, Integer)
y = CType(TextBox2.Text, Integer)
r = CType(TextBox3.Text, Integer)
FillCircle(g, x, y, 7, Brushes.Black)
DrawCircle(g, x, y, r, Pens.Red)
End Sub
Re: Graphing a Circle in a Coordinate Plane with inputs Center and Radius
I don't know what setting click=True or click=False is doing for you.
Well, if you used my code then it would be...
Code:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
TextBox1.Clear()
TextBox2.Clear()
TextBox3.Clear()
DrawIt = False 'Don't do the drawing on the graph
Invalidate() 'Update the graph so the drawing portion is cleared
End Sub
I don't know what your full code looks like now, so can't really give a valid solution for that.
But logically, since you just cleared the textboxes, you wouldn't want any of the code that tries to create lines from the values in the textboxes (there aren't any, and you shouldn't be drawing from the Button click events anyway).
Last edited by passel; Oct 17th, 2015 at 08:44 AM.
Re: Graphing a Circle in a Coordinate Plane with inputs Center and Radius
Just so you know, because we invert the Y axis it means things that have an discernible orientation like a bitmap, or String will be seen to be drawn upside down.
If you want to draw a string at a particular points in your plot, you will most likely want to temporarily re-invert the Y axis at that point before drawing the string (or likewise, when using DrawImage).
One way this can be done is using a GraphicsContext object to save the current state of the Graphics object (what we're using to draw with), moving the 0,0 point to where you want to draw the object (e.g. the string, or a bitmap), invert the Y axis, draw the object at 0,0 (since we've moved 0,0 to that point), then restore the Graphics object back to what it was (0,0 at the center of the graph, the Y axis positive going Up) using the GraphicsContext object.
As an example, I've modified your code (actually modified my modification of your code) to add the capability of inputting a string to be drawn at the coordinate in the textbox. Also, went ahead and added a minimal bit of List processing, so the Circle is added to a list, and another list is used to support the strings independently (could be used to label the axis, for instance).
The remove buttons won't clear the textboxes any longer. They just remove the last entry added to the respective list, and redraw.
Here's the code. I added another textbox for the string input and two buttons for string list processing (add and remove).
p.s I also move the code that was processed in the Add buttons to subroutines that are called by the buttons, so that they can easily be called from other places besides the button click events. This is demonstrated by initializing the textboxes with some default values in the IDE, and calling the ProcessInput subs from the Form Load event handler to add an initial circle and string to the lists at startup.
You can always just hit the remove buttons to remove the initial default circle and string before adding new ones.
Also, got rid of the DrawIt boolean Flag. Since the circles and strings are drawn from lists, don't need the DrawIt flag. If there is nothing in the lists, there won't be an attempt to draw that object, so no need to branch around it. The DrawIt flag was superfluous.
Also added a function (ParseSingle) to consolidate the parsing and error checking of a value from the textbox, so that the common parsing and error checking and raising a messagebox to inform the user of any problems with the input could be done in one place, and be a oneliner for where it is used.
Code:
Public Class Form1
Private Class CircleType
Public x As Single
Public y As Single
Public r As Single
End Class
Private Class StringType
Public x As Single
Public y As Single
Public s As String
End Class
Private circleList As New List(Of CircleType)
Private stringList As New List(Of StringType)
Private Sub Form1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles Me.Paint ' //the drawing of Cartesian Plane
Dim g As Graphics = e.Graphics
g.TranslateTransform(500, 310) 'Put 0,0 of the plot at pixel 500,310
g.ScaleTransform(1, -1) 'Reverse the Y axis so +Y is above 0,0
g.FillRectangle(Brushes.White, New Rectangle(-250, -250, 500, 500)) 'a 500x500 area centered on the 0,0 coordinate
g.DrawLine(Pens.Black, 0, -250, 0, 250)
g.DrawLine(Pens.Black, -250, 0, 250, 0)
For Each ct As CircleType In circleList 'For each circle in the circle list
FillCircle(g, ct.x, ct.y, 7, Brushes.Black) ' draw the circle
DrawCircle(g, ct.x, ct.y, ct.r, Pens.Red)
Next
Dim gc As Drawing2D.GraphicsContainer
For Each st As StringType In stringList
gc = g.BeginContainer() 'Save our current matrix state, so we can temporarily modify it
g.TranslateTransform(st.x, st.y) 'Move (0,0) point to our String Start Point (Y is + up)
g.ScaleTransform(1, -1) 'Flip the Y axis so string characters are not drawn upsidedown (Y is + down)
g.DrawString(st.s, Me.Font, Brushes.Black, 7, 0) 'Draw String with bias 7 pixels in X so is outside the filled black dot (if at a circle)
g.EndContainer(gc) 'Restore our current matrix state, (0,0) back at the center, Y scale + up
Next
End Sub
Private Sub DrawCircle(g As Graphics, x As Single, y As Single, r As Single, p As Pen)
g.DrawEllipse(p, x - r, y - r, r * 2, r * 2)
End Sub
Private Sub FillCircle(g As Graphics, x As Single, y As Single, r As Single, b As Brush)
g.FillEllipse(b, x - r, y - r, r * 2, r * 2)
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
ProcessCircleInputs()
End Sub
Private Sub ProcessCircleInputs()
If TextBox1.TextLength <> 0 And TextBox2.TextLength <> 0 And TextBox3.TextLength <> 0 Then
Dim ct As New CircleType
Dim OK As Boolean
OK = ParseSingle(ct.x, TextBox1.Text, "Input for X is not a number")
OK = OK And ParseSingle(ct.y, TextBox2.Text, "Input for Y is not a number")
OK = OK And ParseSingle(ct.r, TextBox3.Text, "Input for R is not a number")
If OK Then 'If none of the inputs failed then
circleList.Add(ct) ' Add the circle to the list
Invalidate() ' Redraw the form
End If
Else
MessageBox.Show("Please Complete All Fields Before Plotting!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
Private Function ParseSingle(ByRef retVal As Single, strIn As String, ErrString As String) As Boolean
If Single.TryParse(strIn, retVal) Then
Return True
Else
MessageBox.Show(ErrString, "Input Error")
Return False
End If
End Function
Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
If circleList.Count > 0 Then
circleList.Remove(circleList.Last)
End If
Invalidate()
End Sub
Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
ProcessStringInputs()
End Sub
Private Sub ProcessStringInputs()
If TextBox4.Text.Length > 0 Then
Dim st As New StringType
Dim OK As Boolean
OK = ParseSingle(st.x, TextBox1.Text, "Input for X is not a number")
OK = OK And ParseSingle(st.y, TextBox2.Text, "Input for Y is not a number")
If OK Then
st.s = TextBox4.Text
stringList.Add(st)
Invalidate()
End If
End If
End Sub
Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
If stringList.Count > 0 Then
stringList.Remove(stringList.Last)
Invalidate()
End If
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
ProcessCircleInputs()
ProcessStringInputs()
End Sub
End Class
For those that don't want to manually setup a project, adding the four buttons and textboxes, I've attach a solution project (VS 2010).
Should have added labels to identify the X,Y and R textboxes, but the project solution is already zipped up, and I'm not going to bother now.
Last edited by passel; Oct 18th, 2015 at 05:04 AM.