Stateful systems

Conditions, integration and simulations resolved before rendering.

Contents:

  • k.when: Edge-triggered effect: when cond goes from false to true, it fires an animation or emits an event.
  • k.integrate: Integral of an expression with respect to the change in d= (default k.time), starting at the cursor, with initial= and clamp=(lo, hi).
  • k.simulate: Fixed-step simulation: step(state, dt) -> state, run in Python during resolve.
  • k.State: Base of simulation states: plain fields (floats, bools, pairs) plus k.Events. class Ball(k.State): y: float = 4.0 v: float = 0.0 bounce: k.Event[Impact]
  • k.trace: Trail: the last length seconds of the path of a moving point (usually obj.world.position), drawn as a stroke.
  • k.Trail: Trail: the last length seconds of the path of a moving point (usually obj.world.position), drawn as a stroke.

Back to the reference index.

k.when (function)

k.when(
    cond: BoolVal,
    action: Animation | EventSource[P],
    *,
    once: bool = False,
    rearm: BoolVal | None = None,
) -> Effect

Edge-triggered effect: when cond goes from false to true, it fires an animation or emits an event. It does not move the cursor. Without rearm=, the condition must become false again before it fires again; once=True fires only the first time.

Parameters:

Name Type Default Description
cond BoolVal required Edge-triggered effect: when cond goes from false to true, it fires an animation or emits an event.
action Animation | EventSource[P] required Edge-triggered effect: on false → true, start action (an animation) or fire it (an event).
once bool False Without rearm=, the condition must become false again before it fires again; once=True fires only the first time.
rearm BoolVal | None None Without rearm=, the condition must become false again before it fires again; once=True fires only the first time.

Example:

@k.scene
def trigger(s: k.Scene):
    x = k.signal(-4.0)
    dot = k.Dot(r=0.25, x=x)
    s.add(dot)
    k.when(x >= 2, k.flash(dot, color=k.YELLOW))
    s.play(x.to(4), duration=3, ease=k.ease.linear)

See also: event.on, s.wait_for, k.integrate.

k.integrate (function)

k.integrate(
    expr: FloatVal,
    d: FloatVal | None = None,
    *,
    initial: float = 0.0,
    clamp: tuple[float, float] | None = None,
) -> Expr[float]

Integral of an expression with respect to the change in d= (default k.time), starting at the cursor, with initial= and clamp=(lo, hi). It is precomputed during resolve; the result is a read-only signal. A clock used as d= must advance linearly.

Parameters:

Name Type Default Description
expr FloatVal required
d FloatVal | None None Integral of an expression with respect to the change in d= (default k.time), starting at the cursor, with initial= and clamp=(lo, hi).
initial float 0.0 Integral of an expression with respect to the change in d= (default k.time), starting at the cursor, with initial= and clamp=(lo, hi).
clamp tuple[float, float] | None None Integral of an expression with respect to the change in d= (default k.time), starting at the cursor, with initial= and clamp=(lo, hi).

Example:

@k.scene
def tank(s: k.Scene):
    flow = k.signal(0.5)
    level = k.integrate(flow, initial=0.0, clamp=(0, 3))
    water = k.Rect(w=2, h=level + 0.01, fill=k.BLUE, fill_opacity=0.8).place(at="center")
    s.add(water)
    s.wait(2)
    s.play(flow.to(-1), duration=1)
    s.wait(2)

See also: k.when, k.time, k.Component.

k.simulate (function)

k.simulate(
    step: Callable[[StateT, float], StateT],
    state: StateT,
    dt: float = 0.00416667,
    until: float | None = None,
) -> Simulation[StateT]

Fixed-step simulation: step(state, dt) -> state, run in Python during resolve. The state is a subclass of k.State (float, bool or pair fields with defaults, plus k.Event[P] events); states are values, so the step returns st.replace(field=...) and emits events with st.event.emit(payload) — an immediate effect at the simulated instant, not part of the returned value (event without payload: bounce: k.Event, st.bounce.emit()). The step is plain Python (if, min, math all work). Each field becomes a signal (sim.y) and each event a source (sim.bounce). Start it with s.start(sim); sim.done fires at the end (until=).

