instrument: fix AHD node-tracking/tie-break/live-latch defects and close staged-envelope-curve test gaps

This commit is contained in:
2026-07-31 09:52:17 -04:00
parent d60ab1524a
commit 2fa1405b06
27 changed files with 360 additions and 86 deletions
+26 -1
View File
@@ -193,9 +193,20 @@ static void testAhdStageTimesTrackTheWallClockScale() {
const StageEnvelope attack =
resolveNodeDrag(e, EnvNode::AttackEnd, overlayOf(a), kTotal, bounds(), 100, 0);
CHECK(std::fabs(attack.attackSeconds - (e.attackSeconds + 100 * secPerPx)) < 1e-9);
// DecayEnd's underlying param (decaySeconds) does NOT move 1:1 with the cursor: the drawn
// endpoint is t0 + total, and Hold eats a holdFraction share of whatever decay gives up
// (d(total)/d(decay) = 1 - holdFraction), so decaySeconds itself has to move faster than
// the cursor to make the DRAWN node track it. Assert on the RENDERED position, not the
// raw param — that is the property a drag actually has to deliver, and asserting the old
// 1:1 param delta here is exactly what let the node-tracking defect through undetected.
EnvVertex before;
CHECK(findNode(buildEnvelopePolyline(e, overlayOf(a), kTotal), EnvNode::DecayEnd, before));
const StageEnvelope decay =
resolveNodeDrag(e, EnvNode::DecayEnd, overlayOf(a), kTotal, bounds(), 100, 0);
CHECK(std::fabs(decay.decaySeconds - (e.decaySeconds + 100 * secPerPx)) < 1e-9);
EnvVertex after;
CHECK(findNode(buildEnvelopePolyline(decay, overlayOf(a), kTotal), EnvNode::DecayEnd, after));
CHECK(std::abs((after.x - before.x) - 100) <= 1); // 1:1 with the cursor, to rounding
}
// Hold is a fraction of what attack and decay left, so the node's pixel motion converts through
@@ -270,6 +281,19 @@ static void testKnotOnALevelSegmentIsANoOp() {
CHECK(out.decayCurve == 2.5);
}
// A NEAR-level segment (sustain 0.99) is not caught by the exact-equality guard above, but its
// tiny divisor turns a one-pixel drag into a saturating swing of the exponent — the drag must
// still be a no-op rather than slam to a domain endpoint.
static void testKnotOnANearLevelSegmentIsANoOp() {
const Rect a = wideArea();
StageEnvelope e = ahdsrEnv();
e.sustainLevel = 0.99;
e.decayCurve = 2.5;
const StageEnvelope out =
resolveNodeDrag(e, EnvNode::DecayCurve, overlayOf(a), kTotal, bounds(), 0, -1);
CHECK(out.decayCurve == 2.5);
}
// --- degenerate ----------------------------------------------------------------
static void testDegenerateInputsAreNoOps() {
@@ -299,6 +323,7 @@ int main() {
testKnotDragMovesTheExponentWithinItsDomain();
testKnotAndModelCannotDiverge();
testKnotOnALevelSegmentIsANoOp();
testKnotOnANearLevelSegmentIsANoOp();
testDegenerateInputsAreNoOps();