'GeoSub
' A set of subroutines and functions written in Visual Basic
Option Explicit
DefSng A-Z
Global iv(3), jv(3), kv(3)
Global Const pi = 3.141593
Global Const DtoR = pi / 180
Global Const RtoD = 180 / pi
Global i As Integer, j As Integer, k As Integer, l As Integer
Function ArcCos (x)
'Given a cosine, yields an angle between 0 and pi radians
Dim cosang, y
cosang = x
If Abs(cosang) > 1 Then cosang = Sgn(cosang)
If Abs(cosang) <= .0000001 Then
ArcCos = 1
Else
y = Atn(Sqr(1 - cosang * cosang) / Abs(cosang))
If cosang >= 0 Then ArcCos = y Else ArcCos = pi - y
End If
End Function
Function ArcSin (s)
'Given a sine, yields an angle between -pi/2 and pi/2 radians
If Abs(s) >= 1 Then
ArcSin = Sgn(s) * 3.1415927 / 2
Else
ArcSin = Sgn(s) * Atn(Abs(s) / Sqr(1 - s * s))
End If
End Function
Sub CliffMult (u(), v(), uv())
'Clifford product computed as dot product - cross product; so defined, the
'product is not associative, but as we never Clifford multiply more than 2
'vectors, we don't run into any problems.
Dim i As Integer
ReDim subTemp(3)
subTemp(0) = VDot(u(), v())
VCross v(), u(), subTemp()
For i = 0 To 3: uv(i) = subTemp(i): Next i
End Sub
Sub FitFunction (x(), y(), xx(), xy(), par(), stage)
Dim nIn As Integer, nOut As Integer
nIn = UBound(x)
nOut = UBound(y)
ReDim xxInv(nIn, nIn)
If stage = 1 Then
For i = 1 To nIn: For j = 1 To nIn
xx(i, j) = xx(i, j) + x(i) * x(j)
Next j, i
For i = 1 To nIn: For j = 1 To nOut
xy(i, j) = xy(i, j) + x(i) * y(j)
Next j, i
Else
MatInv xx(), xxInv()
MatMult xxInv(), xy(), par()
End If
End Sub
Sub GramSchmidt (r())
'Converts a 3 by 3 matrix into an orthogonal 3 by 3 matrix
Dim nr1, ip, nr3
nr1 = Sqr(r(1, 1) * r(1, 1) + r(1, 2) * r(1, 2) + r(1, 3) * r(1, 3))
r(1, 1) = r(1, 1) / nr1: r(1, 2) = r(1, 2) / nr1: r(1, 3) = r(1, 3) / nr1
ip = r(1, 1) * r(3, 1) + r(1, 2) * r(3, 2) + r(1, 3) * r(3, 3)
r(3, 1) = r(3, 1) - ip * r(1, 1)
r(3, 2) = r(3, 2) - ip * r(1, 2)
r(3, 3) = r(3, 3) - ip * r(1, 3)
nr3 = Sqr(r(3, 1) * r(3, 1) + r(3, 2) * r(3, 2) + r(3, 3) * r(3, 3))
r(3, 1) = r(3, 1) / nr3: r(3, 2) = r(3, 2) / nr3: r(3, 3) = r(3, 3) / nr3
r(2, 1) = r(3, 2) * r(1, 3) - r(3, 3) * r(1, 2)
r(2, 2) = r(3, 3) * r(1, 1) - r(3, 1) * r(1, 3)
r(2, 3) = r(3, 1) * r(1, 2) - r(3, 2) * r(1, 1)
End Sub
Sub MatInv (subM(), subIM())
'Inverts a square matrix
Dim column As Integer, nDim As Integer, tem
nDim = UBound(subM, 1)
If nDim <> UBound(subM, 2) Then
Dim response As Integer
response = MsgBox("Invert nonsquare matrix", 48, "Booboo")
Exit Sub
End If
ReDim r(nDim, nDim)
For i = 1 To nDim: For j = 1 To nDim
If i = j Then subIM(i, j) = 1 Else subIM(i, j) = 0
Next j, i
For column = 1 To nDim
For i = 1 To nDim: For j = 1 To nDim
r(i, j) = subM(i, j)
Next j, i
For k = 1 To nDim
If r(k, k) <> 0 Then
tem = 1 / r(k, k)
Else
tem = 1000000
End If
For j = k To nDim
r(k, j) = r(k, j) * tem
Next j
subIM(k, column) = subIM(k, column) * tem
For j = 1 To nDim
If k <> j Then
tem = r(j, k)
For l = k To nDim
r(j, l) = r(j, l) - r(k, l) * tem
Next l
subIM(j, column) = subIM(j, column) - subIM(k, column) * tem
End If
Next j
Next k
Next column
End Sub
Sub MatMult (subA(), subB(), subAB())
'Multiplies two matrices; numbers of rows and columns must correspond
Dim i As Integer, j As Integer, k As Integer
Dim nRowsA As Integer, nColsA As Integer, nColsB As Integer
nRowsA = UBound(subA, 1)
nColsA = UBound(subA, 2)
nColsB = UBound(subB, 2)
ReDim subTemp(nRowsA, nColsB)
For i = 0 To nRowsA: For j = 0 To nColsB: subTemp(i, j) = 0
For k = 0 To nColsA
subTemp(i, j) = subTemp(i, j) + subA(i, k) * subB(k, j)
Next k
Next j, i
For i = 0 To nRowsA: For j = 0 To nColsB
subAB(i, j) = subTemp(i, j)
Next j, i
End Sub
Sub MatTranspose (subM(), subMT())
'Transposes a matrix
Dim nRows As Integer, nCols As Integer
nRows = UBound(subM, 1)
nCols = UBound(subM, 2)
ReDim subTemp(nCols, nRows)
For i = 0 To nCols: For j = 0 To nRows
subTemp(i, j) = subM(j, i)
Next j, i
For i = 0 To nCols: For j = 0 To nRows
subMT(i, j) = subTemp(i, j)
Next j, i
End Sub
Sub MatVect (subM(), subV(), subMV())
'Left multiplies a vector by a matrix
Dim nRowsM As Integer, nColsM As Integer
nRowsM = UBound(subM, 1)
nColsM = UBound(subM, 2)
ReDim subTemp(nRowsM)
For i = 0 To nRowsM: For j = 0 To nColsM
subTemp(i) = subTemp(i) + subM(i, j) * subV(j)
Next j, i
For i = 0 To nRowsM: subMV(i) = subTemp(i): Next i
End Sub
Sub MoorePenrose (mat(), mp())
'Computes Moore-Penrose pseudoinverse of a matrix
Dim nRows As Integer, nCols As Integer, dimInv As Integer
nRows = UBound(mat, 1)
nCols = UBound(mat, 2)
ReDim mt(nCols, nRows): ReDim mtm(nCols, nCols): ReDim mmt(nRows, nRows)
If nRows < nCols Then dimInv = nRows Else dimInv = nCols
ReDim inv(dimInv, dimInv)
MatTranspose mat(), mt()
If nRows >= nCols Then
MatMult mt(), mat(), mtm()
MatInv mtm(), inv()
MatMult inv(), mt(), mp()
Else
MatMult mat(), mt(), mmt()
MatInv mmt(), inv()
MatMult mt(), inv(), mp()
End If
End Sub
Sub QConj (p(), q(), pqpi())
'Conjugates quaternion or vector q with quaternion p
ReDim pq(3), Temp(3)
pq(0) = p(0) * q(0) - p(1) * q(1) - p(2) * q(2) - p(3) * q(3)
pq(1) = p(0) * q(1) + p(1) * q(0) + p(2) * q(3) - p(3) * q(2)
pq(2) = p(0) * q(2) + p(2) * q(0) - p(1) * q(3) + p(3) * q(1)
pq(3) = p(0) * q(3) + p(3) * q(0) + p(1) * q(2) - p(2) * q(1)
Temp(0) = pq(0) * p(0) + pq(1) * p(1) + pq(2) * p(2) + pq(3) * p(3)
Temp(1) = -pq(0) * p(1) + pq(1) * p(0) - pq(2) * p(3) + pq(3) * p(2)
Temp(2) = -pq(0) * p(2) + pq(2) * p(0) + pq(1) * p(3) - pq(3) * p(1)
Temp(3) = -pq(0) * p(3) + pq(3) * p(0) - pq(1) * p(2) + pq(2) * p(1)
For i = 0 To 3: pqpi(i) = Temp(i): Next i
End Sub
Sub QInv (q(), iq())
'Inverts a quaternion
Dim magSqr
magSqr = q(0) * q(0) + q(1) * q(1) + q(2) * q(2) + q(3) * q(3)
iq(0) = q(0) / magSqr
For i = 1 To 3: iq(i) = -q(i) / magSqr: Next i
End Sub
Sub QMult (p(), q(), pq())
'Multiplies two quaternions
ReDim subTemp(3)
subTemp(0) = p(0) * q(0) - p(1) * q(1) - p(2) * q(2) - p(3) * q(3)
subTemp(1) = p(0) * q(1) + p(1) * q(0) + p(2) * q(3) - p(3) * q(2)
subTemp(2) = p(0) * q(2) + p(2) * q(0) - p(1) * q(3) + p(3) * q(1)
subTemp(3) = p(0) * q(3) + p(3) * q(0) + p(1) * q(2) - p(2) * q(1)
For i = 0 To 3: pq(i) = subTemp(i): Next i
End Sub
Sub QPower (power, q(), r())
'Power of a quaternion; doesn't work for large rotations
Dim vecMag, a, s, n1, n2, n3, qMag
ReDim nq(3)
VNorm q(), nq()
vecMag = Sqr(nq(1) * nq(1) + nq(2) * nq(2) + nq(3) * nq(3))
If Abs(nq(0)) > vecMag Then a = ArcCos(nq(0)) Else a = ArcSin(vecMag)
s = Sin(a)
If s = 0 Then
n1 = 0: n2 = 0: n3 = 0
Else
n1 = nq(1) / s: n2 = nq(2) / s: n3 = nq(3) / s
End If
a = a * power
qMag = VMag(q()) ^ power
r(0) = qMag * Cos(a): s = qMag * Sin(a)
r(1) = s * n1: r(2) = s * n2: r(3) = s * n3
End Sub
Sub Rmtoq (rm(), q())
'Converts a rotation matrix to the unit quaternion with nonnegative scalar
'component that represents the same rotation.
q(0) = rm(1, 1) + rm(2, 2) + rm(3, 3) + 1
If q(0) > 0 Then q(0) = .5 * Sqr(q(0)) Else q(0) = .0000001
q(1) = (rm(3, 2) - rm(2, 3)) / (4 * q(0))
q(2) = (rm(1, 3) - rm(3, 1)) / (4 * q(0))
q(3) = (rm(2, 1) - rm(1, 2)) / (4 * q(0))
End Sub
Sub TestFit (x(), y(), xx(), par(), er(), fConf(), stage)
'Computes errors and confidence intervals for a function fit
Dim numIn As Integer, numOut As Integer, fuNum As Integer
numIn = UBound(x)
numIn = UBound(y)
ReDim xxInv(numIn, numIn), pred(numOut), sumSqEr(numOut)
MatInv xx(), xxInv()
If stage = 1 Then
fuNum = fuNum + 1
For i = 1 To numOut: pred(i) = 0
For j = 1 To numIn: pred(i) = pred(i) + x(j) * par(j, i): Next j
er(i) = y(i) - pred(i)
sumSqEr(i) = sumSqEr(i) + er(i) * er(i)
Next i
Else
For i = 1 To numOut
sumSqEr(i) = sumSqEr(i) / (fuNum - numIn)
er(i) = Sqr(sumSqEr(i))
Next i
For i = 1 To numOut: For j = 1 To numIn
fConf(j, i) = sumSqEr(i) * xxInv(j, j)
If fConf(j, i) > 0 Then
fConf(j, i) = 2 * Sqr(fConf(j, i))
Else
fConf(j, i) = -1
End If
Next j, i
End If
End Sub
Function VADot (x(), a As Integer, y(), b As Integer)
'Dot product of vectors a and b in arrays x and y
VADot = x(a, 0) * y(b, 0) + x(a, 1) * y(b, 1) + x(a, 2) * y(b, 2) + x(a, 3) * y(b, 3)
End Function
Function VAMag (x(), a)
'Magnitude of vector a in array x
Dim magSqr
magSqr = x(a, 0) * x(a, 0) + x(a, 1) * x(a, 1) + x(a, 2) * x(a, 2) + x(a, 3) * x(a, 3)
If magSqr > 0 Then VAMag = Sqr(magSqr) Else VAMag = 0
End Function
Function VAng (u(), v())
'Gives angle in DEGREES between two vectors
Dim cosang, sinang, y, magU, magV
ReDim x(3)
magU = VMag(u()): magV = VMag(v())
If magU = 0 Or magV = 0 Then
VAng = 0
Exit Function
End If
cosang = VDot(u(), v()) / (VMag(u()) * VMag(v()))
If Abs(cosang) < .99 Then
VAng = ArcCos(cosang) * RtoD
Else
VCross u(), v(), x()
sinang = VMag(x()) / (VMag(u()) * VMag(v()))
y = ArcSin(sinang) * RtoD
If VDot(u(), v()) >= 0 Then VAng = y Else VAng = 180 - y
End If
End Function
Sub VCross (u(), v(), x())
'Cross product of two vectors
ReDim subTemp(3)
subTemp(1) = u(2) * v(3) - u(3) * v(2)
subTemp(2) = u(3) * v(1) - u(1) * v(3)
subTemp(3) = u(1) * v(2) - u(2) * v(1)
For i = 1 To 3: x(i) = subTemp(i): Next i
End Sub
Function VDot (u(), v())
'Dot product of two vectors
VDot = u(0) * v(0) + u(1) * v(1) + u(2) * v(2) + u(3) * v(3)
End Function
Function VMag (v())
'Magnitude of a vector
Dim magSqr
magSqr = v(0) * v(0) + v(1) * v(1) + v(2) * v(2) + v(3) * v(3)
If magSqr > 0 Then VMag = Sqr(magSqr) Else VMag = 0
End Function
Sub VNorm (v(), n())
'Normalizes a vector
Dim vm
vm = VMag(v())
If vm <= 0 Then
For i = 0 To 3: n(i) = 0: Next i
Else
For i = 0 To 3: n(i) = v(i) / vm: Next i
End If
End Sub