Visual Basic - Making a Line Chart - vb.net

This is my first post on stackoverflow so sorry in advance for anything I may have messed up, but for a project I am doing a simulator for projectile motion. I am only a beginner programmer and dont have much experience with charts, but there aren't any youtube videos that I find helpful.
The chart is plotting vertical displacement against horizontal displacement, and should look like an inverted parabola but it doesn't look anything like what it should.
I am struggling to set axis intervals correctly, each time the line should start from the origin but the x axis contains negative values for displacement, something which should definitely not be occurring. Each time a different simulation is done the axis should change so that each interval in the x axis is something along the lines of a tenth of the total range of the projectile.
Anything that could point me in the right direction would be incredibly useful.
Code:
Public Sub CreateDiagram(ByVal flightRange As Double, ByVal totalTime As Double, ByVal velocity
As Double, ByVal angle As Double, ByVal elevation As Double, ByVal heightOfProjectile As Double)
Chart1.Titles.Add("Projectile Motion")
Chart1.ChartAreas.Clear()
Chart1.ChartAreas.Add("Default")
Dim xAxisInterval As Integer = CInt(totalTime / 10)
Dim yAxisInterval As Integer = CInt(heightOfProjectile / 10)
With Chart1.ChartAreas("Default")
.AxisX.Interval() = xAxisInterval
.AxisY.Interval() = yAxisInterval
End With
Chart1.Series.Add("projection")
Chart1.Series("projection").Color = Color.Black
Chart1.Series("projection").ChartType = DataVisualization.Charting.SeriesChartType.Line
For x = 0 To totalTime Step 0.1
Dim xPos As Double = findXLocation(velocity, angle, x)
Dim yPos As Double = findYLocation(velocity, angle, x, elevation)
Chart1.Series("projection").Points.AddXY(xPos, yPos)
Next
End Sub
EDIT: findXLocation and findYLocation code:
Public Function findYLocation(ByVal velocity As Double, ByVal angle As Double, ByVal time As Double, ByVal elevation As Double) As Double
Dim y As Double
y = elevation + findVerticalVelocity(velocity, angle) - ((0.5 * gConstant) * (time ^ 2))
y = Math.Round(y, 1)
If y < 0 Then
y = 0
End If
Return y
End Function
Public Function findXLocation(ByVal velocity As Double, ByVal angle As Double, ByVal time As Double) As Double
Dim x As Double
x = findHorizontalVelocity(velocity, angle) * time
x = Math.Round(x, 1)
If x < 0 Then
x = 0
End If
Return x
End Function

If you want to explicitly set the minimum value of the X Axis you can use Axis.Minimum. In your case you would use it as Chart1.ChartAreas("Default").AxisX.Minimum = SomeValue
Note that when you set the minimum in this way the axis will stop automatically rescaling.
If you want the minimum to always be zero when all x-values are positive, have a look at Axis.IsStartedFromZero
I hope i understood your question correctly because you mention intervals, but seem to be talking about the minimum value of your x axis.
As a further comment on your code unrelated to the question; it is unnecessary and inefficient to remove the ChartArea every time you want to update the graph, same goes for the Series. Doing this will result in having to re-format the chart on every update (like you are in fact doing with the series).
It suffices to call YourSeries.Points.Clear() and to then add the new points. This way you don't have to re-specify the series color and chartype on each update.
EDIT:
I also see that you round the x position to one decimal, presumably to prevent the x-axis from having long ugly numbers on it. However, a better way to do this is to specify a format for the axis:
chart1.ChartAreas.FirstOrDefault.AxisX.LabelStyle.Format = "{0:0.0}" in your case. This way your series will still store the exact value, but only one decimal will be displayed.

Related

How to fit straight line to variable curve and determining the x-intercept

