Fix envelope-overlay knot/trace disagreement on odd pixel spans
Generalize curveMidLevel/curveFromMidLevel to curveLevelAt/curveFromLevelAt at arbitrary phi; knotVtx and its drag inverse now read the phi a knot's truncated x actually implies, not always 0.5.
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <initializer_list>
|
||||
|
||||
using namespace reasampler::util;
|
||||
|
||||
@@ -107,6 +108,39 @@ static void testMidLevelInverseSaturates() {
|
||||
CHECK(std::fabs(curveFromMidLevel(0.5) - kCurveNeutral) < 1e-12);
|
||||
}
|
||||
|
||||
// curveLevelAt/curveFromLevelAt is the general form a knot's own (possibly off-centre) phi
|
||||
// needs — curveMidLevel/curveFromMidLevel is the phi = 0.5 case, not a second law.
|
||||
static void testMidLevelIsThePhiHalfSpecialCase() {
|
||||
for (double e : {kCurveMin, 0.3, kCurveNeutral, 2.0, kCurveMax}) {
|
||||
CHECK(curveLevelAt(0.5, e) == curveMidLevel(e));
|
||||
}
|
||||
for (double u : {0.0, 0.2, 0.5, 0.8, 1.0}) {
|
||||
CHECK(curveFromLevelAt(0.5, u) == curveFromMidLevel(u));
|
||||
}
|
||||
}
|
||||
|
||||
// The round trip must hold at an arbitrary phi, not only 0.5 — this is what a knot whose
|
||||
// integer x lands off its segment's true midpoint (an odd pixel span) actually exercises.
|
||||
static void testLevelAtRoundTripsAtArbitraryPhi() {
|
||||
for (double phi : {0.1, 0.3, 0.42, 0.5, 0.63, 0.9}) {
|
||||
for (int i = 0; i <= 50; ++i) {
|
||||
const double e = kCurveMin + (kCurveMax - kCurveMin) * (i / 50.0);
|
||||
const double level = curveLevelAt(phi, e);
|
||||
CHECK(level > 0.0 && level < 1.0);
|
||||
CHECK(std::fabs(curveFromLevelAt(phi, level) - e) < 1e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Saturation holds at an arbitrary phi too, not only the mid-level special case.
|
||||
static void testLevelAtInverseSaturatesAtArbitraryPhi() {
|
||||
for (double phi : {0.2, 0.5, 0.8}) {
|
||||
CHECK(curveFromLevelAt(phi, 0.0) == kCurveMax);
|
||||
CHECK(curveFromLevelAt(phi, 1.0) == kCurveMin);
|
||||
CHECK(curveFromLevelAt(phi, std::nan("")) == kCurveMax);
|
||||
}
|
||||
}
|
||||
|
||||
// --- The inner dial's travel ---------------------------------------------------
|
||||
|
||||
// The knob drag delivers `start - dy/kKnobDragRangePixels`. param_slider owns that constant and
|
||||
@@ -184,6 +218,9 @@ int main() {
|
||||
testClampCurveHoldsTheDomain();
|
||||
testMidLevelRoundTripsAgainstTheExponent();
|
||||
testMidLevelInverseSaturates();
|
||||
testMidLevelIsThePhiHalfSpecialCase();
|
||||
testLevelAtRoundTripsAtArbitraryPhi();
|
||||
testLevelAtInverseSaturatesAtArbitraryPhi();
|
||||
testKnobLawIsExactAtTheNeutralCentre();
|
||||
testADialSweptThroughNeutralLandsOnTheIdentity();
|
||||
testKnobLawRoundTripsOutsideTheDetent();
|
||||
|
||||
Reference in New Issue
Block a user