// 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 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; }