determine clockwise or counterclockwise rotation between 2 lines - vba

I have a piece of code that takes 4 points (which makes up 2 lines), and determines the angle between them. How can I programmatically determine if this is a clockwise rotation or counter-clockwise rotation?
Private Function calculateAngleAlt(L1X1 As Double, L1Y1 As Double, L1X2 As Double, L1Y2 As Double, L2X1 As Double, L2Y1 As Double, L2X2 As Double, L2Y2 As Double) As Double
line1A = L1X2 - L1X1
line1B = L1Y2 - L1Y1
line2A = L2X2 - L2X1
line2B = L2Y2 - L2Y1
lineDot = (line1A * line2A) + (line1B * line2B)
distL1 = Abs(Sqr((line1A * line1A) + (line1B * line1B)))
distL2 = Abs(Sqr((line2A * line2A) + (line2B * line2B)))
calculateAngleAlt = ArcCos(lineDot /(distL1 * distL2))
End Function
Thanks!

Related

Calculating distance in kilometers between coordinates

I'm trying to calculate distance in kilometers between two geographical coordinates using the haversine formula.
Code:
Dim dbl_dLat As Double
Dim dbl_dLon As Double
Dim dbl_a As Double
dbl_P = WorksheetFunction.Pi / 180
dbl_dLat = dbl_P * (dbl_Latitude2 - dbl_Latitude1)
dbl_dLon = dbl_P * (dbl_Longitude2 - dbl_Longitude1)
dbl_a = Sin(dbl_dLat / 2) * Sin(dbl_dLat / 2) + Cos(dbl_Latitude1 * dbl_P) * Cos(dbl_Latitude2 * dbl_P) * Sin(dbl_dLon / 2) * Sin(dbl_dLon / 2)
dbl_Distance_KM = 6371 * 2 * WorksheetFunction.Atan2(Sqr(dbl_a), Sqr(1 - dbl_a))
I'm testing with these coordinates:
dbl_Longitude1 = 55.629178
dbl_Longitude2 = 29.846686
dbl_Latitude1 = 37.659466
dbl_Latitude2 = 30.24441
And the code returns 20015.09, which is obviously wrong. It should be 642 km according to Yandex maps.
Where am I wrong? Are the longitude and latitude in wrong format?
As far as I can tell, the issue is that the order of arguments to atan2() varies by language. The following works* for me:
Option Explicit
Public Sub Distance()
Dim dbl_Longitude1 As Double, dbl_Longitude2 As Double, dbl_Latitude1 As Double, dbl_Latitude2 As Double
dbl_Longitude1 = 55.629178
dbl_Longitude2 = 29.846686
dbl_Latitude1 = 37.659466
dbl_Latitude2 = 30.24441
Dim dbl_dLat As Double
Dim dbl_dLon As Double
Dim dbl_a As Double
Dim dbl_P As Double
dbl_P = WorksheetFunction.Pi / 180
dbl_dLat = dbl_P * (dbl_Latitude2 - dbl_Latitude1) 'to radians
dbl_dLon = dbl_P * (dbl_Longitude2 - dbl_Longitude1) 'to radians
dbl_a = Sin(dbl_dLat / 2) * Sin(dbl_dLat / 2) + _
Cos(dbl_Latitude1 * dbl_P) * Cos(dbl_Latitude2 * dbl_P) * Sin(dbl_dLon / 2) * Sin(dbl_dLon / 2)
Dim c As Double
Dim dbl_Distance_KM As Double
c = 2 * WorksheetFunction.Atan2(Sqr(1 - dbl_a), Sqr(dbl_a)) ' *** swapped arguments to Atan2
dbl_Distance_KM = 6371 * c
Debug.Print dbl_Distance_KM
End Sub
*Output: 2507.26205401321, although gcmap.com says the answer is 2512 km. This might be a precision issue --- I think it's close enough to count as working. (Edit it might also be that gcmap uses local earth radii rather than the mean radius; I am not sure.)
Explanation
I found this description of the haversine formula for great-circle distance, which is what you are implementing. The JavaScript implementation on that page gives this computation for c:
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
In JavaScript, atan2() takes parameters y, x. However, in Excel VBA, WorksheetFunction.Atan2 takes parameters x, y. Your original code passed Sqr(dbl_a) as the first parameter, as it would be in JavaScript. However, Sqr(dbl_a) needs to be the second parameter in Excel VBA.
A comment on naming
Building on #JohnColeman's point, there are lots of ways to name variables. In this case, I would recommend using the prefixes for unit rather than for type: e.g., deg_Latitude1, RadPerDeg = Pi/180, and rad_dLat = RadPerDeg * (deg_Latitude2 - deg_Latitude1). I personally think that helps avoid unit-conversion mishaps.
My VBA code that returns the answer in feet; However 'd' is the answer in kilometers.
Imports System.Math
Module Haversine
Public Function GlobalAddressDistance(sLat1 As String, sLon1 As String, sLat2 As String, sLon2 As String) As String
Const R As Integer = 6371
Const cMetersToFeet As Single = 3.2808399
Const cKiloMetersToMeters As Integer = 1000
Dim a As Double = 0, c As Double = 0, d As Double = 0
'Convert strings to numberic double values
Dim dLat1 As Double = Val(sLat1)
Dim dLat2 As Double = Val(sLat2)
Dim dLatDiff As Double = DegreesToRadians(CDbl(sLat2) - CDbl(sLat1))
Dim dLonDiff As Double = DegreesToRadians(CDbl(sLon2) - CDbl(sLon1))
a = Pow(Sin(dLatDiff / 2), 2) + Cos(DegreesToRadians(dLat1)) * Cos(DegreesToRadians(dLat2)) * Pow(Sin(dLonDiff / 2), 2)
c = 2 * Atan2(Sqrt(a), Sqrt(1 - a))
d = R * c
'Convert kilometers to feet
Return Format((d * cKiloMetersToMeters * cMetersToFeet), "0.##").ToString
End Function
Private Function DegreesToRadians(ByVal dDegrees As Double) As Double
Return (dDegrees * PI) / 180
End Function
End Module