I'm trying to figure out how to code a straight line to the straight part of a curve, the curve should look something like the exponential, click the link to open the image:
Straight line to Curve and determining the x-intercept
Here is the code, I'm only using the exponential as an example
`
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim s As String
Dim xl As New Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
wb = xl.Workbooks.Add 'create new workbook
ws = wb.Worksheets(1) 'select sheet 1
ws.Activate()
Dim Currents() As Double
Dim PhotodiodeValues() As Double
Dim AppliedCurrent As Double
'AppliedCurrent = SerialPort1.ReadLine
AppliedCurrent = 0
If AppliedCurrent >= 0 And AppliedCurrent < 0.1 Then
Dim j As Integer = 1
For i As Double = 0 To 5 Step 0.5
ReDim Preserve Currents(j)
ReDim Preserve PhotodiodeValues(j)
MsgBox(i)
MsgBox("LDI " & CType(i, String))
s = ("LDI " & CType(i, String))
AppliedCurrent = i
If AppliedCurrent >= i And AppliedCurrent < (i + 0.1) Then
Currents(j) = CType(i, Double)
Label1.Text = Currents(j)
PhotodiodeValues(j) = CType(Math.E ^ (i), Double)
ws.Cells(j, 1) = Currents(j)
ws.Cells(j, 2) = PhotodiodeValues(j)
Else
System.Threading.Thread.Sleep(1000)
End If
j = j + 1
Next
Else
System.Threading.Thread.Sleep(1000)
End If
sfd1.ShowDialog() 'get file name
wb.SaveAs(sfd1.FileName) 'save data to file
wb.Close()
xl = Nothing 'dispose of excel
ScatterGraph1.PlotXY(Currents, PhotodiodeValues)
'SerialPort1.Close()
End Sub
End Class`
First off, I'll explain my thought process. If I have misunderstood, please let me know and I will update my answer. The slope dy/dx of the curve y = e^x is dy/dx = e^x, a monotonically increasing function of x for all real x. There is never a point at which the function becomes linear and, while it has a horizontal asymptote (y = 0) it has no vertical asymptote.
I take it that what you want is the equation of a tangent line taken at a point where the slope first becomes greater than some cutoff value m*. After that point, the graph of y = e^x "might as well" be a straight line for your intents and purposes.
So, we must first solve the equation m* = dy/dx = e^x for the x at which m* occurs. The range of e^x is all positive real numbers and e^x is monotonically increasing, so any positive real number m* will have a unique solution x*. indeed, x* = ln(m*). Our tangent line will pass through the point (x*, e^x*) and have slope m*. Recall that m* = e^x*, so the point is (ln(m*), m*) and the slope is m*.
With a point and the slope, we can figure out the equation of a line. We have that the slope from the given point to any other point must be m*; so, (y - y*)/(x - x*) = m*. Rearranging, (y - y*) = m*(x - x*), y = mx - mx* + y*, and finally y = mx + (y - mx) = mx + (m - mln(m)). The Y-intercept is therefore (m* - mln(m)). We can get the X-intercept by setting y = 0 and solving for x: 0 = mx + (m - mln(m)), mx = mln(m*) - m*, x = ln(m*) - 1.
In summary:
the equation of the line tangent to y = e^x with slope m* is y = mx + (m - mln(m)).
the Y-intercept of this line is (m* - mln(m)).
the X-intercept of this line is ln(m*) - 1
If the curve is known at compile time, I recommend hard-coding a closed form analytical solution for the derivative and any asymptotes. If the function is not known until runtime, the derivative at a given point can be approximated numerically using a variety of methods. Intuitively, the definition of the derivative as the limit of (f(x+d) - f(x)) / d as d approaches zero can be used to find approximations of the derivative where the derivative (likely) exists. For well-behaved analytic functions, you will typically be safe except in special cases.
If the function's derivative is monotonically non-decreasing, as in this example, you can find the point (if any) at which the function's slope meets or exceeds a certain cutoff using approximation (as above) in conjunction with something akin to binary search. Start at a value such as x = 0, and increase or decrease x by some multiplicatively increasing factor until you have passed your target. Now, with bounds on the values of x between which your target can be found, check the middle of the range, and then either the left or right half recursively until a suitably good x* is found.

3D rotation of XYZ coordinates - Wrong results in certain angles?

