rdpe 0.1.0

Reaction Diffusion Particle Engine - GPU particle simulations made easy
Documentation
# Emitters

Emitters continuously spawn new particles into the simulation, replacing dead particles. They enable effects like fountains, explosions, rain, and any other continuous particle generation.

## How Emitters Work

When you add an emitter, RDPE:
1. Finds dead particles (those with `alive == 0`)
2. Respawns them based on the emitter's rate and position
3. Sets initial velocity according to the emitter type

Emitters work best with lifecycle rules:
- `Rule::Age` - increments particle age each frame
- `Rule::Lifetime(seconds)` - kills particles after a duration

## Emitter Types

### Point

Emits particles from a single point in all directions.

```rust
.with_emitter(Emitter::Point {
    position: Vec3::ZERO,
    rate: 500.0,    // particles per second
    speed: 1.0,     // initial speed (0 = random)
})
```

### Burst

One-time explosion of particles. Fires once at simulation start.

```rust
.with_emitter(Emitter::Burst {
    position: Vec3::new(0.0, 0.5, 0.0),
    count: 1000,    // total particles to spawn
    speed: 3.0,     // outward speed
})
```

### Cone

Directional emission in a cone shape. Great for fountains, jets, and thrusters.

```rust
.with_emitter(Emitter::Cone {
    position: Vec3::new(0.0, -0.5, 0.0),
    direction: Vec3::Y,     // points up
    speed: 2.5,
    spread: 0.3,            // cone half-angle in radians
    rate: 800.0,
})
```

The `spread` parameter controls the cone width:
- `0.0` = laser beam (no spread)
- `0.3` = tight cone (~17 degrees)
- `PI/4` = 45-degree cone
- `PI/2` = hemisphere

### Sphere

Spawns particles on a sphere surface, moving outward (or inward).

```rust
.with_emitter(Emitter::Sphere {
    center: Vec3::ZERO,
    radius: 0.5,
    speed: 1.0,     // positive = outward, negative = inward
    rate: 1000.0,
})
```

### Box

Spawns particles at random positions within a box volume.

```rust
.with_emitter(Emitter::Box {
    min: Vec3::new(-1.0, 1.0, -1.0),
    max: Vec3::new(1.0, 1.2, 1.0),
    velocity: Vec3::new(0.0, -2.0, 0.0),  // falling rain
    rate: 2000.0,
})
```

## Complete Example

Here's a fountain that continuously emits particles:

```rust
use rdpe::prelude::*;

#[derive(Particle, Clone)]
struct Drop {
    position: Vec3,
    velocity: Vec3,
    #[color]
    color: Vec3,
}

fn main() {
    Simulation::<Drop>::new()
        .with_particle_count(10_000)
        .with_bounds(2.0)
        // Start all particles dead - emitter will spawn them
        .with_spawner(|_, _| Drop {
            position: Vec3::ZERO,
            velocity: Vec3::ZERO,
            color: Vec3::new(0.3, 0.6, 1.0),
        })
        // Cone emitter shooting upward
        .with_emitter(Emitter::Cone {
            position: Vec3::new(0.0, -0.8, 0.0),
            direction: Vec3::Y,
            speed: 3.0,
            spread: 0.2,
            rate: 1000.0,
        })
        // Lifecycle management
        .with_rule(Rule::Age)
        .with_rule(Rule::Lifetime(2.0))
        // Physics
        .with_rule(Rule::Gravity(4.0))
        .with_rule(Rule::Drag(0.3))
        .with_rule(Rule::BounceWalls { restitution: 1.0 })
        .run();
}
```

## Multiple Emitters

You can add multiple emitters to create complex effects:

```rust
// Twin fountains
.with_emitter(Emitter::Cone {
    position: Vec3::new(-0.5, -0.8, 0.0),
    direction: Vec3::Y,
    speed: 3.0,
    spread: 0.15,
    rate: 500.0,
})
.with_emitter(Emitter::Cone {
    position: Vec3::new(0.5, -0.8, 0.0),
    direction: Vec3::Y,
    speed: 3.0,
    spread: 0.15,
    rate: 500.0,
})
```

## Tips

- **Rate tuning**: Match your rate to particle count and lifetime. If `rate * lifetime > particle_count`, you'll run out of dead particles to respawn.
- **Dead start**: When using emitters, initialize particles as dead in your spawner (they'll be spawned by the emitter).
- **Burst timing**: Burst emitters fire at `time < 0.1`, so they work immediately on startup.

## Sub-Emitters

Sub-emitters spawn child particles when parent particles die. This enables fireworks, explosions, chain reactions, and biological reproduction.

### Basic Sub-Emitter

```rust
#[derive(ParticleType)]
enum Firework {
    Rocket,
    Spark,
}

Simulation::<Particle>::new()
    .with_sub_emitter(SubEmitter::new(
        Firework::Rocket.into(),  // Parent type
        Firework::Spark.into(),   // Child type
    )
    .count(30)                    // Children per death
    .speed(1.0..3.0)              // Random speed range
    .spread(std::f32::consts::PI) // Hemisphere
    .inherit_velocity(0.3))       // 30% of parent velocity
    .run();
```

### Sub-Emitter Options

| Method | Description |
|--------|-------------|
| `.count(n)` | Number of children per parent death |
| `.speed(min..max)` | Random speed range |
| `.spread(radians)` | 0 = laser, PI = hemisphere, TAU = full sphere |
| `.inherit_velocity(factor)` | 0.0 to 1.0, how much parent velocity children get |
| `.child_lifetime(secs)` | Override lifetime for children |
| `.child_color(Vec3)` | Override color for children |
| `.spawn_radius(r)` | Random offset from parent position |

### Chaining Sub-Emitters

Create multi-stage effects:

```rust
// Rockets → Sparks → Embers
.with_sub_emitter(SubEmitter::new(Rocket.into(), Spark.into()).count(30))
.with_sub_emitter(SubEmitter::new(Spark.into(), Ember.into()).count(5))
```