Open TK determine sphere (quadstrips) normals?

Introduction
I am somewhat new to using Open GL / Open TK. I have learned how to draw basic shapes, use matrices, lighting, shadowing, etc. I have a function that draws a sphere:
Private Sub drawSphere(r As Double, lats As Integer, longs As Integer)
Dim i As Integer, j As Integer
For i = 0 To lats
Dim lat0 As Double = PI * (-0.5 + CDbl(i - 1) / lats)
Dim z0 As Double = Sin(lat0)
Dim zr0 As Double = Cos(lat0)
Dim lat1 As Double = PI * (-0.5 + CDbl(i) / lats)
Dim z1 As Double = Sin(lat1)
Dim zr1 As Double = Cos(lat1)
GL.Begin(PrimitiveType.QuadStrip)
For j = 0 To longs
Dim lng As Double = 2 * PI * CDbl(j - 1) / longs
Dim x As Double = Cos(lng)
Dim y As Double = Sin(lng)
GL.Normal3(x * zr0 * r, y * zr0 * r, z0 * r)
GL.Vertex3(x * zr0 * r, y * zr0 * r, z0 * r)
GL.Normal3(x * zr1 * r, y * zr1 * r, z1 * r)
GL.Vertex3(x * zr1 * r, y * zr1 * r, z1 * r)
Next
GL.End()
Next
End Sub
I have other code that sets up the lights. I know the other code works because I have a separate function for drawing an STL object:
Dim texture As UInteger() = New UInteger(0) {}
Dim i As Integer = 0
If stl_table.Items.Count > 0 Then
find_center_of_part()
GL.Begin(PrimitiveType.Triangles)
GL.Color3(part_color.R, part_color.G, part_color.B)
Do Until i + 4 >= stl_table.Items.Count
GL.Normal3(Convert.ToSingle(stl_table.Items.Item(i).SubItems(0).Text), Convert.ToSingle(stl_table.Items.Item(i).SubItems(1).Text), Convert.ToSingle(stl_table.Items.Item(i).SubItems(2).Text))
GL.Vertex3(stl_table.Items.Item(i + 1).SubItems(0).Text - avgx, stl_table.Items.Item(i + 1).SubItems(1).Text - avgy, stl_table.Items.Item(i + 1).SubItems(2).Text - avgz)
GL.Vertex3(stl_table.Items.Item(i + 2).SubItems(0).Text - avgx, stl_table.Items.Item(i + 2).SubItems(1).Text - avgy, stl_table.Items.Item(i + 2).SubItems(2).Text - avgz)
GL.Vertex3(stl_table.Items.Item(i + 3).SubItems(0).Text - avgx, stl_table.Items.Item(i + 3).SubItems(1).Text - avgy, stl_table.Items.Item(i + 3).SubItems(2).Text - avgz)
i = i + 4
Loop
GL.End()
End If
This second function basically imports a CAD STL file and draws it as triangles. The normal vectors are simply an input from the CAD file (so they are already computed). This method's lighting works perfectly fine which makes me know my lighting code is correct.
Problem
The problem is that my sphere is not getting light correctly. I know through testing that this is due to my normal vectors.
With the current code, my sphere looks like this:
There is a "spot" of light which makes me think that is simply one of the quadstrips having the normal correct.
Does anybody have any suggestions on setting up the normal vectors correctly inside my function? Also before anybody suggests it, I can't use GLU or GLUT for what I am trying to accomplish, which is why I need the sphere function.

