Fix BezierCurve edge cases

This commit is contained in:
Freevatar 2025-11-02 09:27:41 -05:00
parent 3df7bde01e
commit a584202c87

View file

@ -26,20 +26,15 @@ void CBezierCurve::setup4(const std::array<Vector2D, 4>& pVec) {
pVec[3], pVec[3],
}; };
if (m_vPoints.size() != 4) // Pre-bake curve
std::abort(); //
// We start baking at t=(i+1)/n not at t=0
// bake BAKEDPOINTS points for faster lookups // That means the first baked x can be > 0 if curve itself starts at x>0
// T -> X ( / BAKEDPOINTS )
for (int i = 0; i < BAKEDPOINTS; ++i) { for (int i = 0; i < BAKEDPOINTS; ++i) {
float const t = (i + 1) / sc<float>(BAKEDPOINTS); // When i=0 -> t=1/255
const float t = (i + 1) * INVBAKEDPOINTS;
m_aPointsBaked[i] = Vector2D(getXForT(t), getYForT(t)); m_aPointsBaked[i] = Vector2D(getXForT(t), getYForT(t));
} }
for (int j = 1; j < 10; ++j) {
float i = j / 10.0f;
getYForPoint(i);
}
} }
float CBezierCurve::getXForT(float const& t) const { float CBezierCurve::getXForT(float const& t) const {
@ -71,21 +66,40 @@ float CBezierCurve::getYForPoint(float const& x) const {
else else
index -= step; index -= step;
// Clamp to avoid index walking off
if (index < 0)
index = 0;
else if (index > BAKEDPOINTS - 1)
index = BAKEDPOINTS - 1;
below = m_aPointsBaked[index].x < x; below = m_aPointsBaked[index].x < x;
} }
int lowerIndex = index - (!below || index == BAKEDPOINTS - 1); int lowerIndex = index - (!below || index == BAKEDPOINTS - 1);
// in the name of performance i shall make a hack // Clamp final indices
const auto LOWERPOINT = &m_aPointsBaked[lowerIndex]; if (lowerIndex < 0)
const auto UPPERPOINT = &m_aPointsBaked[lowerIndex + 1]; lowerIndex = 0;
else if (lowerIndex > BAKEDPOINTS - 2)
lowerIndex = BAKEDPOINTS - 2;
const auto PERCINDELTA = (x - LOWERPOINT->x) / (UPPERPOINT->x - LOWERPOINT->x); // In the name of performance I shall make a hack
const auto& LOWERPOINT = m_aPointsBaked[lowerIndex];
const auto& UPPERPOINT = m_aPointsBaked[lowerIndex + 1];
if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA)) // can sometimes happen for VERY small x const float dx = (UPPERPOINT.x - LOWERPOINT.x);
return 0.f; // If two baked points have almost the same x
// just return the lower one
if (dx <= 1e-6f)
return LOWERPOINT.y;
return LOWERPOINT->y + ((UPPERPOINT->y - LOWERPOINT->y) * PERCINDELTA); const auto PERCINDELTA = (x - LOWERPOINT.x) / dx;
// Can sometimes happen for VERY small x
if (std::isnan(PERCINDELTA) || std::isinf(PERCINDELTA))
return LOWERPOINT.y;
return LOWERPOINT.y + ((UPPERPOINT.y - LOWERPOINT.y) * PERCINDELTA);
} }
const std::vector<Hyprutils::Math::Vector2D>& CBezierCurve::getControlPoints() const { const std::vector<Hyprutils::Math::Vector2D>& CBezierCurve::getControlPoints() const {