Widen the deck row block to 1028 so the filter tie-line is exact, and accumulate the meter's block peaks instead of sampling one in 47
This commit is contained in:
+35
-25
@@ -12,6 +12,7 @@
|
||||
// and which selections are inert).
|
||||
|
||||
#include "../src/core/instrument/ui/deck_groups.h"
|
||||
#include "../src/core/instrument/ui/master_meter.h" // kMeterColumnW: MASTER's reserve IS this
|
||||
#include "../src/core/instrument/ui/sample_bands.h"
|
||||
|
||||
#include <cmath>
|
||||
@@ -310,12 +311,15 @@ static void testTheEditorFloorIsDerivedFromTheDeckWidthBudget() {
|
||||
CHECK(kDeckRowBlockW + kDeckGroupGap + kDeckSpanningW + 2 * kPad == kEditorMinWidth);
|
||||
// The budget: what is left between the derived floor and the hard ceiling, and it is spent
|
||||
// once. A cell costs 60 of it.
|
||||
CHECK(kEditorCeilingWidth - kEditorMinWidth == 90);
|
||||
CHECK(kEditorCeilingWidth - kEditorMinWidth == 82);
|
||||
// 82 still buys one more deck cell (60), which is the only purchase the ledger promises —
|
||||
// the widen below spent 8 px of slack, not the layout's purchasing power.
|
||||
CHECK(kEditorCeilingWidth - kEditorMinWidth >= kDeckCellW);
|
||||
// The reflow's 112 px goes entirely to the waveform, so the height does not move.
|
||||
CHECK(kEditorMinHeight == 680);
|
||||
// The floor did not move to make the reflow fit — the reflow was fitted to the floor. This
|
||||
// wave spends the budget it was handed; it does not widen it.
|
||||
CHECK(kEditorMinWidth == 1190);
|
||||
// 1190 + 8: the row block was widened 1020 -> 1028 to put the two rows' filter edges on
|
||||
// one pixel, which is the only reason the floor moved off Γ-W1-T4's number.
|
||||
CHECK(kEditorMinWidth == 1198);
|
||||
CHECK(kEditorMinWidth <= kEditorCeilingWidth);
|
||||
CHECK(kEditorMinHeight <= 720);
|
||||
// And the row block really is what the two rows justify inside — derived from the floor
|
||||
@@ -382,11 +386,11 @@ static void testBothRowsAndTheSpanningDeckFitTheBudget() {
|
||||
}
|
||||
|
||||
// The gutters the justification law produces at the floor, and the alignment they buy. The
|
||||
// SPEC (instrument-control-surface.md §1.2/§1.3) states row 1 as 12/14/14 with both filter
|
||||
// edges at x = 636; equal division of 40 px over three gutters cannot produce that, so what is
|
||||
// pinned here is what the LAW produces — 14/13/13, filter edge 638 — with row 2 exact at
|
||||
// 72/72 and 636. The 2 px is flagged for review; a row block of 1028 (floor 1198, still under
|
||||
// the 1280 ceiling) is the width at which the law puts both edges on 640.
|
||||
// At the 1028 block the justification law makes the tie-line exact by arithmetic rather than
|
||||
// by a special rule: row 1's slack is 48 over three gutters (16 each, no residue) and row 2's
|
||||
// is 152 over two (76 each), which lands both filter edges on 640. Only two of the three
|
||||
// properties §1.3 once claimed can hold at once — a smallest gutter of exactly kDeckGroupGap
|
||||
// needs a 1016 block — and 12 is a floor, not a target, so 16 satisfies the real rule.
|
||||
static void testGutterArithmeticAndTheFilterTieLineAtTheFloor() {
|
||||
const std::vector<DeckGroupDesc> g = sampleDeckGroups(PlayMode::Gate);
|
||||
const DeckLayout dl = layoutDeck(g, kPad, 0, kAvailAtMinWidth);
|
||||
@@ -394,24 +398,25 @@ static void testGutterArithmeticAndTheFilterTieLineAtTheFloor() {
|
||||
const auto box = [&](int id) {
|
||||
return dl.groups[static_cast<std::size_t>(indexOfGroup(g, id))].box;
|
||||
};
|
||||
// Row 1: flush left, flush right on the block, gutters 14/13/13.
|
||||
// Row 1: flush left, flush right on the block, and three EQUAL gutters — 48 divides by 3
|
||||
// with no residue, so no gutter carries a leftover pixel.
|
||||
CHECK(box(kGroupPitch).x == kPad);
|
||||
CHECK(box(kGroupFilter).x - box(kGroupPitch).right() == 14);
|
||||
CHECK(box(kGroupVelocity).x - box(kGroupFilter).right() == 13);
|
||||
CHECK(box(kGroupVoice).x - box(kGroupVelocity).right() == 13);
|
||||
CHECK(box(kGroupFilter).x - box(kGroupPitch).right() == 16);
|
||||
CHECK(box(kGroupVelocity).x - box(kGroupFilter).right() == 16);
|
||||
CHECK(box(kGroupVoice).x - box(kGroupVelocity).right() == 16);
|
||||
CHECK(box(kGroupVoice).right() == kPad + kDeckRowBlockW);
|
||||
|
||||
// Row 2: flush left, flush right, and its two gutters exactly equal — the property the
|
||||
// 1020 block was chosen for, and the one it does deliver.
|
||||
// Row 2: flush left, flush right, two gutters exactly equal.
|
||||
CHECK(box(kGroupPitchEnv).x == kPad);
|
||||
CHECK(box(kGroupFilterEnv).x - box(kGroupPitchEnv).right() == 72);
|
||||
CHECK(box(kGroupAmpEnv).x - box(kGroupFilterEnv).right() == 72);
|
||||
CHECK(box(kGroupFilterEnv).x - box(kGroupPitchEnv).right() == 76);
|
||||
CHECK(box(kGroupAmpEnv).x - box(kGroupFilterEnv).right() == 76);
|
||||
CHECK(box(kGroupAmpEnv).right() == kPad + kDeckRowBlockW);
|
||||
|
||||
// The filter tie-line, block-relative. Row 2 lands on the specified 636; row 1 lands 2 px
|
||||
// past it. See this test's header.
|
||||
CHECK(box(kGroupFilterEnv).right() - kPad == 636);
|
||||
CHECK(box(kGroupFilter).right() - kPad == 638);
|
||||
// The tie-line, block-relative: both filter edges on ONE pixel, which is what the widen
|
||||
// bought. Pinned as an identity too, so a group-width change cannot pass by moving both.
|
||||
CHECK(box(kGroupFilterEnv).right() - kPad == 640);
|
||||
CHECK(box(kGroupFilter).right() - kPad == 640);
|
||||
CHECK(box(kGroupFilter).right() == box(kGroupFilterEnv).right());
|
||||
|
||||
// MASTER is right-anchored outside the block, one kDeckGroupGap clear of it.
|
||||
CHECK(box(kGroupMaster).x - box(kGroupVoice).right() == kDeckGroupGap);
|
||||
@@ -450,8 +455,8 @@ static void testGuttersHoldTheirMinimumAndTheTieLineDriftsAboveTheFloor() {
|
||||
CHECK(drift <= lastDrift);
|
||||
lastDrift = drift;
|
||||
}
|
||||
// It really does open up, and by far more than the 2 px it starts at — separation
|
||||
// above the floor is the accepted outcome, not a near-miss to be pinned back.
|
||||
// It really does open up: the tie-line is exact AT the floor and separates above it,
|
||||
// which is the accepted outcome rather than a near-miss to be pinned back.
|
||||
CHECK(lastDrift < -50);
|
||||
}
|
||||
}
|
||||
@@ -467,8 +472,13 @@ static void testTheMasterDeckInteriorLandsOnBothRowBaselines() {
|
||||
|
||||
CHECK(m.box.width == 142);
|
||||
CHECK(m.box.height == 216);
|
||||
// 6 + 60 + 8 + 62 + 6 — the decomposition, not just the total.
|
||||
CHECK(kDeckGroupPadX + kDeckCellW + kDeckColumnGap + 62 + kDeckGroupPadX == 142);
|
||||
// 6 + 60 + 8 + 62 + 6 — the decomposition, not just the total, and the 62 is the meter
|
||||
// module's own kMeterColumnW rather than a copy of it. That link is the whole point: the
|
||||
// column is banked to GROW (§1.2), and a reserve that did not track it would leave the
|
||||
// interior underfilling or overrunning with every test still green.
|
||||
CHECK(kDeckGroupPadX + kDeckCellW + kDeckColumnGap + kMeterColumnW + kDeckGroupPadX == 142);
|
||||
CHECK(m.column.id == cell(DeckParam::kMasterMeter));
|
||||
CHECK(m.column.box.width == kMeterColumnW);
|
||||
|
||||
// One cell drawn (gain) and one slot RESERVED below it: the reserve is height at a fixed
|
||||
// position and draws nothing.
|
||||
|
||||
Reference in New Issue
Block a user