Parameters:

Name Type Default Description
step Callable[[StateT, float], StateT] required
state StateT required
dt float 0.00416667
until float | None None Start it with s.start(sim); sim.done fires at the end (until=).

Example:

class Fall(k.State):
    y: float = 3.0
    v: float = 0.0

def step(st: Fall, dt: float) -> Fall:
    return st.replace(y=st.y + st.v * dt, v=st.v - 9.8 * dt)

@k.scene
def fall(s: k.Scene):
    sim = k.simulate(step, Fall(), dt=1 / 240, until=1)
    s.add(k.Circle(r=0.3, y=sim.y))
    s.start(sim)
    s.wait_for(sim.done)

See also: event.on, s.wait_for, k.Event.

k.State (class)

k.State(**values: object)

Base of simulation states: plain fields (floats, bools, pairs) plus k.Events.

class Ball(k.State): y: float = 4.0 v: float = 0.0 bounce: k.Event[Impact]

Documented together with k.simulate.

Parameters:

Name Type Default Description
**values object variadic

Members:

  • replace: A copy with some fields changed (states are values).

k.State.replace (method)

state.replace(**changes: object) -> State

A copy with some fields changed (states are values).

Parameters:

Name Type Default Description
**changes object variadic

k.trace (function)

k.trace(point: VecVal, length: float = 2.0, **style: Unpack[StyleKeywords]) -> Trail

Trail: the last length seconds of the path of a moving point (usually obj.world.position), drawn as a stroke. It is sampled from the timeline, so rendering stays pure.

Parameters:

Name Type Default Description
point VecVal required
length float 2.0 Trail: the last length seconds of the path of a moving point (usually obj.world.position), drawn as a stroke.
**style Unpack[StyleKeywords] variadic Keyword arguments (StyleKeywords): name: str | None, key: str | None, x: FloatVal, y: FloatVal, position: VecVal, rotate: FloatVal, anchor: VecVal, z: FloatVal, scale: FloatVal, scale_x: FloatVal, scale_y: FloatVal, opacity: FloatVal, visible: BoolVal, fill: ColorVal, fill_opacity: FloatVal, stroke: ColorVal, stroke_width: FloatVal, dash: FloatsVal, color: ColorVal.

Example:

@k.scene
def trail(s: k.Scene):
    dot = k.Dot(r=0.15, x=k.cos(k.time * 2) * 3, y=k.sin(k.time * 3) * 2)
    trail = k.trace(dot.world.position, length=1.5, stroke=k.TEAL)
    s.add(trail, dot)
    s.wait(4)

See also: k.follow, k.time.

k.Trail (class)

k.Trail(point: VecVal, length: float = 2.0, **props: Unpack[StyleKeywords])

Documented together with k.trace. Trail: the last length seconds of the path of a moving point (usually obj.world.position), drawn as a stroke. It is sampled from the timeline, so rendering stays pure.

Parameters:

Name Type Default Description
point VecVal required
length float 2.0
**props Unpack[StyleKeywords] variadic Keyword arguments (StyleKeywords): name: str | None, key: str | None, x: FloatVal, y: FloatVal, position: VecVal, rotate: FloatVal, anchor: VecVal, z: FloatVal, scale: FloatVal, scale_x: FloatVal, scale_y: FloatVal, opacity: FloatVal, visible: BoolVal, fill: ColorVal, fill_opacity: FloatVal, stroke: ColorVal, stroke_width: FloatVal, dash: FloatsVal, color: ColorVal.

Props (animatable with .to(), settable with .set() or in the constructor):

Prop Kind Default Interpolation
fill color theme.fg linear
fill_opacity float 0.0 linear
stroke color theme.fg linear
stroke_width float theme.stroke_width linear
dash floats () step_end
point vec2 (0.0, 0.0) linear
length float 2.0 linear

Props inherited from k.Node: x, y, rotate, scale, scale_x, scale_y, anchor, opacity, z, visible.

Inherited from k.Node: set, to, unbind, edge, age, entered, exited, copy, place, to_place, unpin.