Calculate the azimuth between two points given the latitude and longitude are known with VBA

I have to calculate the azimuth between two points given in latitude and longitude
is this fynction correct?
Function azimut(lat1, lat2, lon1, lon2)
azimut = WorksheetFunction.Degrees(WorksheetFunction.Atan2(
Cos(Application.WorksheetFunction.Radians(lat1)) *
Sin(Application.WorksheetFunction.Radians(lat2)) -
Sin(Application.WorksheetFunction.Radians(lat1)) *
Cos(Application.WorksheetFunction.Radians(lat2)) *
Cos(Application.WorksheetFunction.Radians(lon2 - lon1)),
Sin(Application.WorksheetFunction.Radians(lon2 - lon1)) *
Cos(Application.WorksheetFunction.Radians(lat2))))
End Function
Assuming your formula is correct (since I interpret it to the code below without checking it), then here is the code:
Function Azimuth(lat1 As Single, lat2 As Single, lon1 As Single, lon2 As Single) As Single
Dim X1 As Single, X2 As Single, Y As Single, dX As Single, dY As Single
With Application.WorksheetFunction
X1 = .Radians(lat1)
X2 = .Radians(lat2)
Y = .Radians(lon2 - lon1)
End With
dX = Math.Cos(X1) * Math.Sin(X2) - Math.Sin(X1) * Math.Cos(X2) * Math.Cos(Y)
dY = Math.Cos(X2) * Math.Sin(Y)
With Application.WorksheetFunction
Azimuth = .Degrees(.Atan2(dX, dY))
End With
End Function
Well, even if the formula turns out to be incorrect, at least the code above should give you the idea to start with.

Rotating Shapes when arranging them into circle ppt vba

I found a great script to arrange objects (shapes) into a circle here:
Aligning Shapes in a Circle using VBA, Microsoft Community
Sub Test()
Call AlignShapesInCircle(720 / 2, 540 / 2, 100, ActiveWindow.Selection.ShapeRange)
End Sub
Function AlignShapesInCircle(x As Single, y As Single, r As Single, shprng As ShapeRange)
'x,y = center point of the circle
'r = radius of the circle
'shprng = the shape selection that needs to be arranged
Dim angle As Single
Dim currentangle As Single
Dim x1 As Single
Dim y1 As Single
Dim i As Integer
currentangle = 0
angle = 360 / shprng.count
For currentangle = 0 To 359 Step angle
i = i + 1
x1 = r * Cos(D2R(currentangle))
y1 = r * Sin(D2R(currentangle))
shprng(i).Left = x + x1
shprng(i).Top = y + y1
Next
End Function
Function D2R(Degrees) As Double
D2R = Degrees / 57.2957795130823
End Function
Function R2D(Radians) As Double
R2D = 57.2957795130823 * Radians
End Function
Now I want the shapes to rotate so that if I use arrows the tip will always show towards the center.
I have to introduce a line here:
shprng(i).Left = x + x1
shprng(i).Top = y + y1
shprng(i).Rotation = ???
Any ideas where I could find the proper formula?
Silly - figured it out - it was easier than I thought. Don't need any SIN and COS which frightened me - just:
shprng(i).Rotation = (360 / (shprng.Count)) * (i - 1)

Perlin Noise acting strange

