cutan.impacts.stroke¶
The stroke: impact events -> one continuous height curve h(t).
h is the object’s height above its impact position, in units of a full
stroke: 0 is contact (or, in the air, the turning point) and 1 a full
preparation. Every object in cutan.impacts.objects maps h AFFINELY onto
one animated property (a stick’s rotation, a ball’s y), which is what makes
the curve below exactly the curve the renderer draws: an eased tween of the
property IS the same easing of h.
The curve is a chain of StrokeSegments built only from the
quadratic easings the cutout runtime already has, and each is chosen for its
physics:
surface — the object falls with
ease_in(constant acceleration, like gravity) and reachesh = 0at full speed, then leaves withease_out: a velocity reversal AT the impact. Contact is visible in position (the object stops at the surface) and as a kink in velocity.air — no surface. The fall is
ease_inand then a shortease_outbrake ofbrakeseconds that brings the object to rest ath = 0; it then leaves withease_in_out. The executed “impact” is that turning point — the moment the stroke’s lowest point is reached — and the peak speed comesbrakeseconds BEFORE it. The brake’s distance is chosen so velocity is continuous into it (d = apex * brake / fall), so the only discontinuity is in acceleration — a muscle stopping a limb, not a collision.
In both kinds the speed at the peak is 2 * apex / fall.
>>> from cutan.impacts.performance import perform
>>> events = perform(120, beats=3)
>>> s = build_stroke(events, kind="surface", duration=2.0)
>>> [s.h(e.t_impact) for e in events]
[0.0, 0.0, 0.0]
>>> round(s.h(0.0), 6), round(s.h(0.75), 6) # at rest, then the apex between hits
(1.0, 1.0)
>>> a = build_stroke(events, kind="air", duration=2.0)
>>> [round(a.velocity(e.t_impact), 9) for e in events] # the air stroke TURNS
[0.0, 0.0, 0.0]
Module Attributes
Longest rise after an impact, and longest fall into one (seconds). |
|
The air stroke's braking time before its turning point (seconds). |
Functions
|
Chain rise / hold / fall segments through every executed impact. |
Classes
|
The whole curve, plus the kinematics of every impact on it. |
|
How the object moved into one impact. |
|
|
|
One impact's own timings (seconds): the |
Exceptions
A stroke that cannot be built from these events. |
- cutan.impacts.stroke.DEFAULT_BRAKE: float = 0.03¶
The air stroke’s braking time before its turning point (seconds).
- cutan.impacts.stroke.DEFAULT_RISE: float = 0.18¶
Longest rise after an impact, and longest fall into one (seconds). A slower tempo holds at the apex between them instead of floating: a drummer’s stroke takes about as long at 60 BPM as at 120, it is the wait that changes.
- class cutan.impacts.stroke.Stroke(kind, duration, segments, kinematics)[source]¶
Bases:
objectThe whole curve, plus the kinematics of every impact on it.
- exception cutan.impacts.stroke.StrokeError[source]¶
Bases:
ValueErrorA stroke that cannot be built from these events.
- class cutan.impacts.stroke.StrokeKinematics(index, kind, t_impact, t_peak_speed, peak_speed, apex, fall, brake, rise=0.0)[source]¶
Bases:
objectHow the object moved into one impact.
t_peak_speedequalst_impactfor a surface impact and precedes it bybrakefor an air impact;peak_speedis in stroke heights per second (multiply by the object’s stroke extent for pixels or radians).
- class cutan.impacts.stroke.StrokeSegment(t0, t1, h0, h1, easing)[source]¶
Bases:
objecthgoes fromh0toh1over[t0, t1]undereasing.
- class cutan.impacts.stroke.StrokeTiming(rise, fall, brake)[source]¶
Bases:
objectOne impact’s own timings (seconds): the
fallinto it, the airbrakebefore it, and theriseout of it (cutan#27: stroke-shape variability, drawn per stroke). Each is the longest it may take: a fast tempo still shortens it to fit.
- cutan.impacts.stroke.build_stroke(events, *, kind='surface', duration, rise=0.18, fall=0.18, brake=0.03, rest_height=1.0, timings=None)[source]¶
Chain rise / hold / fall segments through every executed impact.
The object starts and ends at
rest_height; before impactkit is raised toevents[k].amplitude(a bigger preparation, a harder hit). Segments tile[0, duration]exactly, and every impact is a segment boundary at preciselyt_impact.timingsgives each impact its own rise, fall and brake (one per event, cutan#27); without it every stroke takesrise,fallandbrake.- Return type: