mirror of
https://github.com/hyprwm/hyprutils.git
synced 2025-12-20 08:10:10 +01:00
Fix BezierCurve edge cases
This commit is contained in:
parent
3df7bde01e
commit
a584202c87
1 changed files with 33 additions and 19 deletions
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue