f39fb1b145
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.
66 lines
2.2 KiB
C++
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
|