Bad tangent creation... all else is fine - vb.net

I have this code I sorta put together from a few different sites.
It works perfect at creating the bitangent... but for what ever reason. the tangent is thrashed.. In other words and to make this more clear, the code is generating BAD tangents!!
The tangent is usually pointing down, kinda opposite the normal. I'm expecting it to be aligned with the V axis of the texture coordinate..
The strange thing is, I use the cross of the normal and tangent to get the bitangent, which is fine.
I have tried flipping the coordinates, the UVs.. just about everything..
Im not great in math so.. can some one take a look at my code and see if it looks "legit"? Thank you!
Private Sub ComputeTangentBasis(ByRef p0 As vertex_data, ByRef p1 As vertex_data, ByRef p2 As vertex_data)
Dim tangent, bitangent As Vector3D
Dim n As Vector3D
n.X = p0.nx
n.Y = p0.ny
n.Z = p0.nz
'convert to vector3d type... they are WAY easier to do complex math with!!
Dim v0 = convert_vector3d(p0)
Dim v1 = convert_vector3d(p1)
Dim v2 = convert_vector3d(p2)
Dim edge1 = v1 - v2
Dim edge2 = v2 - v0
Dim deltaU1 = (p1.u - p0.u) * -0.1
Dim deltaU2 = (p2.u - p0.u) * -0.1
Dim deltaV1 = (p1.v - p0.v) * -0.1
Dim deltaV2 = (p2.v - p0.v) * -0.1
Dim dividend = (deltaU1 * deltaV2) - (deltaU2 - deltaV1)
Dim f As Double
If dividend = 0.0 Then
f = 1.0
End If
tangent.X = f * ((deltaV2 * edge1.X) - (deltaV1 * edge2.X))
tangent.Y = f * ((deltaV2 * edge1.Y) - (deltaV1 * edge2.Y))
tangent.Z = f * ((deltaV2 * edge1.Z) - (deltaV1 * edge2.Z))
bitangent = Vector3D.CrossProduct(tangent, n)
'
p0.t.x = tangent.X
p0.t.y = tangent.Y
p0.t.z = tangent.Z
p0.bt.x = bitangent.X
p0.bt.y = bitangent.Y
p0.bt.z = bitangent.Z
'
p1.t.x = tangent.X
p1.t.y = tangent.Y
p1.t.z = tangent.Z
p1.bt.x = bitangent.X
p1.bt.y = bitangent.Y
p1.bt.z = bitangent.Z
'
p2.t.x = tangent.X
p2.t.y = tangent.Y
p2.t.z = tangent.Z
p2.bt.x = bitangent.X
p2.bt.y = bitangent.Y
p2.bt.z = bitangent.Z
End Sub
Here's a pic of the problem.. Norms are red.. Tangents Green... BiTangents.. blueish.

You will likely need to recalculate the tangent from cross-product of the normal and bitangent. This ensures that the normal, tangent and bitangent are orthagonal to one another.
In most implementations I've seen, the first calculation for a perpendicular axis to the normal is a placeholder as there are an infinite number of possible axes to choose from, and as such requires it to be recalculated to be orthagonal.

In your code you don't take dividend into the calculus, except for the case is 0.
Any two points in a triangle form a tangent or a bitangent. There are infinite possibilities. If you want to align T and B with some texture coordinates you need more maths.
With little effort you would have found this: How to calculate Tangent and Binormal?

Ok... I found 2 problems..
Dim edge1 = v1 - v2 should be Dim edge1 = v1 - v0
I looked in to what Ripi2 commented about.. I watched the video I got this from over again.. I messed a part where he changed the code.. He removed dividend and the if statement and replaced it with this line:
Dim f As Single = 1.0! / ((deltaU1 * deltaV2) - (deltaU2 * deltaV1))
Now it creates prefect Tangents and BiTangents.
I added normalization using these 2 lines.
tangent /= tangent.Length
bitangent /= bitangent.Length
I hope this helps anyone out there.

Related

Is there a way to avoid the error statement "Arithmetic operation resulted in an overflow."?

