179 lines
5.2 KiB
C#
179 lines
5.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace DroneClient.utils
|
|
{
|
|
internal class Quaternion
|
|
{
|
|
private float w;
|
|
private float x;
|
|
private float y;
|
|
private float z;
|
|
|
|
public float W
|
|
{
|
|
get => w;
|
|
private set => w = value;
|
|
}
|
|
public float X
|
|
{
|
|
get => x;
|
|
private set => x = value;
|
|
}
|
|
public float Y
|
|
{
|
|
get => y;
|
|
private set => y = value;
|
|
}
|
|
public float Z
|
|
{
|
|
get => z;
|
|
private set => z = value;
|
|
}
|
|
|
|
public Quaternion()
|
|
{
|
|
w = 1;
|
|
x = y = z = 0;
|
|
}
|
|
|
|
public Quaternion(float w, float x, float y, float z)
|
|
{
|
|
this.w = w;
|
|
this.x = x;
|
|
this.y = y;
|
|
this.z = z;
|
|
}
|
|
|
|
public bool IsZero()
|
|
{
|
|
return w == 0 && x == 0 && y == 0 && z == 0;
|
|
}
|
|
|
|
public float getNorm()
|
|
{
|
|
if (IsZero()) return 0;
|
|
return (float)Math.Sqrt(w * w + x * x + y * y + z * z);
|
|
}
|
|
|
|
public void Normalize()
|
|
{
|
|
float norm = getNorm();
|
|
if (norm != 0)
|
|
{
|
|
w /= norm;
|
|
x /= norm;
|
|
y /= norm;
|
|
z /= norm;
|
|
}
|
|
}
|
|
|
|
static public Quaternion FromRotationVector(Vector Va)
|
|
{
|
|
float a = Va.getNorm();
|
|
if (a == 0)
|
|
{
|
|
return new Quaternion();
|
|
}
|
|
|
|
Quaternion w = new Quaternion();
|
|
float cos = (float)Math.Cos(a / 2);
|
|
float sin = (float)Math.Sin(a / 2);
|
|
|
|
w.w = cos;
|
|
w.x = Va.X / a * sin;
|
|
w.y = Va.Y / a * sin;
|
|
w.z = Va.Z / a * sin;
|
|
return w;
|
|
}
|
|
|
|
static public Quaternion FromEulerAngles(Vector euler)
|
|
{
|
|
float halfphi = euler.X / 2;
|
|
float halftheta = euler.Y / 2;
|
|
float halfpsi= euler.Z / 2;
|
|
float cosx = (float)Math.Cos(halfphi);
|
|
float sinx = (float)Math.Sin(halfphi);
|
|
float cosy = (float)Math.Cos(halftheta);
|
|
float siny = (float)Math.Sin(halftheta);
|
|
float cosz = (float)Math.Cos(halfpsi);
|
|
float sinz = (float)Math.Sin(halfpsi);
|
|
|
|
return new Quaternion(
|
|
cosx * cosy * cosz + sinx * siny * sinz,
|
|
sinx * cosy * cosz - cosx * siny * sinz,
|
|
cosx * siny * cosz + sinx * cosy * sinz,
|
|
cosx * cosy * sinz - sinx * siny * cosz);
|
|
}
|
|
|
|
static public Quaternion Copy(Quaternion q)
|
|
{
|
|
return new Quaternion(q.w, q.x, q.y, q.z);
|
|
}
|
|
|
|
public Quaternion getInverse()
|
|
{
|
|
float n = getNorm();
|
|
return new Quaternion(w / n, -x / n, -y / n, -z / n);
|
|
}
|
|
|
|
static public Quaternion Mul(Quaternion q1, Quaternion q2)
|
|
{
|
|
Quaternion res = new Quaternion();
|
|
res.w = q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z;
|
|
res.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y;
|
|
res.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z;
|
|
res.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x;
|
|
return res;
|
|
}
|
|
|
|
public void UpdateByRates(Vector w, float dt)
|
|
{
|
|
Quaternion omega = new Quaternion(0, w.X, w.Y, w.Z);
|
|
Quaternion res = Mul(this, omega);
|
|
this.w += res.w * 0.5f * dt;
|
|
this.x += res.x * 0.5f * dt;
|
|
this.y += res.y * 0.5f * dt;
|
|
this.z += res.z * 0.5f * dt;
|
|
}
|
|
|
|
static public Vector RotateVector(Vector a, Quaternion q)
|
|
{
|
|
Quaternion fromVector = new Quaternion(0, a.X, a.Y, a.Z);
|
|
Quaternion inverse = q.getInverse();
|
|
Quaternion r = Mul(q, fromVector);
|
|
r = Mul(r, inverse);
|
|
return new Vector(r.x, r.y, r.z);
|
|
}
|
|
static public Quaternion QuaternionBetweenVectors(Vector a, Vector b)
|
|
{
|
|
Quaternion res = new Quaternion();
|
|
Vector mul_v = Vector.Mul_Vector(a, b);
|
|
//Если векторы коллинеарны, то нет поворота
|
|
if (mul_v.IsZero()) return res;
|
|
|
|
float mul_s = Vector.Mul_Scalar(a, b);
|
|
//Хотя бы один из векторов нулевой => кватернион неопределен, но мы возвращаем (1, 0, 0, 0)
|
|
if (mul_s == 0) return res;
|
|
|
|
float norm_scalar = a.getNorm() * b.getNorm() + mul_s;
|
|
float normQ = (float)Math.Sqrt(norm_scalar * norm_scalar + mul_v.getNorm() * mul_v.getNorm());
|
|
|
|
res.w = norm_scalar / normQ;
|
|
res.x = mul_v.X / normQ;
|
|
res.y = mul_v.Y / normQ;
|
|
res.z = mul_v.Z / normQ;
|
|
return res;
|
|
}
|
|
|
|
override public string ToString()
|
|
{
|
|
return $"({w}, {x}, {y}, {z})";
|
|
}
|
|
}
|
|
}
|