I am trying to implement a 2D Perlin Noise in VB.Net. I've spent the whole day searching for sources that explain the subject and one of the most notable was this article by Hugo Elias
Most of the implementation went well. On the exception of a very important part that did not seem to work in VB.Net, causing overflows.
function Noise1(integer x, integer y)
n = x + y * 57
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);
end function
In VB.net I translated it to
Private Function Noise(tX As Integer, tY As Integer) As Double
'Return a double between -1 & 1 according to a fixed random seed
Dim n As Integer = tX + tY * 57
n = (n << 13) Xor n
Return (1.0 - ((n * (n * n * 15731 + 789221) + BaseSeed) And &H7FFFFFFF) / 1073741824.0)
End Function
Which cause overflows.
Since the idea seem to be to simply generate a fractional number between -1 and 1. I've made this little function which create a Integer Number based on the coordinates and BaseSeed. BaseSeed(999999) being the base for every noise I'll create in this particular part of my game.
Private Function Noise(tX As Integer, tY As Integer) As Double
Dim tSeed As Integer
tSeed = WrapInteger(789221, BaseSeed, (tX * 1087) + (tY * 2749))
RandomGenerator = New Random(tSeed)
Return (RandomGenerator.Next(-10000, 10001) / 10000)
End Function
WrapInteger simply makes sure that the number will always be in the range of an integer, to avoid overflow errors.
Public Function WrapInteger(ByVal Lenght As Integer, ByVal Position As Integer, ByVal Movement As Integer) As Integer
Lenght += 1
Return ((Position + Movement) + (Lenght * ((Abs(Movement) \ Lenght) + 1))) Mod Lenght
End Function
When I fire it up with a Persistence of 0.25, 6 Octaves and a starting frequency of 2. this is what I get. This is a 128x128 pixel bitmap that I scaled.
Result
Anyone have an idea of why it would be so linear? When I look at this picture I have the feeling that it's not far from the truth, as if it only worked in 1D. All suposition.
Below you will find my entire PerlinNoise Class. I think the rest of it is just fine, but I added it for reference purpose. Beside, I haven't been able to find a single VB implementation of Perlin Noise on the internet. So I guess if I can fix this one, it might help others. There seem to be alot of question about Perlin noise malfunction on StackOverflow
Public Class cdPerlinNoise
Private RandomGenerator As Random
Private BaseSeed As Integer
Private Persistence As Double
Private Frequency As Integer
Private Octaves As Integer
Public Sub New(tSeed As Integer, tPersistence As Double, tOctaves As Integer, tFrequency As Integer)
Frequency = tFrequency
BaseSeed = tSeed
Persistence = tPersistence
Octaves = tOctaves
End Sub
Private Function Noise(tX As Integer, tY As Integer) As Double
Dim tSeed As Integer
tSeed = WrapInteger(789221, BaseSeed, (tX * 1087) + (tY * 2749))
RandomGenerator = New Random(tSeed)
Return (RandomGenerator.Next(-10000, 10001) / 10000)
End Function
Private Function SmoothNoise(tX As Integer, tY As Integer) As Double
Dim Corners As Double = (Noise(tX - 1, tY - 1) + Noise(tX + 1, tY - 1) + Noise(tX - 1, tY + 1) + Noise(tX + 1, tY + 1)) / 16
Dim Sides As Double = (Noise(tX - 1, tY) + Noise(tX + 1, tY) + Noise(tX, tY - 1) + Noise(tX, tY + 1)) / 8
Return (Noise(tX, tY) / 4) + Corners + Sides
End Function
Private Function InterpolateCosine(tA As Double, tB As Double, tX As Double) As Double
Dim f As Double = (1 - Cos(tX * 3.1415927)) * 0.5
Return tA * (1 - f) + tB * f
End Function
Private Function Interpolate2D(tX As Double, tY As Double) As Double
Dim WholeX As Integer = CInt(Fix(tX))
Dim RemainsX As Double = tX - WholeX
Dim WholeY As Integer = CInt(Fix(tY))
Dim RemainsY As Double = tY - WholeY
Dim v1 As Double = SmoothNoise(WholeX, WholeY)
Dim v2 As Double = SmoothNoise(WholeX + 1, WholeY)
Dim v3 As Double = SmoothNoise(WholeX, WholeY + 1)
Dim v4 As Double = SmoothNoise(WholeX + 1, WholeY + 1)
Dim i1 As Double = InterpolateCosine(v1, v2, RemainsX)
Dim i2 As Double = InterpolateCosine(v3, v4, RemainsX)
Return InterpolateCosine(i1, i2, RemainsY)
End Function
Public Function PerlinValue(tX As Double, tY As Double) As Double
Dim Total As Double = 0
Dim Frequency As Double
Dim Amplitude As Double
For i = 0 To Octaves - 1
Frequency = Frequency ^ i
Amplitude = Persistence ^ i
Total = Total + (Interpolate2D(tX * Frequency, tY * Frequency) * Amplitude)
Next
Return Total
End Function
Public Function ScaleNoise(ByVal tX As Double, ByVal tY As Double, ByVal OutputLow As Double, ByVal OutputHigh As Double) As Double
Dim Range1 As Double
Dim Range2 As Double
Dim Result As Double
Range1 = 1 - -1
Range2 = OutputHigh - OutputLow
'(B*C - A*D)/R1 + n1*(R2/R1)
Result = (((1 * OutputLow) - (-1 * OutputHigh)) / Range1) + ((PerlinValue(tX, tY) * (Range2 / Range1)))
If Result < OutputLow Then
Return OutputLow
ElseIf Result > OutputHigh Then
Return OutputHigh
Else
Return Result
End If
End Function
End Class