Please bear with me I´m a newbie in programming and I´m trying to learn how to rotate a 3D point (XYZ) around 0,0,0 and later I´ll try to improve my code to allow rotation around an arbitrary point (XYZ).
I´m starting with VB and after extensive searches here and in the Web, I could not find an explanation for my problem. I´m an almost 40 years old trying to learn math and programming, so please bear with me because it will take time for me to digest all the math side for these problems.
Basically, I´m trying to write an algorithm to rotate a 3D point, however, while it seems that with some angles my code works, with some others I just get weird funky numbers that are probable correct in some aspect, but I can´t find the flaw in the code. I´ve been looking into this for days and tried multiple approaches, but I´m just not being able to spot the error.
This is the UI for my little app. The original coordinates are entered in the top of the form, and in the bottom of the form I show the rotated coordinates.
Notice that in the image below, a simple rotation of a coordinate of Y10.0 around Z axis by 90 degrees return a correct X value (-10), but Y shows a funky number (6.1230...)... However if I change the rotation angle around Z to 45, the results seems to be correct...
I don´t know what I´m doing wrong to get this weird Y. Because of this error, I´m not trusting in the results of this algorithm at all but I´m currently in a blindspot...
This is the code of the calculate button:
Private Sub BtnCompute_Click(sender As Object, e As EventArgs) Handles BtnCompute.Click
'Capture the values from the text boxes and parse then to doubles
ValidateAllFieldsWithDoubleValues()
'Rotate the coordinates
RotateXYZCoordinates(dblOriginalCoordX, dblOriginalCoordY, dblOriginalCoordZ, dblCurrentRotationAroundX, dblCurrentRotationAroundY, dblCurrentRotationAroundZ)
'Update the text boxes for the rotated coordinates for XYZ
txtResultX.Text = dblResultX.ToString
txtResultY.Text = dblResultY.ToString
txtResultZ.Text = dblResultZ.ToString
End Sub
And this is the code of the function that calculates the rotations:
Private Function RotateXYZCoordinates(ByVal XCoord As Double, ByVal YCoord As Double, ByVal ZCoord As Double, ByVal Pitch As Double, ByVal Roll As Double, ByVal Yaw As Double)
'X Rotation
Dim RadPitch As Double = 0
Dim CosPitch As Double = 0
Dim SinPitch As Double = 0
Dim XRotatedAroundX As Double = 0
Dim YRotatedAroundX As Double = 0
Dim ZRotatedAroundX As Double = 0
RadPitch = Pitch * Math.PI / 180
CosPitch = Math.Cos(RadPitch)
SinPitch = Math.Sin(RadPitch)
XRotatedAroundX = XCoord
YRotatedAroundX = YCoord * CosPitch - ZCoord * SinPitch
ZRotatedAroundX = YCoord * SinPitch + ZCoord * CosPitch
'Y Rotation
Dim RadRoll As Double = 0
Dim CosRoll As Double = 0
Dim SinRoll As Double = 0
Dim XRotatedAroundY As Double = 0
Dim YRotatedAroundY As Double = 0
Dim ZRotatedAroundY As Double = 0
RadRoll = Roll * Math.PI / 180
CosRoll = Math.Cos(RadRoll)
SinRoll = Math.Sin(RadRoll)
XRotatedAroundY = ZRotatedAroundX * CosRoll - XRotatedAroundX * SinRoll
YRotatedAroundY = YRotatedAroundX
ZRotatedAroundY = ZRotatedAroundX * SinRoll + XRotatedAroundX * CosRoll
'Z Rotation
Dim RadYaw As Double = 0
Dim CosYaw As Double = 0
Dim SinYaw As Double = 0
Dim XRotatedAroundZ As Double = 0
Dim YRotatedAroundZ As Double = 0
Dim ZRotatedAroundZ As Double = 0
RadYaw = Yaw * Math.PI / 180
CosYaw = Math.Cos(RadYaw)
SinYaw = Math.Sin(RadYaw)
XRotatedAroundZ = XRotatedAroundY * CosYaw - YRotatedAroundY * SinYaw
YRotatedAroundZ = XRotatedAroundY * SinYaw + YRotatedAroundY * CosYaw
ZRotatedAroundZ = ZRotatedAroundY
'Final result
dblResultX = XRotatedAroundZ
dblResultY = YRotatedAroundZ
dblResultZ = ZRotatedAroundZ
Return Nothing
End Function
I know this is not an elegant code but it is what I can code for now... I´d appreciate if someone could take a look at this and point me to the source of error... I´ve been watching videos and did an extensive search in this website before I posted... But it seems some things are still very advanced to me for now... I´m not lazy and I´m willing to learn if someone point me towards something I could digest for now...
If someone could share a hint about how to make this rotate function to support rotation around a point other than 0,0,0 I´d appreciate.
Thank you,
Daniel
The answer is correct. Due to double precision math and a 90 degree rotation there is a limit to the accuracy. The answer is really 6.12303176911189E-16 or .000000000000000612303176911189. Round the number off to a realistic value of decimal points. This is also why 1+1 is not equal to 2 but 1.999999999999999999999999999999 in floating point math.

Debugging Loop for numerical iterations

