Loop: an explicit enable, four named marks with grabbable caps, and the crossfade painted where it is actually heard
hasLoop becomes user-owned with the gestures as shortcuts onto it; no format change. START uses overlay/trace, not accent/primary, which is the waveform's own fill.
This commit is contained in:
@@ -107,7 +107,7 @@ static void testOverlayBoxIsTheWholeArea() {
|
||||
// --- Smallest-target-first: resolveWaveformClaim, the shell's own comparison chain -----
|
||||
//
|
||||
// editor_input_waveform.cpp's mouseDownWaveform resolves a click among a contour node (a fixed
|
||||
// pick box), the crossfade tab, and a marker's full-height column by calling
|
||||
// pick box), a mark's CAP, and a mark's full-height column by calling
|
||||
// resolveWaveformClaim with each candidate's own target area; the smallest hit wins. These
|
||||
// tests build the real geometry over the pure primitives the shell composes, then feed it into
|
||||
// resolveWaveformClaim itself, so a reverted node-first/marker-first/tab-first ordering fails
|
||||
@@ -215,7 +215,45 @@ static void testContourNodeBeatsALoopMarkerAtTheirSharedPixelButNotElsewhere() {
|
||||
WaveformClaimant::kMarker);
|
||||
}
|
||||
|
||||
// The only live tie: the crossfade tab (<=110) can equal the node (169) only off-geometry, but
|
||||
// Case (d): every mark now carries a cap, which RESOLVES the long-open "staged-envelope-node
|
||||
// shadow at zero-attack" wart. A zero-attack AttackEnd node sits at the canvas's top-left — the
|
||||
// same pixel a START marker at frame 0 draws at — and used to win the click outright, because
|
||||
// the marker's only target there was its full-height column. START's cap is the same 11x10 tab
|
||||
// the crossfade always had, so the node no longer shadows it. The cap slot's nominal area is
|
||||
// unchanged by the change (every mark's cap is one markerHandleRect), which is why the
|
||||
// arbitration itself needed no re-tuning: cap < node < column still holds.
|
||||
static void testAMarkCapOutranksACoincidentEnvelopeNodeInTheTopStrip() {
|
||||
const Rect a = Rect{20, 10, 1000, kWaveformMinHeight};
|
||||
const OverlayArea overlay = overlayOf(a);
|
||||
const std::int64_t frames = 100000;
|
||||
// START at frame 0: its cap clips against the band's left edge, and a zero-attack node is
|
||||
// drawn on that same corner.
|
||||
const Rect cap = markerHandleRect(overlay, frames, 0);
|
||||
CHECK(!cap.empty());
|
||||
CHECK(contains(cap, a.x, a.y));
|
||||
|
||||
// NOMINAL, matching what the shell feeds the arbitration — the clipped tab at frame 0 is
|
||||
// the worst case for the cap, and it still wins on the nominal number the shell uses.
|
||||
const std::int64_t capArea =
|
||||
static_cast<std::int64_t>(2 * kMarkerHandleHalfWidth + 1) * kMarkerHandleHeight;
|
||||
const std::int64_t columnArea =
|
||||
static_cast<std::int64_t>(2 * kMarkerGrabWidth + 1) * a.height;
|
||||
CHECK(capArea < kNodeArea);
|
||||
CHECK(kNodeArea < columnArea);
|
||||
|
||||
const WaveformClaim node{true, kNodeArea};
|
||||
const WaveformClaim capClaim{true, capArea};
|
||||
const WaveformClaim column{true, columnArea};
|
||||
CHECK(resolveWaveformClaim(node, capClaim, column, SplineGesture::kLeft) ==
|
||||
WaveformClaimant::kTab);
|
||||
// And the node keeps everything below the cap strip, which is where it is actually drawn
|
||||
// for any non-degenerate envelope.
|
||||
CHECK(!contains(cap, a.x, a.y + kMarkerHandleHeight));
|
||||
CHECK(resolveWaveformClaim(node, WaveformClaim{}, column, SplineGesture::kLeft) ==
|
||||
WaveformClaimant::kNode);
|
||||
}
|
||||
|
||||
// The only live tie: a mark's cap (<=110) can equal the node (169) only off-geometry, but
|
||||
// tab-vs-marker ties at overlay height 10 (kMarkerHandleHeight), where the tab's 11x10 strip
|
||||
// (110) equals a marker column's 11 * 10 (110) — the tab wins, matching check order.
|
||||
static void testTabWinsAGenuineTabVersusMarkerTie() {
|
||||
@@ -269,6 +307,7 @@ int main() {
|
||||
testFreshRampDownEndpointBeatsTheStartMarkerAtFrameZero();
|
||||
testCrossfadeTabBeatsAContourNodeNearItsTopStrip();
|
||||
testContourNodeBeatsALoopMarkerAtTheirSharedPixelButNotElsewhere();
|
||||
testAMarkCapOutranksACoincidentEnvelopeNodeInTheTopStrip();
|
||||
testTabWinsAGenuineTabVersusMarkerTie();
|
||||
testNoHitAnywhereFallsThroughToNone();
|
||||
testAMissedCandidateNeverWinsOnADegenerateZeroArea();
|
||||
|
||||
Reference in New Issue
Block a user