CodeWalker/CodeWalker.WinForms/QuaternionBox.cs

206 lines
5.4 KiB
C#

using SharpDX;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace CodeWalker.WinForms
{
public partial class QuaternionBox : UserControl
{
public QuaternionBox()
{
InitializeComponent();
}
public Quaternion Value
{
get
{
return _Value;
}
set
{
_Value = value;
UpdateFromValue();
}
}
private Quaternion _Value = Quaternion.Identity;
private bool suppressEvents = false;
public event EventHandler ValueChanged;
private void RaiseValueChanged()
{
if (suppressEvents) return;
if (ValueChanged == null) return;
ValueChanged(this, null);
}
private void UpdateFromValue()
{
suppressEvents = true;
UpdateTextBox();
UpdateEulerUpDowns();
suppressEvents = false;
}
private void UpdateFromTextBox()
{
if (suppressEvents) return;
suppressEvents = true;
_Value = ParseQuaternionString(QuaternionTextBox.Text);
UpdateEulerUpDowns();
suppressEvents = false;
RaiseValueChanged();
}
private void UpdateFromEuler()
{
if (suppressEvents) return;
suppressEvents = true;
_Value = GetQuaternion(EulerXUpDown.Value, EulerYUpDown.Value, EulerZUpDown.Value);
UpdateTextBox();
suppressEvents = false;
RaiseValueChanged();
}
private void UpdateTextBox()
{
QuaternionTextBox.Text = GetQuaternionString(_Value);
}
private void UpdateEulerUpDowns()
{
var e = GetEulerAngles(_Value);
EulerXUpDown.Value = (decimal)e.X;
EulerYUpDown.Value = (decimal)e.Y;
EulerZUpDown.Value = (decimal)e.Z;
}
private void Normalize()
{
_Value.Normalize();
UpdateFromValue();
RaiseValueChanged();
}
private static Quaternion ParseQuaternionString(string s)
{
bool tryParseFloat(string str, out float f)
{
if (float.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out f))
{
return true;
}
return false;
}
Quaternion q = Quaternion.Identity;
string[] ss = s.Split(',');
if (ss.Length > 0)
{
tryParseFloat(ss[0].Trim(), out q.X);
}
if (ss.Length > 1)
{
tryParseFloat(ss[1].Trim(), out q.Y);
}
if (ss.Length > 2)
{
tryParseFloat(ss[2].Trim(), out q.Z);
}
if (ss.Length > 3)
{
tryParseFloat(ss[3].Trim(), out q.W);
}
return q;
}
private static string GetQuaternionString(Quaternion q, string d = ", ")
{
var c = CultureInfo.InvariantCulture;
return q.X.ToString(c) + d + q.Y.ToString(c) + d + q.Z.ToString(c) + d + q.W.ToString(c);
}
private static Vector3 GetEulerAngles(Quaternion q)
{
var x = q.X;
var y = q.Y;
var z = q.Z;
var w = q.W;
var xx = x * x;
var yy = y * y;
var zz = z * z;
var ww = w * w;
var ls = xx + yy + zz + ww;
var st = x * w - y * z;
var sv = ls * 0.499f;
var rd = 180.0f / (float)Math.PI;
if (st > sv)
{
return new Vector3(90, (float)Math.Atan2(y, x) * 2.0f * rd, 0);
}
else if (st < -sv)
{
return new Vector3(-90, (float)Math.Atan2(y, x) * -2.0f * rd, 0);
}
else
{
return new Vector3(
(float)Math.Asin(2.0f * st) * rd,
(float)Math.Atan2(2.0f * (y * w + x * z), 1.0f - 2.0f * (xx + yy)) * rd,
(float)Math.Atan2(2.0f * (x * y + z * w), 1.0f - 2.0f * (xx + zz)) * rd
);
}
}
private static Quaternion GetQuaternion(decimal x, decimal y, decimal z)
{
var deg = new Vector3((float)x, (float)y, (float)z);
var rads = deg * (float)(Math.PI / 180.0);
return Quaternion.RotationYawPitchRoll(rads.Y, rads.X, rads.Z);
}
private void QuaternionTextBox_TextChanged(object sender, EventArgs e)
{
UpdateFromTextBox();
}
private void EulerXUpDown_ValueChanged(object sender, EventArgs e)
{
UpdateFromEuler();
}
private void EulerYUpDown_ValueChanged(object sender, EventArgs e)
{
UpdateFromEuler();
}
private void EulerZUpDown_ValueChanged(object sender, EventArgs e)
{
UpdateFromEuler();
}
private void NormalizeButton_Click(object sender, EventArgs e)
{
Normalize();
}
}
}