// // PolynomialCurve.h // cocos2d_libs // // Created by 徐俊杰 on 2020/4/24. // #ifndef PolynomialCurve_h #define PolynomialCurve_h #include "rparticle/Macros/RParticleMacros.h" NS_RRP_BEGIN template class AnimationCurveTpl; typedef AnimationCurveTpl AnimationCurve; //class Vector2f; struct Polynomial { static float EvalSegment (float t, const float* coeff) { return (t * (t * (t * coeff[0] + coeff[1]) + coeff[2])) + coeff[3]; } float coeff[4]; }; // Smaller, optimized version struct OptimizedPolynomialCurve { enum { kMaxPolynomialKeyframeCount = 3, kSegmentCount = kMaxPolynomialKeyframeCount-1, }; Polynomial segments[kSegmentCount]; float timeValue; float velocityValue; // Evaluate double integrated Polynomial curve. // Example: position = EvaluateDoubleIntegrated (normalizedTime) * startEnergy^2 // Use DoubleIntegrate function to for example turn a force curve into a position curve. // Expects that t is in the 0...1 range. float EvaluateDoubleIntegrated (float t) const { DebugAssert(t >= -0.01F && t <= 1.01F); float res0, res1; // All segments are added together. At t = 0, the integrated curve is always zero. // 0 segment is sampled up to the 1 keyframe // First key is always assumed to be at 0 time float t1 = std::min(t, timeValue); // 1 segment is sampled from 1 key to 2 key // Last key is always assumed to be at 1 time float t2 = std::max(0.0F, t - timeValue); res0 = Polynomial::EvalSegment(t1, segments[0].coeff) * t1 * t1; res1 = Polynomial::EvalSegment(t2, segments[1].coeff) * t2 * t2; float finalResult = res0 + res1; // Add velocity of previous segments finalResult += velocityValue * std::max(t - timeValue, 0.0F); return finalResult; } // Evaluate integrated Polynomial curve. // Example: position = EvaluateIntegrated (normalizedTime) * startEnergy // Use Integrate function to for example turn a velocity curve into a position curve. // Expects that t is in the 0...1 range. float EvaluateIntegrated (float t) const { DebugAssert(t >= -0.01F && t <= 1.01F); float res0, res1; // All segments are added together. At t = 0, the integrated curve is always zero. // 0 segment is sampled up to the 1 keyframe // First key is always assumed to be at 0 time float t1 = std::min(t, timeValue); // 1 segment is sampled from 1 key to 2 key // Last key is always assumed to be at 1 time float t2 = std::max(0.0F, t - timeValue); res0 = Polynomial::EvalSegment(t1, segments[0].coeff) * t1; res1 = Polynomial::EvalSegment(t2, segments[1].coeff) * t2; return (res0 + res1); } // Evaluate the curve // extects that t is in the 0...1 range float Evaluate (float t) const { DebugAssert(t >= -0.01F && t <= 1.01F); float res0 = Polynomial::EvalSegment(t, segments[0].coeff); float res1 = Polynomial::EvalSegment(t - timeValue, segments[1].coeff); float result; if (t > timeValue) result = res1; else result = res0; return result; } // Find the maximum of a double integrated curve (x: min, y: max) Vector2f FindMinMaxDoubleIntegrated() const; // Find the maximum of the integrated curve (x: min, y: max) Vector2f FindMinMaxIntegrated() const; // Precalculates polynomials from the animation curve and a scale factor bool BuildOptimizedCurve (const AnimationCurve& editorCurve, float scale); // Integrates a velocity curve to be a position curve. // You have to call EvaluateIntegrated to evaluate the curve void Integrate (); // Integrates a velocity curve to be a position curve. // You have to call EvaluateDoubleIntegrated to evaluate the curve void DoubleIntegrate (); // Add a constant force to a velocity curve // Assumes that you have already called Integrate on the velocity curve. void AddConstantForceToVelocityCurve (float gravity) { for (int i=0;i= -0.01F && t <= 1.01F); float prevTimeValue = 0.0f; for(int i = 0; i < segmentCount; i++) { if(t <= times[i]) { const float time = t - prevTimeValue; return doubleIntegrationCache[i] + integrationCache[i] * time + Polynomial::EvalSegment(time, segments[i].coeff) * time * time; } prevTimeValue = times[i]; } DebugAssert(!"PolyCurve: Outside segment range!"); return 1.0f; } // Evaluate integrated Polynomial curve. // Example: position = EvaluateIntegrated (normalizedTime) * startEnergy // Use Integrate function to for example turn a velocity curve into a position curve. // Expects that t is in the 0...1 range. float EvaluateIntegrated (float t) const { DebugAssert(t >= -0.01F && t <= 1.01F); float prevTimeValue = 0.0f; for(int i = 0; i < segmentCount; i++) { if(t <= times[i]) { const float time = t - prevTimeValue; return integrationCache[i] + Polynomial::EvalSegment(time, segments[i].coeff) * time; } prevTimeValue = times[i]; } DebugAssert(!"PolyCurve: Outside segment range!"); return 1.0f; } // Evaluate the curve // extects that t is in the 0...1 range float Evaluate(float t) const { DebugAssert(t >= -0.01F && t <= 1.01F); float prevTimeValue = 0.0f; for(int i = 0; i < segmentCount; i++) { if(t <= times[i]) return Polynomial::EvalSegment(t - prevTimeValue, segments[i].coeff); prevTimeValue = times[i]; } DebugAssert(!"PolyCurve: Outside segment range!"); return 1.0f; } }; void SetPolynomialCurveToValue (AnimationCurve& a, OptimizedPolynomialCurve& c, float value); void SetPolynomialCurveToLinear (AnimationCurve& a, OptimizedPolynomialCurve& c); void ConstrainToPolynomialCurve (AnimationCurve& curve); bool IsValidPolynomialCurve (const AnimationCurve& curve); void CalculateMinMax(Vector2f& minmax, float value); NS_RRP_END #endif /* PolynomialCurve_h */