I've done some research and the general advice is changing the datatype of the variable holding the expression to Long or Ulong but in either case, I'm still getting the same error. I also tried enclosing the expression with CInt() (or CLong()) to force it to cut out it's decimal portion to reduce the length of the output of the expression but neither is working. It's all pretty confusing. Any help will be deeply appreciated. The code block triggering the error is as follows;
Vsf(i) = CInt((((0.91544 - 0.00166 * Angle(i) - 0.000002 * W - 0.054248 *
Superelevation(i) - Sidefrictionfactor) / 0.013939) * Radius(i)) ^ 0.5)
Vro(i) = CInt((((1.04136 - 0.004528 * Angle(i) - 0.000004 * W - 0.338711 * Superelevation(i) - rolloverthreshold) / 0.014578) * Radius(i)) ^ 0.5)
Vmin(i) = CInt(Math.Min(Vsf(i), Vro(i)))
I declared VSf(), Vro() and Vmin() all as integer arrays. I even enclosed the computation expression in a CInt() in hopes that it would convert the result of Vro (which was triggering the arithmetic overflow error) to an integer and hence not have to deal with decimals which would lead to more digits. Yet when I hover over Vro(i), I see a 4 digit integer with decimals. Not sure why that's happening.
I broke the formula down to try and see where the problem was occurring. Everything buzzed along until the very end. If the value to be raised to .5 is a negative number, the square root does not produce a number. I would perform the steps of the formula without the ^.5 and then check for a negative number.
Private Sub OPCode()
Dim Angle = 90.2
Dim Radius = 100.2
Dim Superelevation = 37.8
Dim Sidefrictionfactor = 0.003
Dim W = 0.00325
Dim Vsf As Double
Dim AngleMultiplication = 0.00166 * Angle
Dim WMultiplication = 0.000002 * W
Dim SuperelavationMultiplication = 0.054248 * Superelevation
Dim SubtractResult = 0.91544 - AngleMultiplication - WMultiplication - SuperelavationMultiplication - Sidefrictionfactor
Dim DivisionResult = SubtractResult / 0.013939
Dim MultiplyByRadius = DivisionResult * Radius
Debug.Print(MultiplyByRadius.ToString) 'With my made up values I get a negative number
Vsf = MultiplyByRadius ^ 0.5 'You cannot get the square root of a negative number
Debug.Print(Vsf.ToString) 'NaN
Dim intVsf = CInt(Vsf) 'The Arithematic overflow error occurs here
End Sub

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.

Steering behaviour in XNA not working as expected

I'm reading the following page about steering AI:
http://rocketmandevelopment.com/blog/steering-behaviors-wander/
At the bottom is some code which I'm trying to covert for use in VB.NET + XNA
My code looks like this:
Sub AI()
Dim circleRadius As Single = 6.0F
Dim wanderAngle As Single = 0.0F
Dim wanderChange As Single = 1.0F
Dim enemySpeed As Single = 0.3F
Dim enemyPosistion As Vector2 = (1,1)
Dim circleMiddle As Vector2 = enemyPosistion
circleMiddle.Normalize()
circleMiddle = Vector2.Multiply(circleMiddle, circleRadius)
Dim wanderForce As New Vector2
wanderForce = Vector2.Normalize(wanderForce) * 3 ' Set length of vector
wanderForce = AngleToVector(wanderAngle)
Randomize()
wanderAngle += Rnd() * wanderChange - wanderChange * 0.5
Dim force As New Vector2
force = Vector2.Add(circleMiddle, wanderForce)
enemyPosistion += force * enemySpeed
End Sub
Private Function AngleToVector(angle As Single) As Vector2
Return New Vector2(CSng(Math.Sin(angle)), -CSng(Math.Cos(angle)))
End Function
I realised I made a simple mistake by setting enemyPosistion to Vector2.Zero, instead I set it to (1,1) and it makes the enemy fly up and to the right. I have included a video:
https://www.youtube.com/watch?v=UZubNaEA9W0
This is more along the lines of what it should do:
https://www.youtube.com/watch?v=1wfgPCMdW2U
Can anybody tell me what I'm doing wrong?
You need to preserve some state across frames. At least the wanderAngle and a velocity vector. Call Randomize() only once at the beginning of the game. circleMiddle should be velocity * circleRadius. velocity should be updated at the end with circleMiddle + wanderForce and enemyPosition += velocity * enemySpeed. Honestly, this whole code looks a bit strange, mixing up some physical concepts. Forces affect a body's acceleration (together with mass), acceleration affects its velocity (together with a time step) and the velocity affects its position (together with a time step). - Nico Schertler