I was creating a critical value approximator of American style options. I was getting the error "#Value!" only after around 40 loops (kept track with a counter).
After some trial and error I realized it came from the part of the loop calling the BlackScholes pricing function. In theory I want to run through a range of values iteratively for the spot price while keeping the other variables fixed in a Black Scholes European price calculation. After tinkering around I reduced the issue to the fact that after the first loop it was no longer calculating Black Scholes the way it would if I just used the value on that iteration and the value I was getting was just increasing by 1, then crapping out after 40 loops of wrong values for some non obvious reason.
So below I truncated the code to a very simple skeleton which is the essence of my problem. Any help would be appreciated.
Function Looper(S As Double, K As Double, r As Double, t As Double, q As Double, Vol As Double) As Double
Dim i As Double
For i = 100 To 150 Step 1#
MsgBox i
MsgBox BS(i, K, r, t, q, Vol, "Call") 'After the first loop the values are wrong,
'What I'd like is, BS(100,...), BS(101,...),BS(102,...) which it is not.
'Not sure what it's actually calculating, since the values are way off
Next i
End Function
Public Function BS(S As Double, K As Double, r As Double, t As Double, q As Double, Vol As Double, CP As String) As Double
Dim volrootime As Double
Dim d1 As Double
Dim d2 As Double
Dim DiscF As Double
Dim DivF As Double
Dim topline1 As Double
Dim topline2 As Double
Dim topline As Double
Dim Price As Double
t = t / 365
r = r / 100
q = q / 100
DiscF = Exp(-r * t)
DivF = Exp(-q * t)
volrootime = (t ^ 0.5) * Vol
topline1 = Log(S / K)
topline2 = ((r - q) + ((Vol ^ 2) / 2)) * t
topline = topline1 + topline2
d1 = topline / volrootime
d2 = d1 - volrootime
If CP = "Call" Then
' Theta is in terms of Calendar days, changing the denominator to 252 changes it to trading days
Price = (S * DivF * Bign(d1)) - (K * DiscF * Bign(d2))
Else
' Theta is in terms of Calendar days, changing the denominator to 252 changes it to trading days
Price = K * DiscF * Bign(-d2) - S * DivF * Bign(-d1)
End If
BS = Price
End Function
The values of r, t, q change each time the BS function is called. If they must stay constant, you should use ByVal in the BS function declaration like this:
BS(S As Double, K As Double, ByVal r As Double, ByVal t As Double, ByVal q As Double, ...
By default, the parameters are passed by reference and any change in the called function are reflected in the calling function.
By the way, in this example, I wouldn't use messageboxes when debugging but instead use debug.print like this:
Debug.Print "i=" & i & vbTab & "BS=" & BS(i, K, r, t, q, Vol, "Call")
The print is made in the window opened by pressing Ctl + G (Go To).

Calculation of minimum distance between coordinate points

I am having a problem in building up VBcode for getting the minimum distance between a set of coordinate points. What I am actually trying is to find the minimum distance between one set of coordinate points (A(x1,y1),Bx2,y2),C(x3,y3),D(x4,y4),E(x5,y5)) with respect to another set of coord points (i(x1,y1),j(x2,y2),k(x3,y3),l(x4,y4),m(x5,y5)).
I hope you understand what I am trying to interpret.
Can anybody help me out?
Public Function DoSearch(ByVal SearchCritera As Bracket, ByVal ListToSearchFrom As System.Collections.Generic.List(Of TRacksDefinitions.Racks.Bracket)) As System.Collections.Generic.List(Of TRacksDefinitions.Search.SearchBracket) Implements TRacksDefinitions.Search.ISearch.DoSearch
_results.Clear()
For Each b As Bracket In ListToSearchFrom
'LAST POINT DISTANCE., WT DIST, number of points, similarity of points (-1 if not used),
Dim dist() As Double = {0, 0, 0, 0, 0}
Dim dx As Double = b.RearPoints(b.RearPoints.Length - 2).X - SearchCritera.RearPoints(SearchCritera.RearPoints.Length - 2).X
Dim dy As Double = b.RearPoints(b.RearPoints.Length - 2).Y - SearchCritera.RearPoints(SearchCritera.RearPoints.Length - 2).Y
Dim dz As Double = b.RearPoints(b.RearPoints.Length - 2).Z - SearchCritera.RearPoints(SearchCritera.RearPoints.Length - 2).Z
dist(0) += Math.Sqrt(dx ^ 2 + dy ^ 2 + dz ^ 2)
dist(1) += Math.Abs(SearchCritera.Wallthickness - b.Wallthickness)
dist(2) += Math.Abs(SearchCritera.RearPoints.Count - b.RearPoints.Count)
If SearchCritera.RearPoints.Count = b.RearPoints.Count Then
Dim d1, d2 As Decimal
' Dim sum As Double = 0
For i As Integer = 0 To b.RearPoints.Count - 1
d1 = Math.Abs(SearchCritera.RearPoints(i).X - b.RearPoints(i).X)
d2 = Math.Abs(SearchCritera.RearPoints(i).Y - b.RearPoints(i).Y)
?????????????????
Next
Else
dist(3) = -1
End If
#LarsTech
The above is code which I have created until now and the next step is calculating the min distance criteria.
Search criteria: rear points is the one we get from solidworks and b.rearpoints is the one present in database we compare both of them find the one which is very similar to the one in database.
You need a distance formula:
Public Function GetDistance(ByVal startPoint As Point, ByVal endPoint As Point) As Integer
Return Math.Sqrt((Math.Abs(endPoint.X - startPoint.X) ^ 2) + _
(Math.Abs(endPoint.Y - startPoint.Y) ^ 2))
End Function
Then you just need to loop through all of your points to find which distance is the smallest:
Dim listOne As New List(Of Point)
listOne.Add(New Point(10, 10))
listOne.Add(New Point(20, 20))
listOne.Add(New Point(30, 30))
listOne.Add(New Point(40, 40))
listOne.Add(New Point(50, 50))
Dim listTwo As New List(Of Point)
listTwo.Add(New Point(50, 10))
listTwo.Add(New Point(50, 20))
listTwo.Add(New Point(50, 30))
listTwo.Add(New Point(50, 40))
'listTwo.Add(New Point(50, 50))
Dim minDistance As Nullable(Of Integer)
For Each p1 As Point In listOne
For Each p2 As Point In listTwo
Dim distance As Integer = GetDistance(p1, p2)
If minDistance Is Nothing OrElse distance < minDistance Then
minDistance = distance
End If
Next
Next
MessageBox.Show("Minimum Distance = " & minDistance.ToString)
In two dimensions, depending upon the distribution of the points, if N exceeds 9, it may make sense to partition the space into int(sqrt(N)-1)^2 identically-sized "boxes". If any box contains more than a hundred or so points, randomly pick ten or so points from the most populous box, find the smallest distance between any pair, and then repartition with a box size of either that smallest distance or a box size reduced by a factor of int(sqrt(k)-1) where k is the population of that box [if a box contains that many points, there must be two within that fraction of the box size.
In the worst case this algorithm could perform badly, since there could be a very dense concentration of points that somehow never get arbitrarily chosen for the distance test, and it could take awhile before the space gets subdivided finely enough to separate them. In practice, however, the points would likely get chosen at some point, allowing the collection to get quickly subdivided.

Calculate angle of two points on Line chart

I want to get the angle of two points on a Line chart.
I know how to calculate an angle, the problem is that I need the x and y of the seriescollection.point and I have no idea how to get it.
Can someone help me with it?
EDIT:
Jean-François Corbett showed me how to get the points, I meant from top and left, and not point on the graph (on X scale and Y scale) though it can work.
I calculate it wrong. how can I calculate the angles in the picture below?
You ask how to get the (x,y) coordinates of points in a chart series. Here is how:
Dim c As Chart
Dim s As Series
Dim x As Variant
Dim y As Variant
Set c = ActiveChart
Set s = c.SeriesCollection.Item(1)
x = s.XValues
y = s.Values
EDIT As far as I can tell from the edited question, OP now wants the pixel coordinates of each point, with origin at the top left of the plot. To do so, you just need to scale by the axis width and span. The x axis is a bit tricky in the case of line plots (which I hate), because there is no min or max scale property; have to use the number of "categories" instead. The following code does this scaling:
Dim c As Chart
Dim s As Series
Dim xa As Axis
Dim ya As Axis
Dim x As Variant
Dim y As Variant
Dim i As Long
Set c = ActiveChart
Set s = c.SeriesCollection.Item(1)
Set xa = c.Axes(xlCategory)
Set ya = c.Axes(xlValue)
x = s.XValues
y = s.Values
For i = LBound(x) To UBound(x)
' Scale x by number of categories, equal to UBound(x) - LBound(x) + 1
x(i) = (i - LBound(x) + 0.5) / (UBound(x) - LBound(x) + 1) * xa.Width
' Scale y by axis span
y(i) = ya.Height - y(i) / (ya.MaximumScale - ya.MinimumScale) * ya.Height
Next i
Note that y increases along the negative y direction on the plot, since you want the origin to be at the top left.
Using this x and y, you can calculate your angle as seen on the screen.
The X and Y values are not directly accessible from the Point object, (as best as I can tell), but they represent actual values passed to the graph. Try accessing them from the worksheet where they are stored.
If that is unavailable, try Series.values, which returns an array of Y-values, and Series.XValues, which returns an array of X-values. (See MSDN Reference)