Ω-W1-T5 review fixes: unify the drag frame resolve onto frameToX/xToFrame; honest comments on grabbableMarks and the grey-loop-mark contrast trade
This commit is contained in:
@@ -10,9 +10,10 @@
|
||||
// waveformOverlayArea (the overlay IS the drawn column band, inset symmetrically);
|
||||
// markerAtPoint (grab band, first-match on overlap, off-area + null-array rejection);
|
||||
// markerHandleRect (the top-strip tab that keeps coincident markers independently grabbable);
|
||||
// resolveDragFrame (round-to-nearest-frame, clamp to [0,frameCount], zero-delta/zero-width
|
||||
// no-ops); nearestZeroCrossing (nearest sign-change, sample-on-zero, equidistant-tie-to-lower,
|
||||
// no-crossing keeps target, target clamp, degenerate buffers); the four marks (per-mark cap
|
||||
// resolveDragFrame (drag lands on the frameToX/xToFrame column under the cursor, clamp to
|
||||
// [0,frameCount], zero-delta/zero-width no-ops); nearestZeroCrossing (nearest sign-change,
|
||||
// sample-on-zero, equidistant-tie-to-lower, no-crossing keeps target, target clamp, degenerate
|
||||
// buffers); the four marks (per-mark cap
|
||||
// resolve, the reverse cap order that keeps a coincident pair separable, label sides/nudging,
|
||||
// the suppression rule and its promoted-first placement, the crossfade wedge ramp);
|
||||
// waveformSurface (two stacked
|
||||
@@ -263,6 +264,36 @@ static void testResolveDragFrameDegenerate() {
|
||||
CHECK(resolveDragFrame(ov, 1000, 5000, 0) == 1000);
|
||||
}
|
||||
|
||||
// A drag must land on the column under the cursor — the SAME frameToX/xToFrame partition, never
|
||||
// a proportional approximation of it. Pins the contract itself (grabX = frameToX(startFrame),
|
||||
// result = xToFrame(grabX + dxPixels)) rather than a captured number, in the frames < columns
|
||||
// regime where the two disagree: a prior independent linear map here left a marker at frame 10
|
||||
// (1000px/37 frames, start=10, +3px) when a fresh xToFrame(x) at the same cursor column
|
||||
// resolves to frame 11 — exactly the class of drift a second frame<->pixel map produces.
|
||||
static void testResolveDragFrameLandsOnCursorColumn() {
|
||||
const OverlayArea ov = overlayOf(Rect::ltrb(0, 0, 1000, 60));
|
||||
const std::int64_t frameCount = 37;
|
||||
const std::int64_t startFrame = 10;
|
||||
const int dx = 3;
|
||||
const int grabX = frameToX(ov, frameCount, startFrame);
|
||||
const std::int64_t cursorFrame = xToFrame(ov, frameCount, grabX + dx);
|
||||
CHECK(cursorFrame == 11); // the contract's own derivation
|
||||
CHECK(resolveDragFrame(ov, frameCount, startFrame, dx) == cursorFrame);
|
||||
// General form, swept across both frames<columns and frames>columns: the resolved frame's
|
||||
// OWN column (frameToX) must contain the cursor pixel the drag actually landed on.
|
||||
const std::int64_t counts[] = {5, 37, 251, 9973};
|
||||
const int deltas[] = {-97, -3, -1, 1, 3, 97};
|
||||
for (std::int64_t n : counts) {
|
||||
for (std::int64_t start = 0; start < n; start += (std::max<std::int64_t>)(1, n / 11)) {
|
||||
for (int d : deltas) {
|
||||
const std::int64_t got = resolveDragFrame(ov, n, start, d);
|
||||
const int wantGrabX = frameToX(ov, n, start);
|
||||
CHECK(got == xToFrame(ov, n, wantGrabX + d));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- nearestZeroCrossing ------------------------------------------------------
|
||||
|
||||
static void testZeroCrossingNearest() {
|
||||
@@ -786,6 +817,7 @@ int main() {
|
||||
testResolveDragFrameClamps();
|
||||
testResolveDragFrameRounds();
|
||||
testResolveDragFrameDegenerate();
|
||||
testResolveDragFrameLandsOnCursorColumn();
|
||||
|
||||
testZeroCrossingNearest();
|
||||
testZeroCrossingSampleOnZero();
|
||||
|
||||
Reference in New Issue
Block a user