Skip to content

Releases

v1.1.0

Choose a tag to compare

@rawframe-release rawframe-release released this 04 Nov 06:25
8842cbb

1.1.0 (2026-11-04)

This release makes mtwnCreateClipPlayer check its bindings in time linear in their count, where it compared each binding with every one before it (#292). A player of 2^20 bindings took 194 s to make at -O2 and now takes 12 ms. At the old rate, the 2^24 slots a clip may have would have taken about 11 hours. Two refusals of a def that binds a slot twice change their status, as below. MTWN_ABI_VERSION and a shared library's SOVERSION are now 1.1. A context def stamped by 1.0.0's headers is refused with mtwn_errorVersion, so a program built against 1.0.0 is rebuilt; its source builds unchanged. No value, velocity or event record changes.

Changed

  • mtwnCreateClipPlayer checks its refusals in this order, which its header now states: an invalid def, then the rate's limit, then the context's capacities of players and of bindings, and a slot bound twice last (#292). A def that binds a slot twice changes its status only where another refusal also applies:
    • with the context's players or bindings full, mtwn_errorCapacity, not counted as misuse, where it was mtwn_errorInvalid, counted;
    • with a rate past MTWN_RATE_LIMIT, mtwn_errorLimit, where it was mtwn_errorInvalid; both are counted as misuse.
  • The clips guide and mtwnClipTrack's header say that a track read with mtwnClip_GetTrack points into the clip's storage. The values layer takes it as it takes a track the host made, reading only within the counts the track states, so the host keeps it as it was read (#299).

v1.0.0

Choose a tag to compare

@rawframe-release rawframe-release released this 03 Nov 14:03
990fc0e

1.0.0 (2026-11-03)

This is the first release of 1. Maul Tween's API is settled, and from here it ships only minor and patch releases of 1 (#273). Every function, type, enum value, macro and struct field in include/maul-tween/ keeps its name, signature and meaning across 1.y. The binary interface holds within a minor version. The API is the same as 0.17.0's, so a program built against 0.17.0 needs only a rebuild. MTWN_ABI_VERSION and a shared library's SOVERSION are now 1.0, and a context def stamped by 0.17.0's headers is refused with mtwn_errorVersion. No value, velocity or event record changes.

Changed

  • The getting started guide's Compatibility section states the promise 1.y keeps (#273):
    • what it covers, and the fields of the structs a maker fills, which it leaves out;
    • the binary interface within a minor version, with MTWN_ABI_VERSION and the SOVERSION at major.minor, and the CMake package taking any later 1.y;
    • a changelog line for any change of results;
    • no stored format to keep.
  • The easing guide and mtwnMakeElasticEasing's header give the largest difference from GSAP's and tween.js's elastic for every amplitude (#272). The new bound is q (1 + q) 2^-10 / (1 - q 2^-10), with q the larger of the amplitude and 1. The old bound, a (1 + a) 2^-10, was too small below amplitude 1 and at large amplitudes. The curves do not change; test_rival_easing checks 90 of the rivals' curves against the new bound.

v0.17.0

Choose a tag to compare

@rawframe-release rawframe-release released this 03 Nov 10:23
4364725

0.17.0 (2026-11-03)

This release changes two results to follow the references they name, ahead of 1.0.0. A finished player given a new rate runs at it from the time it holds, so a positive rate keeps it finished at its end where it jumped back (#264). A path's direction through a hold or a step is the way the path goes on, as SVG 2's path directionality takes it, where it was the way it came (#266). The clips guide's GSAP porting notes now cover grids with fewer items than columns and every way a clip's positions differ from GSAP's timeline, each checked against GSAP 3.15.0's outputs (#260, #262). Results change for a finished player given a positive rate (its time, the values it writes and its records), and for the direction and angle a host asks of a timed path through a hold or a step.

Changed

  • Breaking: mtwnClipPlayer_SetRate on a finished player runs it at the new rate from the time it holds, its start time set again from it, start = now - current / rate (#264). A positive rate keeps the player finished there. It kept the start time it finished with, as Web Animations' setting of the playback rate leaves it, so the next update put it back at (now - start) * rate: a 2 s clip finished at 12 s and given 0.5 at 12.5 s jumped back to 1.3 s and finished again at 14 s.
    • A finish after the change is no longer taken back, and the event records change with it: no iteration or end is crossed again.
    • A negative rate plays the player back from there at once, as before. Every other state takes the rate as before, by Web Animations' seamless update of the playback rate.
    • The specification's own seamless procedure would instead keep a finished player at its end for as long as it stood finished before a negative rate moves it. docs/internals/timing.md names this, and a running player's pending rate, as the two places the players depart from the specification's text.
    • tools/oracle/player_oracle.py writes Level 1's player procedures again from the specification, with those two departures, and test_player matches its 300 scripts to the bit.
  • Breaking: mtwnComputePathDirection and mtwnComputePathAngle give a timed hold (a segment of no length between keyframes at different offsets) and a step the direction of the nearest later segment with a length, at its start, as SVG 2 moves past a segment of no length (#266). They gave the nearest earlier one, at its end. A path right to (1, 0), held there from 0.4 to 0.6 and then up, faced along x through the hold, and now faces up from 0.4.
    • Only at the path's end, where no segment with a length follows, do they still take the earlier one. A plain corner, a path without holds or steps, and every path at constant speed give the same bits.
    • The header and the values guide say that a cusp's outgoing direction is the library's choice, since SVG 2 leaves it open, and that Chrome takes the incoming direction at every boundary, against SVG 2's text.
  • The clips guide's porting note for staggers covers a grid with fewer items than columns (#260): GSAP lays it out as one row of its items and spreads each over count - 1. A grid of one row whose origin GSAP puts off that row has no translation, measured across both axes. test_rival_stagger checks 832 of GSAP's cases.
  • The clips guide names every way a clip's positions differ from GSAP's timeline, each with the translation a port needs (#262). These are a child's delay, a start before 0, a callback, the end after an endless child, percentages and a label not yet added. For a child before 0, it says what plays at the clip's time 0. test_rival_position checks 400 of GSAP's timeline scripts.

v0.16.0

Choose a tag to compare

@rawframe-release rawframe-release released this 03 Nov 03:40
6d6516a

0.16.0 (2026-11-03)

This release changes two stagger results, found by comparing staggers with GSAP's distribute, which the clips guide names as their model. A negative spacing now runs from the farthest items, as GSAP's does, so no offset falls below the start and every offset is a delay a transition takes (#257). An offset whose host easing turns out not finite when it is written is refused, where it was returned (#254). The guide's porting note now gives every translation a port from GSAP needs, checked against GSAP 3.15.0's own outputs (#258). Offsets change for every stagger with a negative spacing; no other value, velocity or event record changes.

Changed

  • Breaking: mtwnComputeStagger with a negative spacing puts the farthest items at the start and the nearest at the start plus the spread's size, on the easing turned around: an offset is start - span * ease(1 - t) (#257). It put the nearest items at the start and the others below it, on the same easing. A row of six from the first with a spacing of -0.1 gave 0, -0.1, ..., -0.5, which as delays a transition refuses; it now gives 0.5, 0.4, ..., 0, as GSAP does.

    • A linear stagger's offsets are the old ones moved up by the whole spread, each item keeping its place in the order. An eased one's follow the easing turned around, so they are not a shift of the old ones.
    • A negative amount over items all at one distance, a single item among them, gives every item start - spacing, where it gave start.
    • A stagger with a positive spacing or a spread of 0 gives the same bits.
  • Breaking: mtwnComputeStagger refuses with mtwn_errorInvalid a stagger whose host easing gives a value that is not finite when an offset is written, where it returned mtwn_success with that offset not finite (#254). The easing is sampled at every item before any offset is written, so only a host function that gives another value at the same progress when sampled again gets there. Its offsets are then unspecified; every other refusal still writes none. Every offset of a success is finite, as the header says.

  • The clips guide's "Porting from GSAP" note names every way a stagger differs from GSAP 3.15.0's distribute, each with its translation (#258):

    • GSAP's each becomes an amount;
    • its ratio points, and "end" and "center" in a grid, become points of the grid;
    • its "edges" becomes the center with the spacing negated;
    • GSAP rounds each offset to 1e-7.

    tools/oracle/rivals.mjs writes 448 of GSAP's cases, and test/test_rival_stagger.c checks that each case, translated as the note says, agrees within GSAP's rounding.

v0.15.4

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 23:48
0016bf5

0.15.4 (2026-11-02)

This release keeps linear() easings monotonic where mtwnIsEasingMonotonic says they are, which a timeline's crossing count relies on (#243). A sample inside a segment could land an ulp past the segment's later point and step down at it. A sample moves only there, by at most about an ulp, so only hosts sampling linear() just below a point see it.

Fixed

  • A linear() sample no longer passes its segment's later point (#243). A segment's sample is a + share * (b - a), and the rounded difference could carry it an ulp past b where the share is 1 or rounds to it: linear(0, 0.03 1%, 0.3 3%, 1) gave 0.30000000000000004 just below 3% and 0.3 at 3%. A timeline eased by such a curve could count a mark twice, depending on where the frames fell. Inside a segment the sample is now held at the point's output where it would pass it.
    • mtwnSampleEasing, and the tracks, transitions and timelines eased by linear(), its fits and rough easings included, change only at those progresses, just below a point, by at most about an ulp of the segment's larger output.
    • A clip's records change only where a timeline eased by such a linear() had a frame land there: a mark it crossed twice is now crossed once.

v0.15.3

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 18:34
0a719e0

0.15.3 (2026-11-02)

This release fixes two ways the event records broke their promise that every crossing between two updates is counted, however far time moved. An endless node whose iteration passes the largest double is held there, as the timing model holds it, where its crossing count was NaN (#237). A sweep's count is taken between its two ends, so it no longer depends on how much the clip crossed before the sweep (#241). No value or velocity changes; event records change only for clips that cross more than 2^53 points before a sweep, or whose endless nodes pass the doubles. Each fix below names who sees it.

Changed

  • The players guide says a count of 2^64 or more is UINT64_MAX, and that an endless node whose iteration passes the largest double crosses nothing between two times past it. The iteration field of mtwnTimingResult says where it is infinite and what holds there.

Fixed

  • An endless node whose iteration passes the largest double no longer makes its crossing count NaN (#237). A track of 1e-308 s passes it after about 1.8 s, and one at an offset of -1e300 with a delay of -DBL_MAX at once. Release builds dropped such crossings without a word, and builds with assertions trapped in the update.
    • Between two times past the doubles the node is held at the end of its infinite iteration, as its values are, and nothing is crossed: no record changes there in Release.
    • In the frame in which an endless timeline passes the doubles, the marks below it now record UINT64_MAX crossings each way they run, where none were recorded.
  • A sweep's records no longer depend on how much its clip crossed before the sweep began (#241). A count was the difference between what each end reached from the clip's start, and past 2^53, the doubles' last exact integer, that difference lost its low bits. A timeline of 1000 iterations of 1 s holding an endless track of 1e-18 s, swept by two doubles near 500 s, recorded 65,536 iterations where about 113,687 are crossed, and a record's first and last iterations landed where the rounded counts flipped, its last before its first.
    • A host sees corrected counts and first and last iterations only where a clip's nodes cross more than 2^53 points before a sweep, which takes iterations far below a microsecond in long or nested timelines. Where every count stays below 2^53 the records are the same bits.
    • A count through timelines nested to MTWN_MAX_CLIP_DEPTH stays linear in the depth.

v0.15.2

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 05:27
16cd892

0.15.2 (2026-11-02)

This release fixes two results that broke what the headers promise. An Oklab or OkLCh color far past its ends keeps each channel's sign (#227), and mtwnIsEasingMonotonic no longer approves a linear() easing whose halves step down where in-out or out-in joins them, so a timeline refuses it (#230). Values change only for colors driven past about 5.6e102 in Oklab or OkLCh; nothing within range moves. Each fix below names who sees it.

Changed

  • The easing and values guides say when linear() is monotonic in in-out and out-in, and that a color channel past the floats' range is held at the largest finite float of its sign in every space.

Fixed

  • An Oklab or OkLCh color whose coordinates pass about 5.6e102 has each channel at the largest finite float of its own sign, as the headers promise, in mtwnMixColor, color transitions, color tracks and mtwnConvertColorFromSpace (#227). A channel whose sum met infinities of both signs was held at +FLT_MAX: black to white at a progress of -1e110 gave +FLT_MAX in each channel, brighter than white, where sRGB gives -FLT_MAX. Oklab is the default space of transitions and tracks.
    • A host that drives a color that far, with a back overshoot past about 1e100 or a linear() output past the doubles' range for example, sees the corrected sign.
    • An OkLCh color so far out that its hue passes the doubles (a progress past about 1e307) takes a hue of 0, as a missing hue does, where its channels were held at +FLT_MAX.
    • Nothing within range moves: 800,000 mixes and conversions in every space and hue method give the same bits as 0.15.1.
  • mtwnIsEasingMonotonic returns false for linear() in in-out ending above 1, or in out-in starting below 0, where it returned true (#230). Those modes join the curve's halves at 1/2, and such an easing steps down there: linear(0, 2) in in-out gives 1 just below 1/2 and 0 at it. mtwnClip_AddTimeline took a timeline eased so, and its marks could be counted twice, or trap in a build with assertions.
    • mtwnClip_AddTimeline now refuses such a timeline with mtwn_errorInvalid. A host that eased a timeline so sees the refusal.
    • Every other easing keeps its verdict, and no value, velocity or event record of an accepted clip changes.

v0.15.1

Choose a tag to compare

@rawframe-release rawframe-release released this 02 Nov 00:31
900c412

0.15.1 (2026-11-01)

This release fixes five results that broke what the headers promise. A timeline eased by a cubic bezier or circ never steps back, so a mark at its end is crossed once (#214). A spring's settling time leaves room for the sample's rounding (#216). A stagger whose offsets would pass the doubles is refused (#221), an easing table holds a sample past the floats at the largest float (#223), and a host easing's NaN is taken as its progress and counted as misuse, while its infinity keeps equal ends in place (#225). Values change: circ samples everywhere, the child times of a timeline eased by a cubic bezier, the ends of spring transitions, and what a host easing's NaN or infinity gives. Each fix below names who sees it.

Fixed

  • A timeline's easing never decreases, as mtwnIsEasingMonotonic promises for circ in every mode and for a cubic bezier with both y in [0, 1], CSS's ease, ease-in, ease-out and ease-in-out among them. Their samples could step back between two progresses a hair apart: circ by an ulp, a cubic bezier by an ulp next to its end (ease) up to 2.6e-5 (cubic-bezier(1, 0, 0, 1) near 1/2). A mark at the end of a timeline eased by ease could then be crossed twice, and a build with assertions trapped (#214).
    • Circ's samples move by at most 2^-53 wherever circ is sampled: mtwnSampleEasing, keyframes, transitions, timelines and the fit. Over a million progresses, 10% (in), 16% (out), 6% (in-out) and 9% (out-in) of them move. A host that plays circ and compares frames across versions sees these.
    • A timeline eased by a cubic bezier moves its children's times: within 2.3e-14 for CSS's keywords, and by up to 3.6e-6 near a vertical tangent, where the new value is the closer one. Event counts change only where they were wrong. A cubic bezier anywhere else (mtwnSampleEasing, keyframes, transitions) keeps its bits, so a timeline's values can differ from them by a few 1e-14 for CSS's keywords, as the easing and clips guides now say.
    • Such a timeline costs more to sample. 1,000 clip players each at its own time, their timeline eased by ease, take about 1.9 times as long to update as on 0.15.0; players in step share the sample and take 1.4% longer. A linear timeline takes 0.5% longer.
  • mtwnComputeSpringSettlingTime gives a time from which mtwnSampleSpring's offset and velocity stay within their tolerances, as it promised. The sample, rounded, could be just outside at that time: a 2 Hz critically damped spring from 0.1 at rest, both tolerances 1e-3, sampled a velocity 3 ulps past it (#216).
    • Every finite settling time moves later or stays: for tolerances from 1e-9 to 1, later by 1.1e-14 to 3.1e-11 relatively, and for tolerances near the subnormals by up to 2.1%. Spring transitions, and an inertia's hand-off to a spring, end later by as much. A host that compares a spring transition's end or its frames past it across versions sees it.
    • A start within both tolerances still settles at once, unless it is within the sample's rounding of a tolerance's edge, relatively about 1e-14 and up to about 4e-9 just above critical damping: then its time is just past 0 (a critical 2 Hz spring from 1e-3 at rest, to 1e-3: 2.77e-8 s).
    • The header and the physics guide say when the time is infinite: an undamped spring, or one decaying at a rate below about 4e-306, that does not start within both.
  • mtwnComputeStagger refuses with mtwn_errorInvalid, writing nothing, a stagger whose offsets would pass the largest double, where it wrote infinity and returned mtwn_success: a start of 1e308 with an amount of 1e308 over 3 items, or an easing overshooting far past its ends. Every stagger accepted before with finite offsets gives the same bits (#221).
  • mtwnSampleEasingTable holds a sample past the floats' range at the largest finite float of its sign, as every other float the library writes is held, where it wrote infinity: cubic-bezier(0.5, 1e200, 0.5, 1), a back easing with an overshoot of 1e100 and linear(0, -1e300), for example. A sample within the range keeps its bits (#223).
  • A host easing's NaN is taken as the progress the function was called with, wherever it is sampled (#225). The same NaN gave a target FLT_MAX, a scale target a NaN velocity, a clip track the base where it came from the track's timing easing, and mtwnSampleEasing and the table NaN.
    • A context counts one misuse for each sample of an update that met one: a transition's value, and a clip track's value for each player. The count is the same with the sharing on or off, serially or on tasks. Calls without a context substitute without counting, so a fit or a rough easing whose host function gave NaN now succeeds where it was refused.
    • A host easing's infinity between equal ends keeps them, velocity 0, in every kind, as a transition and as a track. They showed FLT_MAX or NaN, and a scale track's cubic segment gave NaN between other keyframes too. mtwnMixRotation between equal ends far out (a progress of 1e20) gives the rotation, where it gave a zero quaternion.
    • A host whose easing returns NaN or infinity sees these values and the misuse count. An easing that never does gives the same bits.

v0.15.0

Choose a tag to compare

@rawframe-release rawframe-release released this 31 Oct 03:52
550b9a5

0.15.0 (2026-10-31)

This release follows the Maul family's conventions of .github v0.0.40. The enumerations are int32_t and the rough easing's flags uint32_t, so every struct holding one changes its layout, and the dropped events counter is mtwnContext_GetEventsDropped (#207). mtwnContext_GetMisuse no longer counts two refusals that depend on the library's state (#203). An unknown color space, hue method, angle method or rotation path is refused whatever the target's or track's kind (#210), and five corrections make the headers and guides say what the code does. No value, velocity or event record changes.

Changed

  • Breaking: the 25 enumeration typedefs are int32_t, no longer uint8_t, and mtwnRoughDef.flags is the new mtwnRoughFlags, a uint32_t (#207). Their values do not change. The 25 are mtwnValueKind, mtwnNodeKind, mtwnPositionKind, mtwnEventKind, mtwnPhase, mtwnFill, mtwnDirection, mtwnAlternateEasing, mtwnStaggerOrigin, mtwnStaggerAxis, mtwnStaggerSpacing, mtwnSegmentKind, mtwnTransitionKind, mtwnVelocitySource, mtwnColorSpace, mtwnHueMethod, mtwnAngleMethod, mtwnRotationPath, mtwnComposite, mtwnPlayState, mtwnEasingKind, mtwnStepPosition, mtwnCssKeyword, mtwnEaseMode and mtwnRoughTaper.

    • Every struct holding one changes its layout on some data model: every def, mtwnEvent, mtwnBinding, mtwnTiming and mtwnClipNodeInfo. A program rebuilds against the new headers.
    • A negative kind, mode or choice, which the signed types allow, is refused with mtwn_errorInvalid as an unknown one is, in a call, a def or a made easing, color mix or track; a path's keyframe kind is read within its tables.
    • The default context def's clip storage is 240 KiB, no longer 224 KiB, still 16 default clips: a default clip takes 14,808 bytes, where it took 14,280. With a task system, a group's share per binding grows from 60 to 68 bytes, and the default def's groups from about 900 KB to about 940 KB.
  • Breaking: mtwnContext_GetDroppedEventCount is renamed mtwnContext_GetEventsDropped, as the family names a counter of work lost (#207).

  • Breaking: mtwnContext_GetMisuse no longer counts two refusals the library's state decides (#203):

    • a stale id of a player the context handed out, kept or not, given to mtwnDestroyPlayer, mtwnClipPlayer_GetState or any clip player control. It is still refused with mtwn_errorStale. A player id never handed out still counts: the null id, an index past the highest slot used, generation 0 and a generation its slot has not reached;
    • mtwnDestroyClip refused with mtwn_errorBusy while a player holds the clip.

    Stale target and clip ids still count. A host now reads no misuse for stopping or reading a player after it ended, naming a kept player after destroying it, or destroying a clip a player still holds: the count is smaller for the same calls, never larger. The transitions guide no longer asks for mtwnPlayer_IsValid before stopping a transition that may have ended.

Fixed

  • mtwnStartTransition refuses an unknown color space, hue method, angle method or rotation path with mtwn_errorInvalid and one misuse whatever the target's kind, and mtwnContext_MeasureTransition an unknown angle method or rotation path. mtwnMakeTrack and mtwnClip_AddTrack refuse an unknown color space, hue method or angle method whatever the track's kind, as they already did the rotation path, the add counting one misuse. A kind that does not read the choice took any value, though mtwnStartTransition's header said every unknown choice is refused (#210).
  • The headers and guides say what the code does. None of these changes the code.
    • mtwn_errorStale is an id that names no live object: one destroyed since it was handed out, one never handed out, or the null id. base.h and the getting started guide said only the first (#189).
    • context.h's opening comment names the const readers refused while the host's code runs: mtwnTarget_GetState called back in, and four readers inside an update's groups (#190).
    • mtwnClipPlayer_GetState's header lists only the codes it returns. It said "As mtwnClipPlayer_Play", whose list has mtwn_errorState (#191).
    • A player whose clip has more than 128 nodes plays on the calling thread only when other players play the same clip. The threads guide and mtwnContext_GetSerialAdvanceCount's header said it did whenever other players ran (#192).
    • MTWN_SEEK_LIMIT's comment derived a margin of 16 from a bound that gives 8. It now derives it from the tighter bound the same lines give (#195).

v0.14.0

Choose a tag to compare

@rawframe-release rawframe-release released this 30 Oct 17:30
43bdf88

0.14.0 (2026-10-30)

This release finishes the API review for 1.0. The readers take the context const and read it when host code calls back in (#170). The calls on a clip's player are named for it (#178) and the two counters that were not are named Get...Count (#176). The reduced motion switch returns nothing (#174), the host easing's callback is a function type (#172), and mtwnMakeTrack takes the keyframes it rewrites first (#184). Three getters no longer crash on a null context, and a refused rough easing leaves its points. The getting started guide says what 1.0 will keep. No value, velocity or event record changes.

Changed

  • Breaking: ten readers take a const mtwnContext* (#170): mtwnClip_GetTrack, mtwnClip_GetNode, mtwnClip_FindLabel, mtwnClip_GetDuration, mtwnTarget_GetValue, mtwnTarget_GetState, mtwnPlayer_GetState (now mtwnClipPlayer_GetState), mtwnContext_ReadTargets, mtwnContext_GetClipBytes and mtwnContext_MeasureTransition. C callers compile as before; a C++ pointer to one of them has a new type.

    • Called back in from the host's code the context runs (a host easing, the allocator, the task system's functions), all but mtwnTarget_GetState now read the context as the call in progress has left it, where they returned mtwn_errorBusy, and count no misuse.
    • mtwnTarget_GetState is still refused when called back in: a target's velocity may be computed when read, by sampling a host easing, which could otherwise call it again without end.
    • While an update's clip players run in groups on the host's task system, mtwnTarget_GetValue, mtwnContext_ReadTargets, mtwnClipPlayer_GetState and mtwnContext_MeasureTransition called from the host's code are refused with mtwn_errorBusy, one misuse counted, their outputs left as they were, since a group may be writing what they read.
    • mtwnDestroyClip and mtwnDestroyContext release a clip's id before the host's free gets its block, so a free that reads back the clip finds the id stale.
  • Breaking: the calls that take only a clip's player are named for it (#178):

    Before Now
    mtwnPlayer_Play, _Pause, _Reverse, _Finish, _Cancel mtwnClipPlayer_Play, _Pause, _Reverse, _Finish, _Cancel
    mtwnPlayer_Seek, _Sweep, _SeekProgress, _SeekLabel mtwnClipPlayer_Seek, _Sweep, _SeekProgress, _SeekLabel
    mtwnPlayer_SetRate, _SetWeight, _GetState mtwnClipPlayer_SetRate, _SetWeight, _GetState
    mtwnCreatePlayer mtwnCreateClipPlayer
    mtwnPlayerDef mtwnClipPlayerDef
    mtwnDefaultPlayerDef mtwnDefaultClipPlayerDef
    MTWN_PLAYER_DEF_COOKIE MTWN_CLIP_PLAYER_DEF_COOKIE
    mtwnPlayerState mtwnClipPlayerState

    mtwnPlayerId, mtwnDestroyPlayer and mtwnPlayer_IsValid, which take a transition's player too, and mtwnPlayState keep their names. Parameters, values and the cookie's value are unchanged.

  • Breaking: mtwnContext_GetDroppedEvents is mtwnContext_GetDroppedEventCount and mtwnContext_GetSerialAdvances is mtwnContext_GetSerialAdvanceCount, as the family names its counters. Their parameters and behavior are unchanged (#176).

  • Breaking: mtwnContext_EnableReducedMotion returns void. A null context changes nothing, and a call back in changes nothing and counts one misuse, as their refusals did; read the state with mtwnContext_IsReducedMotionEnabled (#174).

  • Breaking: mtwnEaseFunction is a function type, double (void* context, double progress), as the task hooks' are: mtwnMakeHostEasing takes, and mtwnEasing.function holds, a mtwnEaseFunction*. Passing a function's name compiles as before; a variable of the old type is declared mtwnEaseFunction* (#172).

  • Breaking: mtwnMakeTrack takes the keyframes and their count first and the def third: mtwnMakeTrack(keyframes, count, def, lengthsOut, mixesOut, trackOut). It rewrites the keyframes in place for angles and for rotations on the shortest path, so they are the call's object, as the family's conventions order it. A call in the old order no longer compiles (#184).

  • docs/user/getting-started.md has a Compatibility section, linked from the README: what 1.0.0 will keep across 1.y (every function, type, enum value, macro and struct field in the headers, things only added), what it leaves out (the fields of the made value structs a host never reads), the binary interface within a minor version, and results the same bits on every platform, any change of a bit stated here. The transitions guide says to ask mtwnPlayer_IsValid before stopping a transition that may have ended (#180).

Fixed

  • mtwnContext_GetMisuse, mtwnContext_GetSerialAdvanceCount and mtwnContext_GetMemoryBytes read 0 from a null context, where they crashed, as the other getters do (#165).
  • A refused mtwnMakeRoughEasing leaves the caller's points as they were. A host shape giving a value that is not finite past the first inner points left part of a new set there; the shape is now sampled at every inner point before one is written (#168).
  • mtwnFitLinearEasing says what a refused fit leaves in its points, which its header called unspecified: the fit's first points, at most the capacity, exactly as a larger buffer would start, and the rest as they were (#182).