loop: crossfade the Gate sustain seam, and unshadow the loop handles that made loop points look gone
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
//
|
||||
// Covers: frameToX / xToFrame (linear map + inverse, edge clamps, degenerate frameCount/width);
|
||||
// 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); waveformSurface (two stacked
|
||||
@@ -309,6 +310,57 @@ static void testMarkerGrabInMonoSpansTheBand() {
|
||||
CHECK(markerAtPoint(s.overlay, frames, markers, 1, mx, b.bottom() + 5) == -1);
|
||||
}
|
||||
|
||||
// --- The marker grab handle ----------------------------------------------------
|
||||
|
||||
static void testMarkerHandleIsATopStripCentredOnTheMarker() {
|
||||
const Rect a = wideArea();
|
||||
const int mx = frameToX(overlayOf(a), 1000, 250);
|
||||
const Rect h = markerHandleRect(overlayOf(a), 1000, 250);
|
||||
CHECK(h.x == mx - kMarkerHandleHalfWidth);
|
||||
CHECK(h.right() == mx + kMarkerHandleHalfWidth + 1);
|
||||
CHECK(h.y == a.y);
|
||||
CHECK(h.height == kMarkerHandleHeight);
|
||||
CHECK(contains(h, mx, a.y));
|
||||
CHECK(contains(h, mx, a.y + kMarkerHandleHeight - 1));
|
||||
CHECK(!contains(h, mx, a.y + kMarkerHandleHeight)); // below the strip is the column's
|
||||
}
|
||||
|
||||
// The whole reason the handle exists: two markers that share a frame both stay reachable —
|
||||
// markerAtPoint gives its full-height column to the first in draw order, and the handle owns
|
||||
// the strip above. Without the split, the loser could never be dragged apart again.
|
||||
static void testCoincidentMarkersStayIndependentlyGrabbable() {
|
||||
const Rect a = wideArea();
|
||||
const std::int64_t markers[2] = {250, 250};
|
||||
const int mx = frameToX(overlayOf(a), 1000, 250);
|
||||
// The column resolves to the first marker at every height, including the top strip.
|
||||
CHECK(markerAtPoint(overlayOf(a), 1000, markers, 2, mx, a.y) == 0);
|
||||
CHECK(markerAtPoint(overlayOf(a), 1000, markers, 2, mx, a.bottom() - 1) == 0);
|
||||
// The handle, asked first, resolves the second one in that same top strip.
|
||||
CHECK(contains(markerHandleRect(overlayOf(a), 1000, 250), mx, a.y));
|
||||
CHECK(!contains(markerHandleRect(overlayOf(a), 1000, 250), mx, a.bottom() - 1));
|
||||
}
|
||||
|
||||
static void testMarkerHandleClipsIntoTheArea() {
|
||||
const Rect a = wideArea();
|
||||
// At the last frame the marker maps to right(); an unclipped tab would claim pixels
|
||||
// outside the band the caller already hit-tested.
|
||||
const Rect hi = markerHandleRect(overlayOf(a), 1000, 1000);
|
||||
CHECK(hi.right() == a.right());
|
||||
CHECK(!contains(hi, a.right(), a.y));
|
||||
CHECK(contains(hi, a.right() - 1, a.y));
|
||||
// And at frame 0 it cannot reach left of the band.
|
||||
const Rect lo = markerHandleRect(overlayOf(a), 1000, 0);
|
||||
CHECK(lo.x == a.x);
|
||||
CHECK(!contains(lo, a.x - 1, a.y));
|
||||
}
|
||||
|
||||
static void testMarkerHandleOnDegenerateAreas() {
|
||||
CHECK(markerHandleRect(overlayOf(Rect{}), 1000, 0).empty());
|
||||
// A band shorter than the strip yields a handle the height of the band, never taller.
|
||||
const Rect thin = Rect{0, 0, 100, 4};
|
||||
CHECK(markerHandleRect(overlayOf(thin), 1000, 500).height == 4);
|
||||
}
|
||||
|
||||
// --- Per-lane envelope content -------------------------------------------------
|
||||
|
||||
static void testAsymmetricStereoLanesCarryDifferentContent() {
|
||||
@@ -379,6 +431,11 @@ int main() {
|
||||
testMarkerGrabReachesTheLowerStereoLane();
|
||||
testMarkerGrabInMonoSpansTheBand();
|
||||
|
||||
testMarkerHandleIsATopStripCentredOnTheMarker();
|
||||
testCoincidentMarkersStayIndependentlyGrabbable();
|
||||
testMarkerHandleClipsIntoTheArea();
|
||||
testMarkerHandleOnDegenerateAreas();
|
||||
|
||||
testAsymmetricStereoLanesCarryDifferentContent();
|
||||
testLaneEnvelopeRejectsOutOfRangeLane();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user