using Cryville.Common; using Cryville.Common.Pdt; using System; using System.Collections.Generic; using System.Text.RegularExpressions; using UnityEngine; namespace Cryville.Crtr { public enum TransitionType : byte { In = 0, Out = 1, InOut = 2, OutIn = 3, Ease = 0, Sine = 4, Expo = 8, Circ = 12, Elastic = 16, Back = 20, Bounce = 24, } public static class MotionLerper { public static void Lerp(float time, float tt, T tv, float ft, T fv, TransitionType type, float rate, ref T result) where T : Vector { if (fv == null) fv = (T)ReflectionHelper.InvokeEmptyConstructor(tv.GetType()); Lerp((time - ft) / (tt - ft), fv, tv, type, rate, ref result); } public static void Lerp(float scaledTime, T from, T to, TransitionType type, float rate, ref T result) where T : Vector { if (scaledTime >= 1) { result = to; return; } if (scaledTime < 0) { result = from; return; } var r = (Vector)result; to.LerpWith(from, GetEaseTime(scaledTime, type, rate), ref r); } public static float Delerp(T value, float tt, T tv, float ft, T fv, TransitionType type, float rate) where T : Vector { if (fv == null) fv = (T)ReflectionHelper.InvokeEmptyConstructor(tv.GetType()); var t = Delerp(value, fv, tv, type, rate); return ft * (1 - t) + tt * t; } public static float Delerp(T value, T from, T to, TransitionType type, float rate) where T : Vector { if (value.CompareTo(to) >= 0) return 1; if (value.CompareTo(from) < 0) return 0; float lerpedTime = to.DelerpWith(from, value); return GetUneaseTime(lerpedTime, type, rate); } public static float GetEaseTime(float time, TransitionType type, float rate) { switch ((byte)type & 3) { case (byte)TransitionType.In: return 1 - GetEaseOutTime(1 - time, type, rate); case (byte)TransitionType.Out: return GetEaseOutTime(time, type, rate); case (byte)TransitionType.InOut: time *= 2; if (time <= 1) return (1 - GetEaseOutTime(1 - time, type, rate)) / 2; else return (GetEaseOutTime(time - 1, type, rate) + 1) / 2; case (byte)TransitionType.OutIn: time *= 2; if (time <= 1) return GetEaseOutTime(time, type, rate) / 2; else return (2 - GetEaseOutTime(2 - time, type, rate)) / 2; default: throw new ArgumentException("Unknown transition"); } } static float GetUneaseTime(float time, TransitionType type, float rate) { switch ((byte)type & 3) { case (byte)TransitionType.In: return 1 - GetUneaseOutTime(1 - time, type, rate); case (byte)TransitionType.Out: return GetUneaseOutTime(time, type, rate); case (byte)TransitionType.InOut: time *= 2; if (time <= 1) return (1 - GetUneaseOutTime(1 - time, type, rate)) / 2; else return (GetUneaseOutTime(time - 1, type, rate) + 1) / 2; case (byte)TransitionType.OutIn: time *= 2; if (time <= 1) return GetUneaseOutTime(time, type, rate) / 2; else return (2 - GetUneaseOutTime(2 - time, type, rate)) / 2; default: throw new ArgumentException("Unknown transition"); } } static float GetEaseOutTime(float p, TransitionType type, float rate) { switch ((byte)type & 252) { case (byte)TransitionType.Ease: return 1 - Mathf.Pow(1 - p, rate); case (byte)TransitionType.Sine: return Mathf.Sin(p * (Mathf.PI / 2)); case (byte)TransitionType.Expo: return 1 - Mathf.Pow(2, -10 * p); case (byte)TransitionType.Circ: p -= 1; return Mathf.Sqrt(1 - p * p); case (byte)TransitionType.Elastic: float s = rate / (2 * Mathf.PI) * Mathf.Asin(1); return Mathf.Pow(2, -10 * p) * Mathf.Sin((p - s) * (2 * Mathf.PI) / rate) + 1; case (byte)TransitionType.Back: p -= 1; return p * p * ((rate + 1) * p + rate) + 1; case (byte)TransitionType.Bounce: if (p < 1 / 2.75) { return 7.5625f * p * p; } else if (p < (2 / 2.75)) { p -= 1.5f / 2.75f; return 7.5625f * p * p + .75f; } else if (p < 2.5 / 2.75) { p -= 2.25f / 2.75f; return 7.5625f * p * p + .9375f; } else { p -= 2.625f / 2.75f; return 7.5625f * p * p + .984375f; } default: throw new ArgumentException("Unknown transition"); } } static float GetUneaseOutTime(float p, TransitionType type, float rate) { switch ((byte)type & 252) { case (byte)TransitionType.Ease: return 1 - Mathf.Pow(1 - p, 1 / rate); case (byte)TransitionType.Sine: return Mathf.Asin(p) / (Mathf.PI / 2); case (byte)TransitionType.Expo: return -Mathf.Log(1 - p, 2) / 10; case (byte)TransitionType.Circ: return 1 - Mathf.Sqrt(1 - p * p); default: throw new ArgumentException("Unknown transition"); } } } public struct MotionRegistry { readonly Type m_Type; public Type Type { get { return m_Type; } } readonly Vector m_InitValue; public Vector InitValue { get { return m_InitValue.Clone(); } } readonly Vector m_GlobalInitValue; public Vector GlobalInitValue { get { return m_GlobalInitValue.Clone(); } } public MotionRegistry(Type type) : this((Vector)ReflectionHelper.InvokeEmptyConstructor(type)) { } public MotionRegistry(Vector init) : this(init, init) { } public MotionRegistry(Vector init, Vector globalInit) { m_InitValue = init; m_GlobalInitValue = globalInit; if (!init.GetType().Equals(globalInit.GetType())) throw new ArgumentException(); m_Type = init.GetType(); } } public class RealtimeMotionValue { public Vector AbsoluteValue; List RelativeNodes; public RealtimeMotionValue Init(Vector init) { RelativeNodes = new List { new MotionNode() { Id = 0, Time = new Vec1(0), Rate = new Vec1(1), Value = init } }; AbsoluteValue = (Vector)ReflectionHelper.InvokeEmptyConstructor(init.GetType()); return this; } public RealtimeMotionValue Clone() { var r = new RealtimeMotionValue() { AbsoluteValue = AbsoluteValue.Clone() }; var rel = new List(RelativeNodes.Count); for (var i = 0; i < RelativeNodes.Count; i++) { rel.Add(RelativeNodes[i].Clone()); } r.RelativeNodes = rel; return r; } public void CopyTo(RealtimeMotionValue dest) { AbsoluteValue.CopyTo(dest.AbsoluteValue); int tcount = RelativeNodes.Count; int dcount = dest.RelativeNodes.Count; int count = Math.Min(tcount, dcount); for (int i = 0; i < count; i++) { RelativeNodes[i].CopyTo(dest.RelativeNodes[i]); } if (tcount > dcount) for (int i = dcount; i < tcount; i++) dest.RelativeNodes.Add(RelativeNodes[i].Clone()); else dest.RelativeNodes.RemoveRange(tcount, dcount - tcount); } public MotionNode GetRelativeNode(ushort id) { int i = RelativeNodes.FindIndex(n => n.Id == id); if (i == -1) { var r = GetRelativeNode((ushort)(id - 1)); r.Time = null; r.Id = id; return r; } return RelativeNodes[i]; } public MotionNode QueryRelativeNode(ushort id) { int i = RelativeNodes.FindIndex(n => n.Id == id); MotionNode cnode; if (i == -1) cnode = new MotionNode() { Id = id }; else cnode = RelativeNodes[i]; return cnode; } public void SetRelativeNode(MotionNode node) { int i = RelativeNodes.FindIndex(n => n.Id == node.Id); MotionNode cnode; if (i == -1) { cnode = new MotionNode() { Id = node.Id, Time = new Vec1(0), Rate = new Vec1(1), Value = (Vector)ReflectionHelper.InvokeEmptyConstructor(node.Value.GetType()) }; } else { cnode = RelativeNodes[i]; RelativeNodes.RemoveAt(i); } if (node.Time != null) cnode.Time = node.Time; if (node.Transition.HasValue) cnode.Transition = node.Transition.Value; if (node.Rate != null) cnode.Rate = node.Rate; if (node.Value != null) cnode.Value.ReplaceFrom(node.Value); int i2 = RelativeNodes.BinarySearch(cnode); if (i2 < 0) i2 = ~i2; RelativeNodes.Insert(i2, cnode); } /// /// Computes the motion value at the given relative time. /// /// The vector type of the value. /// The relative time. /// The result. /// public void GetValue(float reltime, ref T result) where T : Vector { int i = BinarySearch(reltime); if (i >= 0) RelativeNodes[i].Value.CopyTo(result); else { i = ~i; if (i >= RelativeNodes.Count) { RelativeNodes[i - 1].Value.CopyTo(result); } else if (i == 0) { RelativeNodes[0].Value.CopyTo(result); } else { MotionNode t = RelativeNodes[i], f = RelativeNodes[i - 1]; MotionLerper.Lerp(reltime, t.Time.Value, (T)t.Value, f.Time.Value, (T)f.Value, t.Transition.Value, t.Rate.Value, ref result); } } result.ApplyFrom(AbsoluteValue); } // Adopted from System.Collections.Generic.ArraySortHelper.InternalBinarySearch(T[] array, int index, int length, T value, IComparer comparer) int BinarySearch(float value) { int num = 0; int num2 = RelativeNodes.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); int num4 = RelativeNodes[num3].Time.Value.CompareTo(value); if (num4 == 0) return num3; if (num4 < 0) num = num3 + 1; else num2 = num3 - 1; } return ~num; } } public class MotionNode : IComparable { public ushort Id; public Vec1 Time; float CmpTime { get { return Time != null ? Time.Value : 0; } } public TransitionType? Transition; public Vec1 Rate; public Vector Value; public MotionNode Clone() { return new MotionNode() { Id = Id, Time = (Vec1)Time.Clone(), Transition = Transition, Rate = (Vec1)Rate.Clone(), Value = Value.Clone() }; } public void CopyTo(MotionNode dest) { dest.Id = Id; Time.CopyTo(dest.Time); dest.Transition = Transition; Rate.CopyTo(dest.Rate); Value.CopyTo(dest.Value); } public int CompareTo(MotionNode other) { return CmpTime.CompareTo(other.CmpTime); } public void LerpWith(MotionNode start, float lerpedTime, ref MotionNode result) { result.Id = Id; if (Time == null) result.Time = null; else { var t = (Vector)result.Time; Time.LerpWith(start.Time, lerpedTime, ref t); } result.Transition = Transition; if (Rate == null) result.Rate = null; else { var t = (Vector)result.Rate; Rate.LerpWith(start.Rate, lerpedTime, ref t); } if (Value == null) result.Value = null; else Value.LerpWith(start.Value, lerpedTime, ref result.Value); } } public abstract class Vector : IComparable { public Vector() { } public abstract int CompareTo(Vector other); public abstract void ApplyFrom(Vector parent); public abstract void ReplaceFrom(Vector parent); public abstract void LerpWith(Vector start, float lerpedTime, ref Vector result); public abstract float DelerpWith(Vector start, Vector value); public abstract bool IsZero(); public override abstract string ToString(); public abstract float[] ToArray(); public Vector Clone() { return (Vector)MemberwiseClone(); } public abstract void CopyTo(Vector dest); static readonly Type[] stringTypeArray = new Type[] { typeof(string) }; static readonly Type[] floatArrayTypeArray = new Type[] { typeof(float[]) }; public static Vector Construct(Type type, string s) { if (!typeof(Vector).IsAssignableFrom(type)) throw new ArgumentException("Type is not vector"); return (Vector)type.GetConstructor(stringTypeArray).Invoke(new object[] { s }); } public static Vector Construct(Type type, float[] values) { if (!typeof(Vector).IsAssignableFrom(type)) throw new ArgumentException("Type is not vector"); return (Vector)type.GetConstructor(floatArrayTypeArray).Invoke(new object[] { values }); } } public class Vec1 : Vector, IComparable { public float Value; public Vec1() { Value = 0; } public Vec1(string s) { Value = float.Parse(s); } public Vec1(float[] v) { Value = v[0]; } public Vec1(float i) { Value = i; } public override int CompareTo(Vector other) { return CompareTo((Vec1)other); } public int CompareTo(Vec1 other) { return Value.CompareTo(other.Value); } public override void ApplyFrom(Vector parent) { var p = (Vec1)parent; Value += p.Value; } public override void ReplaceFrom(Vector parent) { var p = (Vec1)parent; Value = p.Value; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { var r = (Vec1)result; if (start == null) { r.Value = Value; return; } var s = (Vec1)start; r.Value = s.Value * (1 - lerpedTime) + Value * lerpedTime; } public override float DelerpWith(Vector start, Vector value) { var s = (Vec1)start; var v = (Vec1)value; return (v.Value - s.Value) / (this.Value - s.Value); } public override bool IsZero() { throw new NotImplementedException(); } public override void CopyTo(Vector dest) { var d = (Vec1)dest; d.Value = Value; } public override string ToString() { return Value.ToString(); } public override float[] ToArray() { return new float[] { Value }; } } public class VecI1 : Vector { public int Value; public VecI1() { Value = 0; } public VecI1(string s) { Value = int.Parse(s); } public VecI1(float[] v) { Value = (int)Math.Round(v[0]); } public VecI1(int i) { Value = i; } public override int CompareTo(Vector other) { throw new NotImplementedException(); } public override void ApplyFrom(Vector parent) { var p = (VecI1)parent; Value += p.Value; } public override void ReplaceFrom(Vector parent) { var p = (VecI1)parent; Value = p.Value; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { var r = (VecI1)result; if (start == null) { r.Value = Value; return; } var s = (VecI1)start; r.Value = (int)(s.Value * (1 - lerpedTime) + Value * lerpedTime); } public override float DelerpWith(Vector start, Vector value) { throw new NotImplementedException(); } public override bool IsZero() { throw new NotImplementedException(); } public override void CopyTo(Vector dest) { var d = (VecI1)dest; d.Value = Value; } public override string ToString() { return Value.ToString(); } public override float[] ToArray() { return new float[] { Value }; } } public class Vec1m : Vector { public float Value; public Vec1m() { Value = 1; } public Vec1m(string s) { Value = float.Parse(s); } public Vec1m(float[] v) { Value = v[0]; } public Vec1m(float i) : base() { Value = i; } public override int CompareTo(Vector other) { throw new NotImplementedException(); } public override void ApplyFrom(Vector parent) { var p = (Vec1m)parent; Value *= p.Value; } public override void ReplaceFrom(Vector parent) { var p = (Vec1m)parent; Value = p.Value; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { var r = (Vec1m)result; if (start == null) { r.Value = Value; return; } var s = (Vec1m)start; r.Value = s.Value * (1 - lerpedTime) + Value * lerpedTime; } public override float DelerpWith(Vector start, Vector value) { throw new NotImplementedException(); } public override bool IsZero() { throw new NotImplementedException(); } public override void CopyTo(Vector dest) { var d = (Vec1m)dest; d.Value = Value; } public override string ToString() { return Value.ToString(); } public override float[] ToArray() { return new float[] { Value }; } } public class VecPtComp : Vector { float? w, h; public VecPtComp() { w = h = 0; } public VecPtComp(string s) { Parse(s, out w, out h); } public VecPtComp(float[] v) { w = v[0]; h = v[1]; } public VecPtComp(float w, float h) : base() { this.w = w; this.h = h; } public static void Parse(string s, out float? w, out float? h) { string[] c = s.Split('+'); float rw = 0, rh = 0; bool aw = false, ah = false; if (s != "") { foreach (var i in c) { if (i == "0") { aw = true; ah = true; continue; } float n = float.Parse(i.Substring(0, i.Length - 1)); if (i.EndsWith("w")) { rw += n; aw = true; } else if (i.EndsWith("h")) { rh += n; ah = true; } } } w = aw ? rw : (float?)null; h = ah ? rh : (float?)null; } public static VecPtComp Parse(string s) { var r = new VecPtComp(); float? w, h; Parse(s, out w, out h); r.w = w; r.h = h; return r; } public override int CompareTo(Vector other) { throw new NotImplementedException(); } public override void ApplyFrom(Vector parent) { var p = (VecPtComp)parent; w += p.w ?? 0; h += p.h ?? 0; } public override void ReplaceFrom(Vector parent) { var p = (VecPtComp)parent; if (p.w.HasValue) w = p.w; if (p.h.HasValue) h = p.h; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { throw new NotImplementedException(); } public override float DelerpWith(Vector start, Vector value) { throw new NotImplementedException(); } public override bool IsZero() { throw new NotImplementedException(); } public float ToFloat(Rect rect) { return (w ?? 0) * rect.width + (h ?? 0) * rect.height; } public override void CopyTo(Vector dest) { var d = (VecPtComp)dest; d.w = w; d.h = h; } public static string ToString(float? w, float? h) { List list = new List(); if (w != null) list.Add(w.ToString() + "w"); if (h != null) list.Add(h.ToString() + "h"); return string.Join("+", list.ToArray()); } public override string ToString() { return ToString(w, h); } public override float[] ToArray() { return new float[] { w.Value, h.Value }; } } public class Vec3 : Vector { float? x, y, z; public Vec3() { x = y = z = 0; } public Vec3(string s) { Match m = Regex.Match(s, @"^(\^?)(.*?),(.*?),(.*?)$"); if (m.Groups[1].Value != "") { float tx = 0, ty = 0, tz = 0; if (m.Groups[2].Value != "") tx = VecPtComp.Parse(m.Groups[2].Value).ToFloat(ChartPlayer.hitRect); if (m.Groups[3].Value != "") ty = VecPtComp.Parse(m.Groups[3].Value).ToFloat(ChartPlayer.hitRect); if (m.Groups[4].Value != "") tz = VecPtComp.Parse(m.Groups[4].Value).ToFloat(ChartPlayer.hitRect); Vector3 r = Quaternion.LookRotation(new Vector3(tx, ty, tz)).eulerAngles; x = r.x; y = r.y; z = r.z; } else { if (m.Groups[2].Value != "") x = float.Parse(m.Groups[2].Value); if (m.Groups[3].Value != "") y = float.Parse(m.Groups[3].Value); if (m.Groups[4].Value != "") z = float.Parse(m.Groups[4].Value); } } public Vec3(float[] v) { x = v[0]; y = v[1]; z = v[2]; } public Vec3(float x, float y, float z) { this.x = x; this.y = y; this.z = z; } public override int CompareTo(Vector other) { throw new NotImplementedException(); } public override void ApplyFrom(Vector parent) { var p = (Vec3)parent; x += p.x ?? 0; y += p.y ?? 0; z += p.z ?? 0; } public override void ReplaceFrom(Vector parent) { var p = (Vec3)parent; if (p.x.HasValue) x = p.x; if (p.y.HasValue) y = p.y; if (p.z.HasValue) z = p.z; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { var r = (Vec3)result; if (start == null) { r.x = x; r.y = y; r.z = z; return; } var s = (Vec3)start; r.x = x.HasValue ? (s.x.Value * (1 - lerpedTime) + x.Value * lerpedTime) : s.x.Value; r.y = y.HasValue ? (s.y.Value * (1 - lerpedTime) + y.Value * lerpedTime) : s.y.Value; r.z = z.HasValue ? (s.z.Value * (1 - lerpedTime) + z.Value * lerpedTime) : s.z.Value; } public override float DelerpWith(Vector start, Vector value) { throw new NotImplementedException(); } public override bool IsZero() { throw new NotImplementedException(); } public Vector3 ToVector3() { return new Vector3(x.Value, y.Value, z.Value); } public override void CopyTo(Vector dest) { var d = (Vec3)dest; d.x = x; d.y = y; d.z = z; } public override string ToString() { return string.Format("{0},{1},{2}", x != null ? x.ToString() : "", y != null ? y.ToString() : "", z != null ? z.ToString() : "" ); } public override float[] ToArray() { return new float[] { x.Value, y.Value, z.Value }; } } public class VecPt : Vector { public float? xw, xh, yw, yh; public VecPt() { xw = xh = yw = yh = 0; } public VecPt(string s) { Match m = Regex.Match(s, "^(.*?),(.*?)$"); VecPtComp.Parse(m.Groups[1].Value, out xw, out xh); VecPtComp.Parse(m.Groups[2].Value, out yw, out yh); } public VecPt(float[] v) { xw = v[0]; xh = v[1]; yw = v[2]; yh = v[3]; } public VecPt(float xw, float xh, float yw, float yh) { this.xw = xw; this.xh = xh; this.yw = yw; this.yh = yh; } public override int CompareTo(Vector other) { throw new NotImplementedException(); } public override void ApplyFrom(Vector parent) { var p = (VecPt)parent; xw += p.xw ?? 0; xh += p.xh ?? 0; yw += p.yw ?? 0; yh += p.yh ?? 0; } public override void ReplaceFrom(Vector parent) { var p = (VecPt)parent; if (p.xw.HasValue) xw = p.xw; if (p.xh.HasValue) xh = p.xh; if (p.yw.HasValue) yw = p.yw; if (p.yh.HasValue) yh = p.yh; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { var r = (VecPt)result; if (start == null) { r.xw = xw; r.xh = xh; r.yw = yw; r.yh = yh; return; } var s = (VecPt)start; r.xw = xw.HasValue ? (s.xw.Value * (1 - lerpedTime) + xw.Value * lerpedTime) : s.xw.Value; r.xh = xh.HasValue ? (s.xh.Value * (1 - lerpedTime) + xh.Value * lerpedTime) : s.xh.Value; r.yw = yw.HasValue ? (s.yw.Value * (1 - lerpedTime) + yw.Value * lerpedTime) : s.yw.Value; r.yh = yh.HasValue ? (s.yh.Value * (1 - lerpedTime) + yh.Value * lerpedTime) : s.yh.Value; } public override float DelerpWith(Vector start, Vector value) { throw new NotImplementedException(); } public override bool IsZero() { throw new NotImplementedException(); } public Vector2 ToVector2(Rect rect) { return new Vector2( xw.Value * rect.width + xh.Value * rect.height, yw.Value * rect.width + yh.Value * rect.height ); } public override void CopyTo(Vector dest) { var d = (VecPt)dest; d.xw = xw; d.xh = xh; d.yw = yw; d.yh = yh; } public override string ToString() { return VecPtComp.ToString(xw, xh) + "," + VecPtComp.ToString(yw, yh); } public override float[] ToArray() { return new float[] { xw.Value, xh.Value, yw.Value, yh.Value }; } } public class VecCtrl : Vector { public float? xw, xh, yw, yh, z; public VecCtrl() { xw = xh = yw = yh = z = 0; } public VecCtrl(string s) { Match m = Regex.Match(s, "^(.*?),(.*?),(.*?)$"); VecPtComp.Parse(m.Groups[1].Value, out xw, out xh); VecPtComp.Parse(m.Groups[2].Value, out yw, out yh); if (m.Groups[3].Value != "") z = float.Parse(m.Groups[3].Value); } public VecCtrl(float[] v) { xw = v[0]; xh = v[1]; yw = v[2]; yh = v[3]; z = v[4]; } public VecCtrl(float xw, float xh, float yw, float yh, float z) { this.xw = xw; this.xh = xh; this.yw = yw; this.yh = yh; this.z = z; } public override int CompareTo(Vector other) { throw new NotImplementedException(); } public override void ApplyFrom(Vector parent) { var p = (VecCtrl)parent; xw += p.xw ?? 0; xh += p.xh ?? 0; yw += p.yw ?? 0; yh += p.yh ?? 0; z += p.z ?? 0; } public override void ReplaceFrom(Vector parent) { var p = (VecCtrl)parent; if (p.xw.HasValue) xw = p.xw; if (p.xh.HasValue) xh = p.xh; if (p.yw.HasValue) yw = p.yw; if (p.yh.HasValue) yh = p.yh; if (p.z.HasValue) z = p.z; } public override void LerpWith(Vector start, float lerpedTime, ref Vector result) { var r = (VecCtrl)result; if (start == null) { r.xw = xw; r.xh = xh; r.yw = yw; r.yh = yh; r.z = z; return; } var s = (VecCtrl)start; r.xw = xw.HasValue ? (s.xw.Value * (1 - lerpedTime) + xw.Value * lerpedTime) : s.xw.Value; r.xh = xh.HasValue ? (s.xh.Value * (1 - lerpedTime) + xh.Value * lerpedTime) : s.xh.Value; r.yw = yw.HasValue ? (s.yw.Value * (1 - lerpedTime) + yw.Value * lerpedTime) : s.yw.Value; r.yh = yh.HasValue ? (s.yh.Value * (1 - lerpedTime) + yh.Value * lerpedTime) : s.yh.Value; r.z = z.HasValue ? (s.z.Value * (1 - lerpedTime) + z.Value * lerpedTime) : s.z.Value; } public override float DelerpWith(Vector start, Vector value) { throw new NotImplementedException(); } public override bool IsZero() { if (!xw.Equals(0)) return false; if (!xh.Equals(0)) return false; if (!yw.Equals(0)) return false; if (!yh.Equals(0)) return false; if (!z.Equals(0)) return false; return true; } public Vector3 ToVector3(Rect rect, float deltaz) { return new Vector3( xw.Value * rect.width + xh.Value * rect.height, yw.Value * rect.width + yh.Value * rect.height, z.Value * deltaz ); } public override void CopyTo(Vector dest) { var d = (VecCtrl)dest; d.xw = xw; d.xh = xh; d.yw = yw; d.yh = yh; d.z = z; } public override string ToString() { return VecPtComp.ToString(xw, xh) + "," + VecPtComp.ToString(yw, yh) + "," + (z != null ? z.ToString() : ""); } public override float[] ToArray() { return new float[] { xw.Value, xh.Value, yw.Value, yh.Value, z.Value }; } } public class VectorSrc : PropSrc { readonly Func _cb; public VectorSrc(Func cb) { _cb = cb; } protected override unsafe void InternalGet(out int type, out byte[] value) { var arr = _cb().ToArray(); if (arr.Length == 1) { type = PdtInternalType.Number; value = new byte[sizeof(float)]; fixed (byte* ptr = value) { *(float*)ptr = arr[0]; } } else { type = PdtInternalType.Vector; value = new byte[sizeof(float) * arr.Length + sizeof(int)]; fixed (byte* rptr = value) { var ptr = (float*)rptr; for (int i = 0; i < arr.Length; i++, ptr++) { *ptr = arr[i]; } *(int*)ptr = PdtInternalType.Number; } } } } public class VectorOp : PropOp { readonly Action _cb; public VectorOp(Action cb) { _cb = cb; } protected unsafe override void Execute() { var op = GetOperand(0); float[] values; if (op.Type == PdtInternalType.Number) { values = new float[] { op.AsNumber() }; } else if (op.Type == PdtInternalType.Vector) { int type; op.GetArraySuffix(out type, out _); if (type != PdtInternalType.Number) throw new InvalidOperationException("Not a vector of numbers"); values = new float[op.Length - sizeof(int)]; fixed (float* ptr = values) { op.CopyTo((byte*)ptr, 0, op.Length - sizeof(int)); } } else throw new InvalidOperationException("Invalid operand"); _cb(values); } } }