fix(instrument): stop Ctrl-before-click stealing a waveform mark grab from a coincident node

Ctrl held before mouse-down forced the node/toggle win over a smaller cap or
column regardless of area; now it defers to the ordinary smallest-area
arbitration like a plain click. Also amends product docs, VERIFICATION.md, and
adds sparse-material/narrow-overlay test fixtures.
This commit is contained in:
2026-08-03 16:07:49 -04:00
parent b7b7e88195
commit f91054276b
10 changed files with 117 additions and 15 deletions
+41
View File
@@ -394,6 +394,45 @@ static void testSnapRadiusIsThePixelBandsOwnFrameSpan() {
CHECK(zeroCrossingSnapFrames(overlayOf(Rect::ltrb(0, 0, 0, 60)), 1000) == 0); // zero width
}
// A narrower-than-kZeroCrossingSnapPx overlay pushes `area.x + kZeroCrossingSnapPx` past
// area.right(), so xToFrame answers frameCount (its own past-the-edge clamp) and the "radius"
// becomes the WHOLE buffer — the original unbounded-snap defect, on a width the band-stack
// allocator's kEditorMinWidth floor never actually produces in the shipped editor. Documented as
// a fixture rather than left implicit, since this is a public pure API and the width sweep
// elsewhere in this file jumps straight from 0 to 1000.
static void testNarrowOverlayLosesTheBoundBelowTheSnapWidth() {
const Rect a = Rect::ltrb(0, 0, 3, 60); // narrower than kZeroCrossingSnapPx (5)
CHECK(zeroCrossingSnapFrames(overlayOf(a), 100000) == 100000);
}
// Long SPARSE material: a 1 s / 48 kHz 40 Hz square wave, crossings ~600 frames apart, drawn
// 1000 px wide (r = 240). The dense sweep above holds every crossing well inside the radius by
// construction, so it can never observe the bound; this is the only fixture where the radius
// sits strictly BETWEEN two crossings on genuinely long material, so the bounded and unbounded
// searches can actually disagree.
static void testSnapBoundsALongSparseCaptureBetweenCrossings() {
constexpr std::int64_t n = 48000, kHalfPeriod = 600; // 40 Hz square wave at 48 kHz
std::vector<AudioSample> pcm(static_cast<std::size_t>(n));
for (std::int64_t i = 0; i < n; ++i) {
pcm[static_cast<std::size_t>(i)] = ((i / kHalfPeriod) % 2 == 0) ? 1.0f : -1.0f;
}
const Rect a = wideArea(); // width 1000 -> 48 frames per px
const std::int64_t r = zeroCrossingSnapFrames(overlayOf(a), n);
CHECK(r == kZeroCrossingSnapPx * 48); // 240
CHECK(r < kHalfPeriod); // strictly between two crossings, not covering either
// Equidistant midpoint between the crossings at 600 and 1200: the unbounded search still
// finds one (the tie rule picks the lower, 600), while the bounded snap correctly leaves the
// mark where it was dropped — this pair IS the observable difference on long material.
const std::int64_t crossing = kHalfPeriod, midpoint = crossing + kHalfPeriod / 2;
CHECK(nearestZeroCrossing(pcm.data(), n, midpoint) == crossing);
CHECK(snapToZeroCrossing(pcm.data(), n, midpoint, r) == midpoint);
// Inside the radius the snap still reaches its crossing, same as ever.
CHECK(snapToZeroCrossing(pcm.data(), n, crossing + r, r) == crossing);
CHECK(snapToZeroCrossing(pcm.data(), n, crossing - r, r) == crossing);
}
// One cycle of a 60 Hz sine at 48 kHz — 800 frames, and exactly ONE interior sign change, at the
// midpoint (frame 0 is on zero, which is not a crossing, and the up-crossing is the wrap). That
// single crossing IS the reported defect: an unbounded search resolves every drop in the buffer
@@ -944,8 +983,10 @@ int main() {
testZeroCrossingClampsTarget();
testZeroCrossingDegenerate();
testSnapRadiusIsThePixelBandsOwnFrameSpan();
testNarrowOverlayLosesTheBoundBelowTheSnapWidth();
testSnapLeavesASingleCycleMarkWhereItWasDropped();
testSnapIsUnchangedOnDenseMaterial();
testSnapBoundsALongSparseCaptureBetweenCrossings();
testSnapTakesACrossingAtTheRadiusAndRefusesOnePastIt();
testSnapKeepsTheTieRuleInsideTheRadius();
testSnapAtZeroRadiusMovesNothingButAnExactHit();