182 lines
6.4 KiB
C#
182 lines
6.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
namespace vtkControl
|
|
{
|
|
static class QuaternionHelper
|
|
{
|
|
public static double[][] TransponseMatrix3x3(double[][] a)
|
|
{
|
|
double[][] b = new double[3][];
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
b[i] = new double[3];
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
b[i][j] = a[j][i];
|
|
}
|
|
}
|
|
return b;
|
|
}
|
|
|
|
public static double[][] Matrix3x3FromQuaternion(double[] q)
|
|
{
|
|
//
|
|
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm
|
|
//
|
|
|
|
double sqw = q[0] * q[0];
|
|
double sqx = q[1] * q[1];
|
|
double sqy = q[2] * q[2];
|
|
double sqz = q[3] * q[3];
|
|
|
|
// invs (inverse square length) is only required if quaternion is not already normalised
|
|
double invs = 1 / (sqx + sqy + sqz + sqw);
|
|
|
|
double[][] m = new double[3][];
|
|
m[0] = new double[3];
|
|
m[1] = new double[3];
|
|
m[2] = new double[3];
|
|
m[0][0] = (sqx - sqy - sqz + sqw) * invs; // since sqw + sqx + sqy + sqz =1/invs*invs
|
|
m[1][1] = (-sqx + sqy - sqz + sqw) * invs;
|
|
m[2][2] = (-sqx - sqy + sqz + sqw) * invs;
|
|
|
|
double tmp1 = q[1] * q[2];
|
|
double tmp2 = q[3] * q[0];
|
|
m[1][0] = 2.0 * (tmp1 + tmp2) * invs;
|
|
m[0][1] = 2.0 * (tmp1 - tmp2) * invs;
|
|
|
|
tmp1 = q[1] * q[3];
|
|
tmp2 = q[2] * q[0];
|
|
m[2][0] = 2.0 * (tmp1 - tmp2) * invs;
|
|
m[0][2] = 2.0 * (tmp1 + tmp2) * invs;
|
|
tmp1 = q[2] * q[3];
|
|
tmp2 = q[1] * q[0];
|
|
m[2][1] = 2.0 * (tmp1 + tmp2) * invs;
|
|
m[1][2] = 2.0 * (tmp1 - tmp2) * invs;
|
|
|
|
return m;
|
|
}
|
|
|
|
public static IntPtr IntPtrFromQuaternion(double[] q)
|
|
{
|
|
//IntPtr p = Marshal.AllocCoTaskMem(Marshal.SizeOf(q[0]) * q.Length);
|
|
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(q[0]) * q.Length);
|
|
Marshal.Copy(q, 0, p, q.Length);
|
|
return p;
|
|
}
|
|
|
|
public static double[] QuaternionFromIntPtr(IntPtr p)
|
|
{
|
|
double[] q = new double[4];
|
|
Marshal.Copy(p, q, 0, q.Length);
|
|
return q;
|
|
}
|
|
|
|
public static double[] QuaternionFromMatrix3x3(double[][] a)
|
|
{
|
|
//
|
|
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/christian.htm
|
|
//
|
|
|
|
double[] q = new double[4];
|
|
|
|
double trace = a[0][0] + a[1][1] + a[2][2]; // I removed + 1.0f; see discussion with Ethan
|
|
if (trace > 0) // I changed M_EPSILON to 0
|
|
{
|
|
double s = 0.5f / Math.Sqrt(trace + 1.0f);
|
|
q[0] = 0.25f / s;
|
|
q[1] = (a[2][1] - a[1][2]) * s;
|
|
q[2] = (a[0][2] - a[2][0]) * s;
|
|
q[3] = (a[1][0] - a[0][1]) * s;
|
|
}
|
|
else
|
|
{
|
|
if (a[0][0] > a[1][1] && a[0][0] > a[2][2])
|
|
{
|
|
double s = 2.0f * Math.Sqrt(1.0f + a[0][0] - a[1][1] - a[2][2]);
|
|
q[0] = (a[2][1] - a[1][2]) / s;
|
|
q[1] = 0.25f * s;
|
|
q[2] = (a[0][1] + a[1][0]) / s;
|
|
q[3] = (a[0][2] + a[2][0]) / s;
|
|
}
|
|
else if (a[1][1] > a[2][2])
|
|
{
|
|
double s = 2.0f * Math.Sqrt(1.0f + a[1][1] - a[0][0] - a[2][2]);
|
|
q[0] = (a[0][2] - a[2][0]) / s;
|
|
q[1] = (a[0][1] + a[1][0]) / s;
|
|
q[2] = 0.25f * s;
|
|
q[3] = (a[1][2] + a[2][1]) / s;
|
|
}
|
|
else
|
|
{
|
|
double s = 2.0f * Math.Sqrt(1.0f + a[2][2] - a[0][0] - a[1][1]);
|
|
q[0] = (a[1][0] - a[0][1]) / s;
|
|
q[1] = (a[0][2] + a[2][0]) / s;
|
|
q[2] = (a[1][2] + a[2][1]) / s;
|
|
q[3] = 0.25f * s;
|
|
}
|
|
}
|
|
return q;
|
|
}
|
|
|
|
public static double[] QuaternionSlerp(double[] qa, double[] qb, double t)
|
|
{
|
|
//
|
|
//http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
|
|
//
|
|
|
|
// quaternion to return
|
|
double[] qm = new double[4];
|
|
|
|
// Calculate angle between them.
|
|
double cosHalfTheta = qa[0] * qb[0] + qa[1] * qb[1] + qa[2] * qb[2] + qa[3] * qb[3];
|
|
// if qa=qb or qa=-qb then theta = 0 and we can return qa
|
|
if (Math.Abs(cosHalfTheta) >= 1.0)
|
|
{
|
|
qm[0] = qa[0]; qm[1] = qa[1]; qm[2] = qa[2]; qm[3] = qa[3];
|
|
return qm;
|
|
}
|
|
// Calculate temporary values.
|
|
double halfTheta = Math.Acos(cosHalfTheta);
|
|
double sinHalfTheta = Math.Sqrt(1.0 - cosHalfTheta * cosHalfTheta);
|
|
// if theta = 180 degrees then result is not fully defined
|
|
// we could rotate around any axis normal to qa or qb
|
|
if (Math.Abs(sinHalfTheta) < 0.001)
|
|
{ // fabs is floating point absolute
|
|
qm[0] = (qa[0] * 0.5 + qb[0] * 0.5);
|
|
qm[1] = (qa[1] * 0.5 + qb[1] * 0.5);
|
|
qm[2] = (qa[2] * 0.5 + qb[2] * 0.5);
|
|
qm[3] = (qa[3] * 0.5 + qb[3] * 0.5);
|
|
return qm;
|
|
}
|
|
double ratioA = Math.Sin((1 - t) * halfTheta) / sinHalfTheta;
|
|
double ratioB = Math.Sin(t * halfTheta) / sinHalfTheta;
|
|
//calculate Quaternion.
|
|
qm[0] = (qa[0] * ratioA + qb[0] * ratioB);
|
|
qm[1] = (qa[1] * ratioA + qb[1] * ratioB);
|
|
qm[2] = (qa[2] * ratioA + qb[2] * ratioB);
|
|
qm[3] = (qa[3] * ratioA + qb[3] * ratioB);
|
|
|
|
return qm;
|
|
}
|
|
|
|
public static double[] VectorLerp(double[] v1, double[] v2, double t)
|
|
{
|
|
double[] v = new double[v1.Length];
|
|
//
|
|
if (t == 0) return v1;
|
|
else if (t == 1) return v2;
|
|
//
|
|
v[0] = v1[0] + t * (v2[0] - v1[0]);
|
|
v[1] = v1[1] + t * (v2[1] - v1[1]);
|
|
if (v.Length == 3) v[2] = v1[2] + t * (v2[2] - v1[2]);
|
|
//
|
|
return v;
|
|
}
|
|
}
|
|
}
|