Need to determine if a list of points are all part of the same circle in VB.net

VB.NET
My data is coming from an exported DXF file from various cad packages. When a circular arc (defined as a part of a true circle) is exported it is sometimes exported as a bunch of line segments rather than a circular arc.
I have a list of points and I am trying to guess if they were derived from the same circle. Basically I iterate through all the points and use a method to find the center point of a circle from three points. My intention was to compare all the calculated center points generated and make a determination if they are close to each other.
My first thought was that I could check to see if the center points are all equal but they have slight differences between them because of rounding and the underlying estimating routine that generates the point in the first place (I have no control over that).
MY second was to check the standard deviation of the x and y values of the circumference points and compare that against the standard deviation of the x,y of the centers and make some judgement from that. VB.net does not seem to have a native stdev function and I am sometimes a bit lazy.
Does anybody have a simple idea on how to determine if a list of points are all from the same circle?
Here are my functions:
To determine the center of a circle given three points:
Public Function getCenter(p1 As Point2D, p2 As Point2D, p3 As Point2D) As Point2D
Dim yDelta_a As Double = p2.Y - p1.Y
Dim xDelta_a As Double = p2.X - p1.X
Dim yDelta_b As Double = p3.Y - p2.Y
Dim xDelta_b = p3.X - p2.X
Dim center As New Point2D
Dim aSlope As Double = yDelta_a / xDelta_a
Dim bSlope As Double = yDelta_b / xDelta_b
center.X = (aSlope * bSlope * (p1.Y - p3.Y) + bSlope * (p1.X + p2.X) - aSlope * (p2.X + p3.X)) / (2 * (bSlope - aSlope))
center.Y = -1 * (center.X - (p1.X + p2.X) / 2) / aSlope + (p1.Y + p2.Y) / 2
Return center
End Function
And then to iterate the list of points and get a collection of centers. FYI...This function received a list of lines that have endpoints that are points so I do a bit of iterating to get all the correct points.
Public Function MakesCircle(lines As List(Of Line))
Dim points As New List(Of Point2D)
If lines.Count < 2 Then
Return False
Else
//Get points from lines
For i As Int16 = 0 To lines.Count - 2
points.Add(lines(i).StartPoint)
Next
points.Add(lines.Last.StartPoint)
End If
//"Prime the pump" for the center calculation loop
Dim centers As New List(Of Point2D)
Dim a As Point2D = points(0)
Dim b As Point2D = points(1)
Dim c As Point2D = points(2)
//Calc all the centers
For i As Int16 = 3 To lines.Count - 1
centers.Add(getCenter(a, b, c))
a = b
b = c
c = points(i)
Next
//This is where I need logic to determine if the points all actually belong to the same circle
Return True
End Function
You can use a GraphicsPath object to find this out. -not tested-
I figured you would be able to construct a Rectangle Structure based on the data coming in (x,y,w,h) then this make-shift algorithm would do for you.
Private Function areAllPointsInEllipsis(ellipsis As Rectangle, pts() As Point) As Boolean
Dim result As Boolean
Using gp As New System.Drawing.Drawing2D.GraphicsPath
gp.AddEllipsis(ellispsis)
result = pts.All(Function(pt) gp.IsVisible(pt))
End Using
Return result
End Function
What I did was generate all the distances from the average center point to each of the points that make up the circumference. Find the max and min distances. They you can use a threshold of a percentage of the radius - a kind of eccentricity measurement. If the difference between the max and min falls below 1% of the radius then I call it a circle.