Q-W1 pt2: core/shell/app relocation + sub-namespaces; one concrete ui::Rect (LTRB fork retired); slot_map split from bank_book; BankIndex→BankModel; 59/59 green
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
// embed_strip.cpp — see embed_strip.h. Pure math; no host types.
|
||||
|
||||
#include "core/instrument/ui/embed_strip.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace reasampler::instrument::ui {
|
||||
|
||||
namespace {
|
||||
|
||||
// Clamp a MIDI note to [0, kEmbedKeyCount-1].
|
||||
int clampNote(int n) {
|
||||
if (n < 0) return 0;
|
||||
if (n > kEmbedKeyCount - 1) return kEmbedKeyCount - 1;
|
||||
return n;
|
||||
}
|
||||
|
||||
// Map a key boundary in [0, kEmbedKeyCount] to an x pixel inside a band of the given
|
||||
// left/width. keyEdge is a boundary (0..128), so keyEdge==128 maps to the band's right.
|
||||
// Integer math, floored — a zone's left uses floor(low) and its right uses floor(high+1),
|
||||
// which tiles adjacent zones without a seam.
|
||||
int keyEdgeToX(int bandLeft, int bandWidth, int keyEdge) {
|
||||
if (keyEdge <= 0) return bandLeft;
|
||||
if (keyEdge >= kEmbedKeyCount) return bandLeft + bandWidth;
|
||||
return bandLeft + (keyEdge * bandWidth) / kEmbedKeyCount;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
EmbedLayout layoutEmbed(int w, int h) {
|
||||
const int cw = std::max(0, w);
|
||||
const int ch = std::max(0, h);
|
||||
|
||||
EmbedLayout out;
|
||||
|
||||
// The level band takes a fixed height at the bottom, but never so much that the keymap
|
||||
// above it falls below its minimum (or that the band exceeds the area). On a very short
|
||||
// area the band yields to the keymap entirely.
|
||||
int bandH = std::min(kEmbedLevelBandHeight, ch);
|
||||
if (ch - bandH < kEmbedKeymapMinHeight) {
|
||||
bandH = std::max(0, ch - kEmbedKeymapMinHeight);
|
||||
}
|
||||
const int keymapBottom = ch - bandH;
|
||||
|
||||
out.keymap = Rect::ltrb(0, 0, cw, keymapBottom);
|
||||
out.levelBand = Rect::ltrb(0, keymapBottom, cw, ch);
|
||||
return out;
|
||||
}
|
||||
|
||||
Rect zoneSegmentRect(const EmbedLayout& layout, int lowNote, int highNote) {
|
||||
const Rect& band = layout.keymap;
|
||||
const int bandWidth = std::max(0, band.width);
|
||||
|
||||
int lo = clampNote(lowNote);
|
||||
int hi = clampNote(highNote);
|
||||
if (lo > hi) lo = hi; // defensive: a malformed zone collapses rather than inverts
|
||||
|
||||
const int leftX = keyEdgeToX(band.x, bandWidth, lo);
|
||||
const int rightX = keyEdgeToX(band.x, bandWidth, hi + 1);
|
||||
return Rect::ltrb(leftX, band.y, std::max(leftX, rightX), band.bottom());
|
||||
}
|
||||
|
||||
int zoneAtPoint(const EmbedLayout& layout, const EmbedZone* zones, int zoneCount, int x,
|
||||
int y) {
|
||||
if (zoneCount <= 0 || zones == nullptr) return -1;
|
||||
if (!contains(layout.keymap, x, y)) return -1;
|
||||
// First covering zone in draw order wins (first-match, mirroring the core's resolve).
|
||||
for (int i = 0; i < zoneCount; ++i) {
|
||||
const Rect r = zoneSegmentRect(layout, zones[i].lowNote, zones[i].highNote);
|
||||
if (contains(r, x, y)) return i;
|
||||
}
|
||||
return -1; // on the band but on an uncovered key
|
||||
}
|
||||
|
||||
Rect levelFillRect(const EmbedLayout& layout, double level) {
|
||||
const Rect& band = layout.levelBand;
|
||||
if (band.width <= 0 || band.height <= 0) return Rect{};
|
||||
double l = level;
|
||||
if (l < 0.0) l = 0.0;
|
||||
if (l > 1.0) l = 1.0;
|
||||
const int fillW = static_cast<int>(l * band.width);
|
||||
if (fillW <= 0) return Rect{};
|
||||
return Rect::ltrb(band.x, band.y, band.x + fillW, band.bottom());
|
||||
}
|
||||
|
||||
} // namespace reasampler::instrument::ui
|
||||
Reference in New Issue
Block a user