easing.h maps a timing's progress, from 0 to 1, onto eased progress. An mtwnEasing is a plain value: a maker checks its parameters and returns a status, and mtwnSampleEasing samples it, giving the eased progress through a pointer and its own status. It may be copied freely; what it references stays the host's: a host function's context must outlive every copy, and the points of linear() every copy the host keeps, since a transition or a clip copies the points in when it takes the easing.
mtwnEasing ease;
mtwnResult made = mtwnMakeCssEasing(mtwn_cssEaseInOut, &ease); // CSS's ease-in-out
double eased = 0.0;
mtwnResult sampled = mtwnSampleEasing(&ease, 0.25, false, &eased); // a quarter of the way
mtwnEasing back;
made = mtwnMakeBackEasing(mtwn_easeOut, 1.70158, &back); // GSAP's back.out
The makers:
| Maker | Curves |
|---|---|
mtwnMakeLinearEasing |
The identity; it cannot fail. |
mtwnMakePowerEasing |
p^k: quad, cubic, quart, quint and any exponent above 0. GSAP's power(n) is k = n + 1. |
mtwnMakePennerEasing |
Sine, expo, circ and bounce. |
mtwnMakeBackEasing |
Back, with GSAP's and Penner's overshoot (1.70158 by default). |
mtwnMakeElasticEasing |
Elastic, with GSAP's amplitude and period. |
mtwnMakeCssEasing |
CSS's keywords: linear, ease, ease-in, ease-out, ease-in-out, step-start, step-end. |
mtwnMakeCubicBezierEasing |
CSS's cubic-bezier(x1, y1, x2, y2). |
mtwnMakeStepsEasing |
CSS's steps(n, position), every jump position. |
mtwnMakeLinearPointsEasing |
CSS's linear() with points. |
mtwnMakeRoughEasing |
A rough curve from a seed, below. |
mtwnMakeSpringEasing |
A spring over a duration (see physics.md). |
mtwnMakeHostEasing |
A function of the host's. |
Every kind takes the same four modes: in (the curve as defined), out (1 - f(1 - p)), in-out and out-in. mtwnSetEaseMode changes the mode of an easing already made.
q 2^-10, where q is the larger of the amplitude and 1, and set it there, so they differ from this one by at most q (1 + q) 2^-10 / (1 - q 2^-10).mtwnSampleEasing is the timing's before flag: in the before phase, steps() landing exactly on a step takes the step below, as CSS says. Pass false when sampling a curve on its own.linear() canonicalizes its points in place as CSS does (missing inputs spread, decreasing inputs raised), so the array is rewritten; an input of NaN is a stop given without a percentage. The easing reads the points where they are: changed afterwards, even out of order, while finite they give an unspecified but finite value. A transition or a clip copies them when it takes the easing, and refuses points the host put out of their canonical order.mtwnContext_GetMisuse). An infinity from the function is kept: a transition or a track between equal ends stays at them, and one between other ends is held at the largest finite float where a target or a table stores it.mtwn_errorInvalid), and mtwnSampleEasing refuses a progress that is not finite. A host never writes an easing's fields; a struct it zeroes is the linear easing.MTWN_MAX_FIT_POINTS points could not follow it, and the makers refuse them.mtwnSampleCubicBezierEasing samples a cubic bezier, or the linear easing, as mtwnSampleEasing does, to the bit, without its dispatch over every kind, which keeps every curve linked. A program that samples only cubic beziers through it is smaller: with GCC 14, -Os, link-time optimization and unused sections dropped, making one cubic-bezier() and sampling it links 1.4 kB of code on x86-64 against 7.9 kB through mtwnSampleEasing. Given an easing of another kind it refuses it.mtwnIsEasingMonotonic tells whether an easing never decreases, which a timeline's easing must. Back, elastic, bounce, springs and host functions are never monotonic for it. In-out and out-in join a curve's halves at 1/2, so linear() is monotonic in in-out only if it ends at 1 or below, and in out-in only if it starts at 0 or above.mtwnSampleEasing, whose solver stops within 1e-14 of the progress and can give a value an ulp or so short of the one before. A timeline's values never decrease, so its children's time never runs back inside a forward play, and they can differ from what the same easing gives in a keyframe, a transition or mtwnSampleEasing by a few 1e-14 for CSS's keywords, more near a vertical tangent, where the solver's is the one off.A rough easing wanders from a shape at points drawn from a seed, as GSAP's RoughEase and anime.js's irregular, the same curve for a seed on every platform. It fills points the host keeps, and is then linear() through them, so its ends stay exactly 0 and 1. A taper grows or calms the offsets toward the ends; the flags clamp the curve to 0 to 1, keep it rising, or set an even zig-zag with no seed.
mtwnLinearPoint points[22]; // kept while the easing is used
mtwnRoughDef def = {.shape = mtwnMakeLinearEasing(), .strength = 0.1, .seed = 42};
def.taper = mtwn_roughTaperOut; // calm by the end
mtwnEasing rough;
mtwnResult madeRough = mtwnMakeRoughEasing(&def, points, 22, &rough);
linear()mtwnSampleEasingTable fills a table of evenly spaced samples, for a shader or a lookup; a sample past the floats' range is held at the largest finite float, as a target holds its value. mtwnFitLinearEasing turns any easing but steps into CSS linear() points within a tolerance, to hand to a browser or another engine: the points lie on the curve and the line between them is within the tolerance at 32 probes in each segment. A buffer that is too small gets mtwn_errorCapacity and the count needed, so call again with that capacity. A fit makes at most MTWN_MAX_FIT_POINTS points; a tolerance the curve cannot meet (below its rounding) ends with mtwn_errorLimit rather than with millions of them. The fit writes its points as it finds them, so a refused one leaves the buffer holding its first points, as many as fit, exactly as a larger buffer would start, and the rest as it was. Steps are refused: CSS writes them as steps().