smear_frame
Draw a motion smear on one animation frame: stretch or echo the subject along its measured movement vector so fast action reads as speed instead of teleporting.
Instructions
Draw one frame's subject along its own path, so fast movement reads as speed.
Between two frames of a fast movement the eye expects a **smear**: one frame where the
subject is stretched along where it came from, or drawn several times faintly, so the
movement reads as speed rather than as teleportation. `offset_cels` produces the
movement; this describes one frame of it.
The movement vector is taken from the sprite rather than restated by the caller: it is
the shift between the centre of the drawn content on `from_frame` and on `frame`. The
*content* box, not `cel.position`, because any tool here that writes a whole canvas
back leaves the position at (0, 0) on every frame, and a position diff would then read
zero while the drawing plainly moved.
Args:
frame: The frame to smear. Its own cel is the subject and stays on top, unchanged;
the trail is drawn behind it. Only this frame is written.
from_frame: The frame the movement came from. Defaults to the one before `frame`.
mode: `stretch` draws the subject once, elongated back along the vector and
thinning to a single pixel at the tip, which is the classic one-frame smear.
`echo` draws it `steps` times along the vector, each copy a step further down
the ramp, which is the multiple-exposure smear.
strength: How far back the smear reaches, as a fraction of the movement. 0.6 is a
trail that clearly trails; 1.0 reaches all the way to the previous position;
above 1 it overshoots, which is a real choice an animator makes.
steps: How many copies `echo` draws. Defaults to 3, and is refused with
`mode="stretch"` rather than silently ignored.
ramp: The colours, darkest first, the trail is allowed to use, as `generate_ramp`
returns them. **Pass this.** Each pixel of the trail is the nearest ramp entry
to the subject's own colour there, stepped toward the dark end and clamped
(never wrapped), so `palette_conformance` stays at 1.0, which is the property
every shading tool here holds to and what a pixel artist actually draws.
Without it the fallback is opacity, and the fallback **leaves the palette**:
the trail is then made of colours that are not in the sprite, which is motion
blur rather than a smear. On an *indexed* sprite the ramp is required rather
than preferred, and the call is refused without one: an indexed pixel is an
offset into a palette and carries no alpha, so the fallback would snap every
copy back to the subject's own colour and draw a solid blob.
Refuses rather than producing a blur of nothing: a frame with no movement to smear
(the two content boxes sit at the same place, and the message says where they are), a
movement too short to smear at this strength (it names the pixels and the length that
would result), `frame` 1 with no earlier frame, `from_frame` equal to `frame`, either
frame undrawn, a group or tilemap layer, a ramp of fewer than two colours or one given
as palette indices, `steps` with `stretch`, and a frame that shares its image with
another frame, since a linked cel would put the smear on every frame in the group and
the whole point is that the unsmeared frames are untouched.
Returns the vector it measured and where it came from, the plots it drew, and the
cel's bounds before and after, so the claim that the smear lies along the movement can
be checked. On an indexed sprite it also returns `trail_on_palette`: what the trail's
own target colours resolve to on this palette, and a warning naming the copies that
will come out the same colour because two of those targets landed on one entry. That
is a different finding from the ramp headroom one above it, which measures the art.
`validate_loop` will now report this frame as breaking the spacing series, which is
correct: a smear frame is one that legitimately does.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | Default: "stretch". | stretch |
| ramp | No | ||
| frame | Yes | ||
| layer | Yes | ||
| steps | No | ||
| filename | Yes | ||
| strength | No | Default: 0.6. | |
| from_frame | No |