instrument: spline EGs — hard points on the one shared spline, a drawn contour per envelope beside its staged state, payload v13
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// Standalone tests for reasampler::instrument::ui::spline_edit — no VST3, no REAPER, no
|
||||
// framework. Asserts the ONE point-editing grammar both spline consumers route through:
|
||||
// left-click grabs a node and adds in empty space, right-click deletes, control-click toggles
|
||||
// hard/smooth, and a click outside the mapping box resolves to nothing unless it lands on a
|
||||
// node's pick radius (so the popup's inset ring can grab but never add).
|
||||
|
||||
#include "../src/core/instrument/ui/spline_edit.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
using namespace reasampler::instrument::ui;
|
||||
using namespace reasampler::instrument::engine;
|
||||
|
||||
static int g_fail = 0;
|
||||
#define CHECK(cond) do { if(!(cond)) { \
|
||||
std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)
|
||||
|
||||
static const VelocityCurve::Box kBox{100, 50, 127, 101};
|
||||
|
||||
// A three-point curve whose interior node sits well away from both endpoints.
|
||||
static VelocityCurve threePoint() {
|
||||
VelocityCurve c = VelocityCurve::rampDown();
|
||||
c.addPoint(64.0, 0.5);
|
||||
return c;
|
||||
}
|
||||
|
||||
static void testLeftClickOnANodeGrabsIt() {
|
||||
const VelocityCurve c = threePoint();
|
||||
const auto px = c.pixelFromPoint(kBox, c.points()[1]);
|
||||
const SplineEdit e = resolveSplineEdit(c, kBox, SplineGesture::kLeft, px.x, px.y);
|
||||
CHECK(e.kind == SplineEditKind::kGrab);
|
||||
CHECK(e.index == 1);
|
||||
}
|
||||
|
||||
static void testLeftClickInEmptySpaceAdds() {
|
||||
const VelocityCurve c = VelocityCurve::rampDown();
|
||||
// Well away from either endpoint's drawn position and from the traced line's nodes.
|
||||
const SplineEdit e = resolveSplineEdit(c, kBox, SplineGesture::kLeft, 160, 60);
|
||||
CHECK(e.kind == SplineEditKind::kAdd);
|
||||
CHECK(e.index == -1); // no point yet — the caller creates it
|
||||
}
|
||||
|
||||
static void testRightClickOnANodeDeletesAndElsewhereDoesNothing() {
|
||||
const VelocityCurve c = threePoint();
|
||||
const auto px = c.pixelFromPoint(kBox, c.points()[1]);
|
||||
const SplineEdit hit = resolveSplineEdit(c, kBox, SplineGesture::kRight, px.x, px.y);
|
||||
CHECK(hit.kind == SplineEditKind::kDelete);
|
||||
CHECK(hit.index == 1);
|
||||
// Right-click on empty canvas must NOT add — delete is the only thing the gesture means.
|
||||
const SplineEdit miss = resolveSplineEdit(c, kBox, SplineGesture::kRight, 160, 60);
|
||||
CHECK(miss.kind == SplineEditKind::kNone);
|
||||
}
|
||||
|
||||
static void testControlClickOnANodeTogglesHardAndElsewhereDoesNothing() {
|
||||
const VelocityCurve c = threePoint();
|
||||
const auto px = c.pixelFromPoint(kBox, c.points()[1]);
|
||||
const SplineEdit hit = resolveSplineEdit(c, kBox, SplineGesture::kControlLeft, px.x, px.y);
|
||||
CHECK(hit.kind == SplineEditKind::kToggleHard);
|
||||
CHECK(hit.index == 1);
|
||||
const SplineEdit miss = resolveSplineEdit(c, kBox, SplineGesture::kControlLeft, 160, 60);
|
||||
CHECK(miss.kind == SplineEditKind::kNone);
|
||||
}
|
||||
|
||||
// The popup draws its box inset inside a border; a click in that ring must be able to GRAB an
|
||||
// endpoint handle (which is drawn on the box edge, within the pick radius of the ring) but must
|
||||
// never ADD — an added point there clamps onto an endpoint's x and stacks an undeletable
|
||||
// duplicate.
|
||||
static void testOutsideTheBoxGrabsButNeverAdds() {
|
||||
const VelocityCurve c = VelocityCurve::rampDown();
|
||||
const auto first = c.pixelFromPoint(kBox, c.points()[0]);
|
||||
const SplineEdit ring =
|
||||
resolveSplineEdit(c, kBox, SplineGesture::kLeft, first.x - 3, first.y - 3);
|
||||
CHECK(ring.kind == SplineEditKind::kGrab);
|
||||
CHECK(ring.index == 0);
|
||||
// Far outside, on no node at all.
|
||||
const SplineEdit away = resolveSplineEdit(c, kBox, SplineGesture::kLeft, kBox.left - 60,
|
||||
kBox.top - 40);
|
||||
CHECK(away.kind == SplineEditKind::kNone);
|
||||
}
|
||||
|
||||
static void testDegenerateBoxResolvesToNothing() {
|
||||
const VelocityCurve c = threePoint();
|
||||
CHECK(resolveSplineEdit(c, VelocityCurve::Box{0, 0, 0, 40}, SplineGesture::kLeft, 0, 0)
|
||||
.kind == SplineEditKind::kNone);
|
||||
CHECK(resolveSplineEdit(c, VelocityCurve::Box{0, 0, 40, 1}, SplineGesture::kLeft, 0, 0)
|
||||
.kind == SplineEditKind::kNone);
|
||||
}
|
||||
|
||||
// The overlay's box is the FULL area — no inset — so the contour spans the sample's whole
|
||||
// drawn width 1:1 with its time axis.
|
||||
static void testOverlayBoxIsTheWholeArea() {
|
||||
const OverlayArea area{Rect::ltrb(12, 40, 812, 240)};
|
||||
const VelocityCurve::Box box = splineOverlayBox(area);
|
||||
CHECK(box.left == 12);
|
||||
CHECK(box.top == 40);
|
||||
CHECK(box.width == 800);
|
||||
CHECK(box.height == 200);
|
||||
}
|
||||
|
||||
int main() {
|
||||
testLeftClickOnANodeGrabsIt();
|
||||
testLeftClickInEmptySpaceAdds();
|
||||
testRightClickOnANodeDeletesAndElsewhereDoesNothing();
|
||||
testControlClickOnANodeTogglesHardAndElsewhereDoesNothing();
|
||||
testOutsideTheBoxGrabsButNeverAdds();
|
||||
testDegenerateBoxResolvesToNothing();
|
||||
testOverlayBoxIsTheWholeArea();
|
||||
if (g_fail == 0) std::printf("spline_edit: all tests passed\n");
|
||||
return g_fail == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user