# Guide

This guide is the main conceptual page for Manim Stateflow. The core pattern is:

```text
state -> lift -> d.Object / d.call -> mobject.d.<method>
```

Manim still owns geometry, layout, rendering, and `self.play(...)`. Manim Stateflow decides when constructors, custom builders, and selected Manim methods should run again because a dependency changed.

## Table Of Contents

- [Core Ideas](#core-ideas)
- [One Pattern, Many Cases](#one-pattern-many-cases)
- [Object-To-Object Wiring](#object-to-object-wiring)
- [State Independence](#state-independence)
- [Animation](#animation)
- [Effects](#effects)

## Core Ideas

Normal Manim code still works. Manim Stateflow only adds dependency timing where you ask for it.

`self.state(...)` creates source values:

```python
x = self.state(0)
state = self.state(x=0, y=1, phase=0)
```

`lift(...)` creates computed values:

```python
y = lift(lambda x_value: np.sin(x_value), x)
point = lift(lambda x_value, y_value: axes.c2p(x_value, y_value), x, y)
```

`d.<ManimMobject>(...)` builds reactive objects when any argument contains a reactive value:

```python
dot = d.Dot(point, color=YELLOW)
line = d.Line([state.x, 0, 0], [state.y, 1, 0])
```

Use `d.call(...)` when a custom builder is clearer than a direct constructor:

```python
graph = d.call(
    lambda phase: axes.plot(lambda t: np.sin(t + phase), x_range=[-4, 4]),
    state.phase,
)
```

## One Pattern, Many Cases

The same small chain handles most teaching scenes:

```text
state -> lift -> d.Object / d.call -> mobject.d.<method>
```

| Situation | Use | Example |
|---|---|---|
| You need a changing source value | `self.state(...)` | `x = self.state(-3)` |
| You need a computed value | `lift(...)` | `y = lift(lambda x: x * x, x)` |
| You need a normal Manim object from state | `d.Object(...)` | `dot = d.Dot([x, y, 0])` |
| You need custom construction logic | `d.call(...)` | `graph = d.call(lambda a: axes.plot(lambda x: a*x*x), a)` |
| You need an existing object to follow state or another object | `mobject.d.<method>(...)` | `label.d.next_to(dot, UP)` |
| You need custom mutation on an existing object | `mobject.d.apply(...)` | `number.d.apply(lambda mob, value: mob.set_value(value), y)` |
| You need frame-time behavior with `dt` | `mobject.d.on_frame(...)` | `square.d.on_frame(lambda mob, dt, speed: mob.rotate(speed * dt), speed)` |

`d.Object(...)` means the reactive version of normal Manim constructors, such
as `d.Dot(...)`, `d.Line(...)`, `d.Text(...)`, `d.DecimalNumber(...)`, or
`d.Arrow(...)`. Use this when the constructor arguments are enough.

`d.call(...)` is the general escape hatch. If you can write a function that
returns a Manim mobject, Stateflow can rebuild or patch that mobject when its
dependencies change:

```python
curve = d.call(
    lambda amplitude: axes.plot(lambda x: amplitude * np.sin(x), x_range=[-4, 4]),
    state.amplitude,
)
```

`.d.<method>(...)` is for objects that already exist. The method is still a
normal Manim method; Stateflow just reruns it when dependencies or anchor
objects move:

```python
label = Text("value")
label.d.next_to(dot, UP)
label.d.set_opacity(state.opacity)
```

`.d.on_frame(...)` is the frame-time case. It is useful when behavior depends on
`dt`, such as simple physics, continuous rotation, or custom motion that should
run every rendered frame:

```python
square.d.on_frame(lambda mob, dt, speed: mob.rotate(speed * dt), state.speed)
```

## Object-To-Object Wiring

Use `mobject.d.<method>(...)` to rerun normal Manim methods when reactive dependencies or anchor objects change:

```python
label = Text("point")
label.d.next_to(dot, UP)

value = DecimalNumber(num_decimal_places=2)
value.d.set_value(y)
value.d.next_to(label, RIGHT)
```

## State Independence

Positional state values are independent sources:

```python
x, y = self.state(1, 2)
```

Named state values are also independent sources wrapped in one convenient object:

```python
state = self.state(x=1, y=2)
```

Changing `state.x` does not dirty objects that only depend on `state.y`.

If you store one whole Python object in one source, that object is one invalidation boundary:

```python
config = self.state(MyState(...))
```

Replacing `config` invalidates everything depending on `config`, even if only one field changed. This is why named state exists: grouping for readability and multi-field animation, not shared invalidation.

## Animation

Use `.animate_to(...)` for one source value:

```python
self.play(x.animate_to(3), run_time=3)
```

Use `state.animate_to(...)` for named values:

```python
self.play(state.animate_to(x=2, y=-1), run_time=3)
```

Stateflow uses Manim's effective animation timing for interpolated values. That
means Manim's default `smooth` rate function is used unless you pass another
`rate_func` to `self.play(...)` or to `animate_to(...)`.

Use `jump(...)` for discrete values:

```python
self.play(mode.animate_to(jump("recursive", "memoized", "iterative")))
```

`jump(...)` is the default linear-timing exception: its stages use raw timeline
positions unless you explicitly pass a `rate_func`. This keeps discrete steps
evenly spaced by default while still letting you override the timing when that
is the intended choreography.

Use `state.animate.transform(...).using({...})` when named state updates should also drive explicit Manim transforms on already-reactive mobjects:

```python
self.play(
    state.animate.transform(n=jump(8)).using({
        value_display: Transform,
        (shape, label): ReplacementTransform,
    }),
    run_time=1,
)
```

With `jump(...)`, `.using({...})` transforms between exact staged snapshots. Large jump gaps can look less geometrically exact between snapshots; use smaller jump steps or continuous `animate_to(...)` when every in-between frame must stay mathematically exact.

## Effects

Use `.d.<method>(...)` first. It is the easiest way to wire normal Manim behavior:

```python
label.d.next_to(dot, UP)
halo.d.move_to(dot)
number.d.set_value(y)
```

Use `.d.apply(...)` for custom dependency-driven updates:

```python
rectangles.d.apply(redraw_rectangles, state.a, state.b)
```

Use `.d.on_frame(...)` only when behavior truly needs frame time and `dt`:

```python
square.d.on_frame(lambda mob, dt, speed: mob.rotate(speed * dt), state.speed)
```
