/// <summary> /// Calculates the tangent coefficents of a segment. This is used in tangent and acceleration calculations. /// </summary> /// <returns>The tangent coefficients.</returns> /// <param name="segment">The segment to calculate the coefficients of.</param> public static CubicBezierTangentCoefficients CalculateTangentCoefficients(this CubicBezierSegment segment) { var coeffs = new CubicBezierTangentCoefficients(); coeffs.Coeff1 = -3f * segment.StartPoint + 9f * segment.StartControlPoint - 9 * segment.EndControlPoint + 3f * segment.EndPoint; coeffs.Coeff2 = 6f * segment.StartPoint - 12f * segment.StartControlPoint + 6 * segment.EndControlPoint; coeffs.Coeff3 = -3f * segment.StartPoint + 3f * segment.StartControlPoint; return(coeffs); }
/// <summary> /// Gets a normal for a given segment. Note that there are an infinite number of possible normals, this method just /// produces a locally consistent normal. /// </summary> /// <returns>A normal at a given point.</returns> /// <param name="segment">The segment to calculate the tangent of.</param> /// <param name="segment">The precalculated coefficients of the curve.</param> /// <param name="t"> /// The time along the curve, where t=0 is the start point, and t=1 is the end point. Note that t can be extrapolated /// beyond these bounds. /// </param> /// <param name="up"> /// The direction the normal should try to match. /// </param> public static Vector3 GetNormal(this CubicBezierSegment segment, CubicBezierTangentCoefficients coeffs, double t, Vector3 up) { Vector3 baseNormal = segment.GetNormal(coeffs, t); Vector3 tangent = coeffs.GetTangent(t); Vector3 upNormal = Vector3.Cross(Vector3.Cross(tangent, up), tangent).normalized; if (Vector3.Angle(tangent, up) == 0f) { return(baseNormal); } float angle = Mathf.Min(Mathf.Abs(Vector3.Angle(tangent, up)), Mathf.Abs(Vector3.Angle(tangent, -up))); float lerp = Mathf.Clamp01((angle + 5) / 45f); return(AngleLerp(baseNormal, upNormal, lerp)); }
/// <summary> /// Finds the points of inflection of the curve. /// </summary> /// <returns>The points of inflection times.</returns> /// <param name="segment">Segment.</param> public static double[] GetPointsOfInflectionTimes(this CubicBezierTangentCoefficients coeffs) { // Solve the x,y and z axis separately. IEnumerable <double> xRoots = GetCubicExtremities( coeffs.Coeff1.x, coeffs.Coeff2.x, coeffs.Coeff3.x); IEnumerable <double> yRoots = GetCubicExtremities( coeffs.Coeff1.y, coeffs.Coeff2.y, coeffs.Coeff3.y); IEnumerable <double> zRoots = GetCubicExtremities( coeffs.Coeff1.z, coeffs.Coeff2.z, coeffs.Coeff3.z); // Join them together, and resort. return(xRoots .Union(yRoots) .Union(zRoots) .Where(t => 0.0 <= t && t <= 1.0) // In Range of bezier. .OrderBy(root => root) .ToArray()); }
/// <summary> /// Gets the tangent at a point along the curve. /// </summary> /// <returns>The tangent at the given time.</returns> /// <param name="coeffs">The precalculated coefficients of the curve.</param> /// <param name="t"> /// The time along the curve, where t=0 is the start point, and t=1 is the end point. Note that t can be extrapolated /// beyond these bounds. /// </param> public static Vector3 GetTangent(this CubicBezierTangentCoefficients coeffs, double t) { Vector3 velocity = (float)(t * t) * coeffs.Coeff1 + ((float)t) * coeffs.Coeff2 + coeffs.Coeff3; // If the velocity is 0, we look at the acceleration of the path to determine which direction the path is about // to travel to. if (velocity.magnitude < float.Epsilon) { Vector3 acceleration = coeffs.GetAcceleration(t); // If acceleration is 0, the entire path is probably a single point. if (acceleration.magnitude < float.Epsilon) { if (coeffs.Coeff1.magnitude < float.Epsilon) { return(Vector3.forward); } return(coeffs.Coeff1.normalized); } return(acceleration.normalized); } return(velocity.normalized); }
/// <summary> /// Gets a normal for a given segment. Note that there are an infinite number of possible normals, this method just /// produces a locally consistent normal. /// </summary> /// <returns>A normal at a given point.</returns> /// <param name="segment">The segment to calculate the tangent of.</param> /// <param name="segment">The precalculated coefficients of the curve.</param> /// <param name="t"> /// The time along the curve, where t=0 is the start point, and t=1 is the end point. Note that t can be extrapolated /// beyond these bounds. /// </param> public static Vector3 GetNormal(this CubicBezierSegment segment, CubicBezierTangentCoefficients coeffs, double t) { Vector3 currentTangent = coeffs.GetTangent(t); Vector3 normal; if (currentTangent == Vector3.zero) { Vector3 acceleration = coeffs.GetAcceleration(t); currentTangent = acceleration; } // The tangent doesn't change, (such in the case of a straight line). // We pick a vector which shouldn't be parallel to the tangent, and // use the cross product with the tangent to find a normal. normal = currentTangent; normal.x = -currentTangent.y; normal.y = -currentTangent.z; normal.z = currentTangent.x; normal = Vector3.Cross(currentTangent, normal).normalized; return(normal); }
/// <summary> /// Get the acceleration at a point along the curve. /// </summary> /// <returns>The acceleration at a given time.</returns> /// <param name="coeffs">The precalculated coefficients of the curve.</param> /// <param name="t"> /// The time along the curve, where t=0 is the start point, and t=1 is the end point. Note that t can be extrapolated /// beyond these bounds. /// </param> public static Vector3 GetAcceleration(this CubicBezierTangentCoefficients coeffs, double t) { return(((float)t) * coeffs.Coeff1 * 2 + coeffs.Coeff2); }
/// <summary> /// Gets the velocity at a point of the curve. /// </summary> /// <returns>The tangent at the given time.</returns> /// <param name="coeffs">The precalculated coefficients of the curve.</param> /// <param name="t"> /// The time along the curve, where t=0 is the start point, and t=1 is the end point. Note that t can be extrapolated /// beyond these bounds. /// </param> public static Vector3 GetVelocity(this CubicBezierTangentCoefficients coeffs, double t) { return((float)(t * t) * coeffs.Coeff1 + ((float)t) * coeffs.Coeff2 + coeffs.Coeff3); }