using System;
using Unity.Mathematics;
namespace UnityEngine.Splines
{
///
/// This struct contains position and tangent data for a knot. The position is a scalar point and the tangents are vectors.
/// The class stores a collection of BezierKnot that form a series of connected
/// . Each knot contains a Position, Tangent In, and Tangent Out. When a spline is not
/// closed, the first and last knots will contain an extraneous tangent (in and out, respectively).
///
[Serializable]
public struct BezierKnot : ISerializationCallbackReceiver, IEquatable
{
///
/// The position of the knot. On a cubic Bezier curve, this is equivalent to or
/// , depending on whether this knot is forming the first or second control point
/// of the curve.
///
public float3 Position;
///
/// The tangent vector that leads into this knot. On a cubic Bezier curve, this value is used to calculate
/// when used as the second knot in a curve.
///
public float3 TangentIn;
///
/// The tangent vector that follows this knot. On a cubic Bezier curve, this value is used to calculate
/// when used as the first knot in a curve.
///
public float3 TangentOut;
///
/// Rotation of the knot.
///
public quaternion Rotation;
///
/// Create a new BezierKnot struct.
///
/// The position of the knot relative to the spline.
public BezierKnot(float3 position): this(position, 0f, 0f, quaternion.identity)
{
}
///
/// Creates a new struct.
///
/// The position of the knot relative to the spline.
/// The leading tangent to this knot.
/// The following tangent to this knot.
public BezierKnot(float3 position, float3 tangentIn, float3 tangentOut)
: this(position, tangentIn, tangentOut, quaternion.identity)
{
}
///
/// Create a new BezierKnot struct.
///
/// The position of the knot relative to the spline.
/// The leading tangent to this knot.
/// The following tangent to this knot.
/// The rotation of the knot relative to the spline.
public BezierKnot(float3 position, float3 tangentIn, float3 tangentOut, quaternion rotation)
{
Position = position;
TangentIn = tangentIn;
TangentOut = tangentOut;
Rotation = rotation;
}
///
/// Multiply the position and tangents by a matrix.
///
/// The matrix to multiply.
/// A new BezierKnot multiplied by matrix.
public BezierKnot Transform(float4x4 matrix)
{
var rotation = math.mul(new quaternion(matrix), Rotation);
var invRotation = math.inverse(rotation);
// Tangents need to be scaled, so rotation should be applied to them.
// No need however to use the translation as this is only a direction.
return new BezierKnot(
math.transform(matrix, Position),
math.rotate(invRotation, math.rotate(matrix, math.rotate(Rotation,TangentIn))),
math.rotate(invRotation, math.rotate(matrix, math.rotate(Rotation,TangentOut))),
rotation);
}
///
/// Knot position addition. This operation only applies to the position, tangents and rotation are unmodified.
///
/// The target knot.
/// The value to add.
/// A new BezierKnot where position is the sum of knot.position and rhs.
public static BezierKnot operator +(BezierKnot knot, float3 rhs)
{
return new BezierKnot(knot.Position + rhs, knot.TangentIn, knot.TangentOut, knot.Rotation);
}
///
/// Knot position subtraction. This operation only applies to the position, tangents and rotation are unmodified.
///
/// The target knot.
/// The value to subtract.
/// A new BezierKnot where position is the sum of knot.position minus rhs.
public static BezierKnot operator -(BezierKnot knot, float3 rhs)
{
return new BezierKnot(knot.Position - rhs, knot.TangentIn, knot.TangentOut, knot.Rotation);
}
internal BezierKnot BakeTangentDirectionToRotation(bool mirrored, BezierTangent main = BezierTangent.Out)
{
if (mirrored)
{
float lead = math.length(main == BezierTangent.In ? TangentIn : TangentOut);
return new BezierKnot(Position,
new float3(0f, 0f, -lead),
new float3(0f, 0f, lead),
SplineUtility.GetKnotRotation(
math.mul(Rotation, main == BezierTangent.In ? -TangentIn : TangentOut),
math.mul(Rotation, math.up())));
}
return new BezierKnot(Position,
new float3(0, 0, -math.length(TangentIn)),
new float3(0, 0, math.length(TangentOut)),
Rotation = SplineUtility.GetKnotRotation(
math.mul(Rotation, main == BezierTangent.In ? -TangentIn : TangentOut),
math.mul(Rotation, math.up())));
}
///
/// See ISerializationCallbackReceiver.
///
public void OnBeforeSerialize() {}
///
/// See ISerializationCallbackReceiver.
///
public void OnAfterDeserialize()
{
// Ensures that when adding the first knot via Unity inspector
// or when deserializing knot that did not have the rotation field prior,
// rotation is deserialized to identity instead of (0, 0, 0, 0) which does not represent a valid rotation.
if (math.lengthsq(Rotation) == 0f)
Rotation = quaternion.identity;
}
///
/// Create a string with the values of this knot.
///
/// A summary of the values contained by this knot.
public override string ToString() => $"{{{Position}, {TangentIn}, {TangentOut}, {Rotation}}}";
///
/// Compare two knots for equality.
///
/// The knot to compare against.
/// Returns true when the position, tangents, and rotation of each knot are identical.
public bool Equals(BezierKnot other)
{
return Position.Equals(other.Position)
&& TangentIn.Equals(other.TangentIn)
&& TangentOut.Equals(other.TangentOut)
&& Rotation.Equals(other.Rotation);
}
///
/// Compare against an object for equality.
///
/// The object to compare against.
///
/// Returns true when is a and the values of each knot are
/// identical.
///
public override bool Equals(object obj)
{
return obj is BezierKnot other && Equals(other);
}
///
/// Calculate a hash code for this knot.
///
///
/// A hash code for the knot.
///
public override int GetHashCode()
{
return HashCode.Combine(Position, TangentIn, TangentOut, Rotation);
}
}
}