Files
Simulator/DroneClient/utils/Quaternion.cs

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})";
}
}
}