Files
reasampler/src/core/instrument/ui/curve_tessellate.cpp
T
daniel f39fb1b145 Γ-W1-T3: staged contour traces draw the curve their exponent defines
New pure curve_tessellate joins the overlay's node vertices through curveMap,
one sample per pixel column; the knot no longer floats off its own trace.
2026-08-02 13:47:19 -04:00

66 lines
2.2 KiB
C++

// curve_tessellate.cpp — see curve_tessellate.h. Pure geometry; no host types.
#include "core/instrument/ui/curve_tessellate.h"
#include <algorithm>
#include <cstdlib>
#include "core/util/curve_law.h"
namespace reasampler::instrument::ui {
using StrokePoint = reasampler::ui::StrokePoint;
namespace {
StrokePoint pt(double x, double y) {
return StrokePoint{static_cast<float>(x), static_cast<float>(y)};
}
// The samples strictly BETWEEN two nodes. The endpoints are the caller's, so a shared node is
// emitted once and the polyline carries no zero-length joint.
void appendInterior(int x0, int y0, int x1, int y1, double exponent,
std::vector<StrokePoint>& out) {
const int span = std::abs(x1 - x0);
if (span < 2 || y1 == y0 || exponent == util::kCurveNeutral) return;
const double dx = static_cast<double>(x1 - x0);
const double dy = static_cast<double>(y1 - y0);
for (int i = 1; i < span; ++i) {
// phi is exact at every column, so x lands on the integer column and the last interior
// sample is one column short of the end node.
const double phi = static_cast<double>(i) / static_cast<double>(span);
out.push_back(pt(static_cast<double>(x0) + dx * phi,
static_cast<double>(y0) + dy * util::curveMap(phi, exponent)));
}
}
} // namespace
double segmentCurve(const StageEnvelope& env, EnvNode endNode) {
switch (endNode) {
case EnvNode::AttackEnd: return env.attackCurve;
case EnvNode::DecayEnd: return env.decayCurve;
case EnvNode::ReleaseEnd: return env.releaseCurve;
default: return util::kCurveNeutral;
}
}
void buildEnvelopeTrace(const std::vector<EnvVertex>& poly, const StageEnvelope& env, int xLo,
int xHi, std::vector<StrokePoint>& out) {
out.clear();
bool started = false;
int px = 0;
int py = 0;
for (const EnvVertex& v : poly) {
if (v.knot) continue;
const int x = std::max(xLo, std::min(xHi, v.x));
if (started) appendInterior(px, py, x, v.y, segmentCurve(env, v.node), out);
out.push_back(pt(x, v.y));
started = true;
px = x;
py = v.y;
}
}
} // namespace reasampler::instrument::ui