Import CMayaSpline from metaforce

This commit is contained in:
Henrique Gemignani Passos Lima
2023-10-07 13:17:12 +03:00
parent 61afe62797
commit 1f57bf839d
7 changed files with 550 additions and 19 deletions
+3
View File
@@ -136,6 +136,9 @@ Kyoto/Input/CDolphinController.cpp:
.sbss start:0x80419B90 end:0x80419B98
.sdata2 start:0x8041E9F0 end:0x8041EA28
Kyoto/Math/CMayaSpline.cpp:
.text start:0x80328B0C end:0x8032A764
Runtime/global_destructor_chain.c:
.text start:0x80344DD8 end:0x80344E38
.sbss start:0x80419CC0 end:0x80419CC4
+19 -19
View File
@@ -14828,27 +14828,27 @@ fn_803283B0 = .text:0x803283B0; // type:function size:0x220 align:4
fn_803285D0 = .text:0x803285D0; // type:function size:0x3D8
fn_803289A8 = .text:0x803289A8; // type:function size:0x164
fn_80328B0C = .text:0x80328B0C; // type:function size:0x22C
fn_80328D38 = .text:0x80328D38; // type:function size:0x144
fn_80328E7C = .text:0x80328E7C; // type:function size:0x248
fn_803290C4 = .text:0x803290C4; // type:function size:0x8 align:4
fn_803290CC = .text:0x803290CC; // type:function size:0x50 align:4
fn_8032911C = .text:0x8032911C; // type:function size:0x28
fn_80329144 = .text:0x80329144; // type:function size:0x8 align:4
CalculateHermiteCoefficients__11CMayaSplineFRCQ24rstl29reserved_vector<9CVector2f,4>Pf = .text:0x80328D38; // type:function size:0x144
FindControlPoints__11CMayaSplineFiRQ24rstl29reserved_vector<9CVector2f,4> = .text:0x80328E7C; // type:function size:0x248
GetKnotCount__11CMayaSplineCFv = .text:0x803290C4; // type:function size:0x8 align:4
GetDuration__11CMayaSplineCFv = .text:0x803290CC; // type:function size:0x50 align:4
GetMaxTime__11CMayaSplineCFv = .text:0x8032911C; // type:function size:0x28
GetKnots__11CMayaSplineCFv = .text:0x80329144; // type:function size:0x8 align:4
fn_8032914C = .text:0x8032914C; // type:function size:0x1B8
fn_80329304 = .text:0x80329304; // type:function size:0x2C4
fn_803295C8 = .text:0x803295C8; // type:function size:0x158
fn_80329720 = .text:0x80329720; // type:function size:0x6C
fn_8032978C = .text:0x8032978C; // type:function size:0x3AC
fn_80329B38 = .text:0x80329B38; // type:function size:0x28
fn_80329B60 = .text:0x80329B60; // type:function size:0x40
EvaluateAtUnclamped__11CMayaSplineFf = .text:0x80329304; // type:function size:0x2C4
EvaluateAt__11CMayaSplineFf = .text:0x803295C8; // type:function size:0x158
FindKnot__11CMayaSplineFfRi = .text:0x80329720; // type:function size:0x6C
EvaluateInfinities__11CMayaSplineFfb = .text:0x8032978C; // type:function size:0x3AC
EvaluateHermite__11CMayaSplineFf = .text:0x80329B38; // type:function size:0x28
SLdrSpline_ct = .text:0x80329B60; // type:function size:0x40
fn_80329BA0 = .text:0x80329BA0; // type:function size:0xDC align:4
fn_80329C7C = .text:0x80329C7C; // type:function size:0xC8
fn_80329D44 = .text:0x80329D44; // type:function size:0xE0
fn_80329E24 = .text:0x80329E24; // type:function size:0x5A4 align:4
fn_8032A3C8 = .text:0x8032A3C8; // type:function size:0xA8
fn_8032A470 = .text:0x8032A470; // type:function size:0x70
fn_8032A4E0 = .text:0x8032A4E0; // type:function size:0x144 align:4
fn_8032A624 = .text:0x8032A624; // type:function size:0x140
SLdrSpline_Read = .text:0x80329C7C; // type:function size:0xC8
vector_Knot_InputStream = .text:0x80329D44; // type:function size:0xE0
CalculateTangents__15CMayaSplineKnotFP15CMayaSplineKnotP15CMayaSplineKnot = .text:0x80329E24; // type:function size:0x5A4 align:4
ValidateTangent__FR9CVector2f = .text:0x8032A3C8; // type:function size:0xA8
GetTangents__15CMayaSplineKnotFP15CMayaSplineKnotP15CMayaSplineKnotR9CVector2fR9CVector2f = .text:0x8032A470; // type:function size:0x70
CMayaSplineKnot_ct = .text:0x8032A4E0; // type:function size:0x144 align:4
__ct__15CMayaSplineKnotFR12CInputStream = .text:0x8032A624; // type:function size:0x140
fn_8032A764 = .text:0x8032A764; // type:function size:0x138 align:4
fn_8032A89C = .text:0x8032A89C; // type:function size:0x1DC align:4
fn_8032AA78 = .text:0x8032AA78; // type:function size:0xB0
+1
View File
@@ -304,6 +304,7 @@ config.libs = [
Object(Matching, "Kyoto/Math/CVector3i.cpp"),
Object(Matching, "Kyoto/Input/DolphinIController.cpp"),
Object(Matching, "Kyoto/Input/CDolphinController.cpp"),
Object(NonMatching, "Kyoto/Math/CMayaSpline.cpp"),
],
},
{
+62
View File
@@ -0,0 +1,62 @@
#ifndef _CMAYASPLINE
#define _CMAYASPLINE
#include "Kyoto/Math/CVector2f.hpp"
#include "rstl/vector.hpp"
#include "rstl/reserved_vector.hpp"
class CMayaSplineKnot {
float x0_time;
float x4_amplitude;
u8 x8_;
u8 x9_;
bool xa_24_dirty : 1;
u8 xb_;
CVector2f xc_cachedTangentA;
CVector2f x14_cachedTangentB;
public:
CMayaSplineKnot(CInputStream& in);
float GetTime() const { return x0_time; }
float GetAmplitude() const { return x4_amplitude; }
u8 GetX8() const { return x8_; }
u8 GetX9() const { return x9_; }
void GetTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next, CVector2f& tangentA, CVector2f& tangentB);
void CalculateTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next);
};
class CMayaSpline {
uint x0_preInfinity;
uint x4_postInfinity;
rstl::vector<CMayaSplineKnot> x8_knots;
uint x18_clampMode;
float x1c_minAmplitudeTime;
float x20_maxAmplitudeTime;
int x24_chachedKnotIndex;
int x28_;
bool x2c_24_dirty;
float x30_cachedMinTime;
float x34_cachedHermitCoefs[4];
public:
CMayaSpline(CInputStream& in, int count);
size_t GetKnotCount() const;
const rstl::vector<CMayaSplineKnot>& GetKnots() const;
float GetMinTime() const;
float GetMaxTime() const;
float GetDuration() const;
float EvaluateAt(float time);
float EvaluateAtUnclamped(float time);
float EvaluateInfinities(float time, bool Pre);
float EvaluateHermite(float time);
bool FindKnot(float time, int& knotIndex);
void FindControlPoints(int knotIndex, rstl::reserved_vector<CVector2f, 4>& controlPoints);
void CalculateHermiteCoefficients(const rstl::reserved_vector<CVector2f, 4>& controlPoits, float* coefs);
};
#endif // _CMAYASPLINE
+3
View File
@@ -11,6 +11,9 @@ public:
float GetX() const { return mX; }
float GetY() const { return mY; }
void SetX(float x) { mX = x; }
void SetY(float y) { mY = y; }
CVector2f& operator+=(const CVector2f& rhs);
CVector2f& operator-=(const CVector2f& rhs);
+2
View File
@@ -81,6 +81,8 @@ _MATH_INLINE float atan2f(float y, float x) { return (float)atan2((double)y, (do
_MATH_INLINE float fmodf(float x, float m) { return (float)fmod((double)x, (double)m); }
float tanf(float x);
double acos(double x);
double modf( double x, double * intptr );
_MATH_INLINE float acosf(float x) { return (float)acos((double)x); }
double ldexp(double x, int exp);
+460
View File
@@ -0,0 +1,460 @@
#include "Kyoto/Math/CMayaSpline.hpp"
#include "Kyoto/Math/CloseEnough.hpp"
#include "Kyoto/Streams/CInputStream.hpp"
#include "float.h"
#include "math.h"
void ValidateTangent(CVector2f& tangent) {
if (tangent.GetX() < 0.f) {
tangent.SetX(0.f);
}
const float mag = tangent.Magnitude();
if (mag != 0.f) {
tangent /= mag;
}
if (tangent.GetX() == 0.f && tangent.GetY() != 0.f) {
const float mul = tangent.GetY() >= 0.f ? 1.f : -1.f;
tangent.SetX(0.0001f);
tangent.SetY(5729578.0f * tangent.GetX() * mul);
}
}
CMayaSplineKnot::CMayaSplineKnot(CInputStream& in)
: xc_cachedTangentA(0.0f, 0.0f), x14_cachedTangentB(0.0f, 0.0f) {
x0_time = in.ReadFloat();
x4_amplitude = in.ReadFloat();
x8_ = in.ReadChar();
x9_ = in.ReadChar();
if (x8_ == 5) {
float x = in.ReadFloat();
float y = in.ReadFloat();
xc_cachedTangentA = CVector2f(x, y);
}
if (x9_ == 5) {
float x = in.ReadFloat();
float y = in.ReadFloat();
x14_cachedTangentB = CVector2f(x, y);
}
}
void CMayaSplineKnot::GetTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next, CVector2f& tangentA,
CVector2f& tangentB) {
if (xa_24_dirty) {
CalculateTangents(prev, next);
}
tangentA = xc_cachedTangentA;
tangentB = x14_cachedTangentB;
}
void CMayaSplineKnot::CalculateTangents(CMayaSplineKnot* prev, CMayaSplineKnot* next) {
xa_24_dirty = false;
bool calculateTangents = false;
if (x8_ == 4 && prev != nullptr) {
float fVar2 = abs(prev->GetAmplitude() - GetAmplitude());
float fVar3 = fVar2;
if (next != nullptr) {
fVar3 = abs(next->GetAmplitude() - GetAmplitude());
}
if (fVar3 <= 0.05f || fVar2 <= 0.05f) {
x8_ = 1;
}
}
if (x8_ == 0) {
if (prev == nullptr) {
xc_cachedTangentA = CVector2f(1.f, 0.f);
} else {
xc_cachedTangentA =
CVector2f(GetTime() - prev->GetTime(), GetAmplitude() - prev->GetAmplitude());
}
} else if (x8_ == 1) {
float fVar1 = 0.f;
if (prev != nullptr) {
fVar1 = GetTime() - prev->GetTime();
} else if (next != nullptr) {
fVar1 = next->GetTime() - GetTime();
}
xc_cachedTangentA = CVector2f(fVar1, 0.f);
} else if (x8_ == 2) {
calculateTangents = true;
} else if (x8_ == 3) {
xc_cachedTangentA = CVector2f(1.0f, 1.0f);
} else if (x8_ == 4) {
x8_ = 2;
calculateTangents = true;
}
if (x9_ == 0) {
if (next == nullptr) {
x14_cachedTangentB = CVector2f(1.0f, 0.0f);
} else {
x14_cachedTangentB =
CVector2f(next->GetTime() - GetTime(), next->GetAmplitude() - GetAmplitude());
}
} else if (x9_ == 1) {
float fVar1 = 0.f;
if (next != nullptr) {
fVar1 = next->GetTime() - GetTime();
} else if (prev != nullptr) {
fVar1 = GetTime() - prev->GetTime();
}
x14_cachedTangentB = CVector2f(fVar1, 0.f);
} else if (x9_ == 2) {
calculateTangents = true;
} else if (x9_ == 3) {
x14_cachedTangentB = CVector2f(1.f, 0.f);
} else if (x9_ == 4 && next != nullptr) {
float fVar1 = next->GetAmplitude() - GetAmplitude();
float fVar2 = fVar1;
if (prev != nullptr) {
fVar2 = prev->GetAmplitude() - GetAmplitude();
}
if (fVar1 <= 0.05f || fVar2 <= 0.05f) {
x9_ = 1;
}
calculateTangents = true;
}
if (calculateTangents) {
CVector2f tangentA(0.0f, 0.0f);
CVector2f tangentB(0.0f, 0.0f);
if (prev == nullptr && next != nullptr) {
tangentA = tangentB =
CVector2f(next->GetTime() - GetTime(), next->GetAmplitude() - GetAmplitude());
} else if (prev != nullptr && next == nullptr) {
tangentA = tangentB =
CVector2f(GetTime() - prev->GetTime(), GetAmplitude() - prev->GetAmplitude());
} else if (prev != nullptr && next != nullptr) {
float timeDiff = next->GetTime() - prev->GetTime();
float ampDiff = next->GetAmplitude() - prev->GetAmplitude();
float amp =
timeDiff >= 0.0001f ? ampDiff / timeDiff : (ampDiff <= 0.f ? -5729578.0f : 5729578.0f);
float nextTimeDiff = next->GetTime() - GetTime();
float prevTimeDiff = GetTime() - prev->GetTime();
float ampA = 0.f;
float ampB = 0.;
float timeA = 0.;
float timeB = 0.;
if (nextTimeDiff >= 0.f) {
ampA = nextTimeDiff * amp;
} else {
timeA = 0.f;
ampA = amp;
}
if (prevTimeDiff >= 0.f) {
ampB = prevTimeDiff * amp;
} else {
timeB = 0.f;
}
tangentB = CVector2f(timeB, ampB);
tangentA = CVector2f(timeA, ampA);
} else {
// TODO
// tangentA.zeroOut();
// tangentB.zeroOut();
}
if (x8_ == 2) {
xc_cachedTangentA = tangentA;
}
if (x9_ == 2) {
x14_cachedTangentB = tangentB;
}
}
ValidateTangent(xc_cachedTangentA);
ValidateTangent(x14_cachedTangentB);
}
// CMayaSpline::CMayaSpline(CInputStream& in, int count) : x0_preInfinity(in.ReadInt8()),
// x4_postInfinity(in.ReadInt8()) {
// u32 knotCount = in.ReadLong();
// x8_knots.reserve(knotCount);
// for (size_t i = 0; i < knotCount; ++i) {
// x8_knots.emplace_back(in);
// }
// x18_clampMode = in.ReadInt8();
// x1c_minAmplitudeTime = in.ReadFloat();
// x20_maxAmplitudeTime = in.ReadFloat();
// }
size_t CMayaSpline::GetKnotCount() const { return x8_knots.size(); }
const rstl::vector< CMayaSplineKnot >& CMayaSpline::GetKnots() const { return x8_knots; }
float CMayaSpline::GetMinTime() const { return x8_knots.empty() ? 0.f : x8_knots[0].GetTime(); }
float CMayaSpline::GetMaxTime() const {
return x8_knots.empty() ? 0.f : x8_knots[GetKnotCount() - 1].GetTime();
}
float CMayaSpline::GetDuration() const {
return x8_knots.empty() ? 0.f : GetMaxTime() - GetMinTime();
}
float CMayaSpline::EvaluateAt(float time) {
float amplitude = EvaluateAtUnclamped(time);
if (x18_clampMode == 1) {
if (x1c_minAmplitudeTime > amplitude) {
return x1c_minAmplitudeTime;
}
if (x20_maxAmplitudeTime < amplitude) {
return x20_maxAmplitudeTime;
}
return amplitude;
} else if (x18_clampMode == 2) {
float center = x20_maxAmplitudeTime - x1c_minAmplitudeTime;
if (center > 0.f) {
if (amplitude <= FLT_EPSILON + x20_maxAmplitudeTime) {
return amplitude - (center * static_cast< float >(
int((amplitude - x20_maxAmplitudeTime) / center) + 1));
}
if (amplitude < x1c_minAmplitudeTime - FLT_EPSILON) {
return amplitude + (center * static_cast< float >(
abs(int((amplitude - x1c_minAmplitudeTime) / center))));
}
}
}
return amplitude;
}
float CMayaSpline::EvaluateAtUnclamped(float time) {
if (x8_knots.empty()) {
return 0.f;
}
u32 lastIdx = x8_knots.size() - 1;
bool bVar2 = false;
float retVal;
if (time < x8_knots[0].GetTime()) {
if (x0_preInfinity == 0) {
return x8_knots[0].GetAmplitude();
}
return EvaluateInfinities(time, true);
} else if (x8_knots[lastIdx].GetTime() >= time) {
bVar2 = false;
int local_68 = -1;
int iVar1 = x24_chachedKnotIndex;
if (iVar1 != -1) {
if (lastIdx <= iVar1 || x8_knots[lastIdx].GetTime() >= time) {
if (iVar1 > 0 && x8_knots[iVar1].GetTime() > time) {
int iVar3 = iVar1 - 1;
bVar2 = x8_knots[iVar3].GetTime() < time;
if (bVar2) {
local_68 = iVar1;
}
if (x8_knots[iVar3].GetTime() == time) {
x24_chachedKnotIndex = iVar3;
return x8_knots[x24_chachedKnotIndex].GetAmplitude();
}
}
} else {
retVal = x8_knots[iVar1 + 1].GetTime();
if (retVal == time) {
x24_chachedKnotIndex = lastIdx;
return x8_knots[x24_chachedKnotIndex].GetAmplitude();
}
if (retVal > time) {
bVar2 = true;
local_68 = iVar1 + 1;
}
}
}
if (!bVar2 && (FindKnot(time, local_68))) {
if (local_68 == 0) {
x24_chachedKnotIndex = 0;
return x8_knots[0].GetAmplitude();
}
if (local_68 == x8_knots.size()) {
x24_chachedKnotIndex = 0;
return x8_knots[lastIdx].GetAmplitude();
}
}
lastIdx = local_68 - 1;
if (x28_ != lastIdx) {
x24_chachedKnotIndex = lastIdx;
x28_ = lastIdx;
if (x8_knots[x24_chachedKnotIndex].GetX9() == 3) {
x2c_24_dirty = true;
} else {
x2c_24_dirty = false;
rstl::reserved_vector< CVector2f, 4 > points;
FindControlPoints(x24_chachedKnotIndex, points);
CalculateHermiteCoefficients(points, x34_cachedHermitCoefs);
x30_cachedMinTime = points[0].GetX();
}
}
if (x2c_24_dirty) {
return x8_knots[x24_chachedKnotIndex].GetTime();
} else {
return EvaluateHermite(time);
}
}
if (x4_postInfinity == 0) {
return x8_knots[lastIdx].GetAmplitude();
}
return EvaluateInfinities(time, false);
}
float CMayaSpline::EvaluateInfinities(float time, bool pre) {
if (x8_knots.empty()) {
return 0.f;
}
int lastIdx = x8_knots.size() - 1;
CMayaSplineKnot* curKnot = &x8_knots[0];
const float startTime = x8_knots[0].GetTime();
const float endTime = x8_knots[lastIdx].GetAmplitude();
float center = endTime - startTime;
if (close_enough(center, 0)) {
return curKnot->GetAmplitude();
}
double tmp = 0.f;
float divTime = (time <= endTime) ? modf((time - startTime) / center, &tmp)
: modf((time - endTime) / center, &tmp);
center = center * abs(divTime);
tmp = 1.f + abs(tmp);
if (!pre) {
if (x4_postInfinity == 4) {
divTime = fmod(tmp, 2.f);
if (close_enough(divTime, 0.f)) {
center = startTime + center;
} else {
center = endTime - center;
}
} else if (x4_postInfinity == 2 || x4_postInfinity == 3) {
center = startTime + center;
} else if (x4_postInfinity == 1) {
center = time - endTime;
CVector2f tangentA(0.0f, 0.0f);
CVector2f tangentB(0.0f, 0.0f);
x8_knots[0].GetTangents((lastIdx < 1) ? nullptr : &x8_knots[lastIdx - 2], nullptr, tangentA,
tangentB);
if (!close_enough(tangentB.GetX(), 0.f)) {
return x8_knots[lastIdx].GetAmplitude() + (center * tangentB.GetY() / tangentB.GetX());
}
return x8_knots[lastIdx].GetAmplitude();
}
} else if (x0_preInfinity == 4) {
divTime = fmod(tmp, 2.f);
if (close_enough(divTime, 0.f)) {
center = endTime - center;
} else {
center = startTime + center;
}
} else if (x0_preInfinity == 2 || x0_preInfinity == 3) {
center = endTime - center;
} else if (x0_preInfinity == 1) {
center = (startTime - time);
CVector2f tangentA(0.0f, 0.0f);
CVector2f tangentB(0.0f, 0.0f);
x8_knots[0].GetTangents(nullptr, &x8_knots[1], tangentA, tangentB);
if (!close_enough(tangentA.GetX(), 0)) {
return (x8_knots[0].GetAmplitude() - (center * tangentA.GetY() / tangentA.GetX()));
}
return x8_knots[0].GetAmplitude();
}
float eval = EvaluateAt(center);
if (pre && x0_preInfinity == 3) {
return eval - (tmp * x8_knots[lastIdx].GetAmplitude() - x8_knots[0].GetAmplitude());
}
if (!pre && x4_postInfinity == 3) {
return eval + (tmp * x8_knots[lastIdx].GetAmplitude() - x8_knots[0].GetAmplitude());
}
return eval;
}
float CMayaSpline::EvaluateHermite(float time) {
const float timeDiff = time - x30_cachedMinTime;
return x34_cachedHermitCoefs[0] + (timeDiff * x34_cachedHermitCoefs[1]) +
(timeDiff * x34_cachedHermitCoefs[2]) + (timeDiff * x34_cachedHermitCoefs[3]);
}
bool CMayaSpline::FindKnot(float time, int& knotIndex) {
if (x8_knots.empty()) {
return false;
}
u32 lower = 0;
u32 upper = x8_knots.size();
while (lower < upper) {
u32 index = (lower + upper) / 2;
const CMayaSplineKnot& knot = x8_knots[index];
if (knot.GetTime() > time) {
upper = index - 1;
} else if (time > knot.GetTime()) {
lower = index + 1;
} else {
knotIndex = index;
return true;
}
}
knotIndex = lower;
return false;
}
void CMayaSpline::FindControlPoints(int knotIndex,
rstl::reserved_vector< CVector2f, 4 >& controlPoints) {
CMayaSplineKnot* knot = &x8_knots[knotIndex];
controlPoints.push_back(CVector2f(knot->GetTime(), knot->GetAmplitude()));
CVector2f tangentA(0.0f, 0.0f);
CVector2f tangentB(0.0f, 0.0f);
CMayaSplineKnot* next = (knotIndex + 1 < x8_knots.size()) ? &x8_knots[knotIndex + 1] : nullptr;
CMayaSplineKnot* prev = (knotIndex - 1 >= 0) ? &x8_knots[knotIndex - 1] : nullptr;
knot->GetTangents(prev, next, tangentA, tangentB);
knot = &x8_knots[knotIndex + 1];
controlPoints.push_back(CVector2f(controlPoints[0] + (tangentB * 1.f / 3.f)));
next = (knotIndex + 2 < x8_knots.size()) ? &x8_knots[knotIndex + 2] : nullptr;
prev = (knotIndex - 2 >= 0) ? &x8_knots[knotIndex - 2] : nullptr;
knot->GetTangents(prev, next, tangentA, tangentB);
CVector2f knotV(knot->GetTime(), knot->GetAmplitude());
controlPoints.push_back(CVector2f(knotV - (tangentA * 1.f / 3.f)));
controlPoints.push_back(knotV);
}
void CMayaSpline::CalculateHermiteCoefficients(
const rstl::reserved_vector< CVector2f, 4 >& controlPoints, float* coefs) {
const CVector2f point1 = controlPoints[3] - controlPoints[0];
const CVector2f point2 = controlPoints[1] - controlPoints[0];
const CVector2f point3 = controlPoints[3] - controlPoints[2];
float dVar7 = point2.GetX() != 0.f ? point2.GetY() / point2.GetX() : 5729578.0f;
float dVar6 = point3.GetX() != 0.f ? point3.GetY() / point3.GetX() : 5729578.0f;
const float point1XSq = point1.GetX() * point1.GetX();
coefs[0] =
((1.f / (point1XSq)) *
(((dVar7 * point1.GetX()) + (dVar6 * point1.GetX()) - point1.GetY()) - point1.GetY())) /
point1.GetX();
coefs[1] = ((1.f / (point1XSq)) *
((((point1.GetY() + (point1.GetY() + point1.GetY())) - (dVar7 * point1.GetX())) -
(dVar7 * point1.GetX())) -
(dVar6 * point1.GetX())));
coefs[2] = dVar7;
coefs[3] = controlPoints[0].GetY();
}