Continuous custom-curve evaluation¶
BezierCurve.CaptureEvaluator() returns one immutable NormalizedEvaluator.
Capture it once for a coupled stick operation and call its
TryEvaluateNormalized(double input, out double output) for both finite
normalized magnitudes. The mapper remains responsible for sign, directional
caps and the final target encoding. The public convenience method captures
the current evaluator for a single value.
Cold InitBezierCurve builds and publishes the evaluator after the original
byte lookup table is initialized. AsString and CustomDefinition alone do
not publish changes. A captured predecessor cannot observe new coefficients
or sample data halfway through an operation. Evaluation does not read mutable
LUT/compiler fields, allocate, lock, or perform I/O.
The implementation preserves the local source's GRE/Mika-N MIT attribution
and curve semantics: fixed endpoints (0,0)/(1,1), custom X controls in
[0,1], output clamping for Y overshoot, linear definitions, and special
99,91..95 enhanced-precision/quadratic/cubic/ease-out formulas. The continuous
inverse uses eleven cold samples, at most eight safeguarded Newton steps and
thirty-two bisection steps. This is not interpolation of a byte table.
Residuals are evaluated around the midpoint or reversed endpoint to avoid
rounding a nearly flat X value to the requested input before subtracting.
The exact degenerate cubic X forms (0,0), (1,1) and (1,0) use their
closed-form cube-root inverses. Y near the midpoint is also centered, preserving
opposing large-control behavior before the existing output clamp.
Nonfinite or out-of-range normalized input returns false and output zero.
Invalid/nonfinite numeric controls or overflowing coefficients return false
while preserving a valid input as linear fallback. The caller must not turn a
valid stick magnitude into center merely because its custom curve is invalid.
Before initialization, the evaluator is linear.
The existing profile-string parser is unchanged; this API does not introduce
a different interpretation of strings accepted by legacy initialization.
The legacy byte LUT algorithm, its original inverse iterations, rounding, truncation and asymmetric stick mirroring are unchanged. Continuous values are not promised to equal a rounded byte at every location; retaining the original sub-byte information is the purpose of the new API.
BezierCurveNormalizedTests checks independent inverse results across the
complete twelve-bit magnitude grid, distinct sixteen-bit magnitudes, flat
slopes/endpoints, special modes, overshoot, rejection/fallback behavior,
immutable paired capture, zero managed hot-path allocations and every legacy
byte against the frozen original LUT solver. These are source/numeric tests,
not end-to-end input-latency or hardware validation.
The mapped Switch 2 scroll-tap, direction-tap and stick-assist lanes likewise
accept finite double profile coordinates in [0,255]. Byte callers convert
exactly. Geometry, scroll steps and assist velocity do not first round to a
byte. Existing center128, scroll3% radial deadzone, assist inclusive127..129
neutral band, timing and source/profile lifetime rules are unchanged. Invalid
coordinates clear the lane baseline and require a fresh baseline before
emission. Switch2MappedStickLanePrecisionTests covers fractional boundaries,
all legacy byte-sector pairs and byte assist magnitudes, pulse expiry,
standalone selection, rebaselining and allocation-free execution.