instrument: fix AHD DecayEnd overlay/grab defect, pin flaky curve test, close comment/doc findings

This commit is contained in:
2026-07-31 10:19:45 -04:00
parent 2fa1405b06
commit 03fb471c92
10 changed files with 117 additions and 28 deletions
+19
View File
@@ -108,11 +108,29 @@ double curveFromKnotDrag(const StageEnvelope& grabEnv, EnvNode knot, double grab
return curveFromMidLevel((newLevel - seg.start) / span);
}
// An AHD's DecayEnd moves decaySeconds via X, scaled by 1/(1 - holdFraction) — see
// resolveNodeDrag's DecayEnd case. At holdFraction == 1.0 that derivative is exactly 0, so a
// drag there can never change anything; when it ALSO coincides with HoldEnd (decay ~ 0) it is a
// dead handle sitting on top of a live one. Excluded from the grabbable set in that exact case
// only — a functional DecayEnd (holdFraction < 1) stays grabbable even when it coincides.
bool ahdDecayEndIsDead(const StageEnvelope& env, const std::vector<EnvVertex>& poly) {
if (env.kind != EnvKind::Ahd) return false;
if (1.0 - clamp01(env.holdFraction) > 1e-9) return false;
EnvVertex hold, decay;
bool haveHold = false, haveDecay = false;
for (const EnvVertex& v : poly) {
if (v.node == EnvNode::HoldEnd) { hold = v; haveHold = true; }
else if (v.node == EnvNode::DecayEnd) { decay = v; haveDecay = true; }
}
return haveHold && haveDecay && hold.x == decay.x;
}
} // namespace
NodeHit nodeAtPoint(const StageEnvelope& env, const OverlayArea& area, double totalSeconds,
int x, int y) {
const std::vector<EnvVertex> poly = buildEnvelopePolyline(env, area, totalSeconds);
const bool dropDeadDecayEnd = ahdDecayEndIsDead(env, poly);
// Nearest draggable, kind-matching node within the pick radius wins (Chebyshev distance);
// ties go to the earlier draw-order node. Knots are appended last, so a knot coincident
// with an endpoint handle loses — a drag there stays a time edit.
@@ -120,6 +138,7 @@ NodeHit nodeAtPoint(const StageEnvelope& env, const OverlayArea& area, double to
int bestDist = kNodeGrabRadius + 1;
for (const EnvVertex& v : poly) {
if (!isDraggable(v.node) || !nodeInKind(v.node, env.kind)) continue;
if (dropDeadDecayEnd && v.node == EnvNode::DecayEnd) continue;
const int dist = std::max(std::abs(x - v.x), std::abs(y - v.y));
if (dist < bestDist) { // strict-less-than keeps ties at the earlier draw order
bestDist = dist;