euv_engine/particle/struct.rs
1use super::*;
2
3/// A deterministic xorshift64* pseudo-random generator used by particle
4/// emitters.
5///
6/// The engine intentionally avoids both the `rand` crate (dependency weight
7/// for a wasm target) and `js_sys::Math::random` (non-deterministic, and
8/// unusable outside a JS runtime, which would make host-side unit tests
9/// impossible). A seeded generator keeps emitter behavior reproducible.
10#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
11pub struct ParticleRng {
12 /// The current generator state. Never zero after construction.
13 #[get(type(copy))]
14 #[get_mut(pub(crate))]
15 #[set(pub(crate))]
16 pub(crate) state: u64,
17}
18
19/// The static configuration of a particle emitter, describing how new
20/// particles are spawned and how they evolve over their lifetime.
21#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
22pub struct ParticleConfig {
23 /// The number of particles spawned per second while the emitter is active.
24 #[get(type(copy))]
25 #[set(pub(crate))]
26 pub(crate) emission_rate: f64,
27 /// The maximum number of simultaneously live particles.
28 #[get(type(copy))]
29 #[set(pub(crate))]
30 pub(crate) max_particles: usize,
31 /// The minimum particle lifetime in seconds.
32 #[get(type(copy))]
33 #[set(pub(crate))]
34 pub(crate) lifetime_min: f64,
35 /// The maximum particle lifetime in seconds.
36 #[get(type(copy))]
37 #[set(pub(crate))]
38 pub(crate) lifetime_max: f64,
39 /// The minimum initial particle speed in world units per second.
40 #[get(type(copy))]
41 #[set(pub(crate))]
42 pub(crate) speed_min: f64,
43 /// The maximum initial particle speed in world units per second.
44 #[get(type(copy))]
45 #[set(pub(crate))]
46 pub(crate) speed_max: f64,
47 /// The central emission direction in radians.
48 #[get(type(copy))]
49 #[new(skip)]
50 pub(crate) angle: f64,
51 /// The total cone width around `angle` in radians within which
52 /// particle directions are uniformly randomized.
53 #[get(type(copy))]
54 #[new(skip)]
55 pub(crate) spread: f64,
56 /// The constant acceleration applied to every live particle.
57 #[get(type(copy))]
58 #[new(skip)]
59 pub(crate) gravity: Vector2D,
60 /// The particle color at birth.
61 #[get(type(copy))]
62 #[new(skip)]
63 pub(crate) color_start: Color,
64 /// The particle color at death. Set its alpha to 0.0 for a fade-out.
65 #[get(type(copy))]
66 #[new(skip)]
67 pub(crate) color_end: Color,
68 /// The particle radius at birth in world units.
69 #[get(type(copy))]
70 #[new(skip)]
71 pub(crate) size_start: f64,
72 /// The particle radius at death in world units.
73 #[get(type(copy))]
74 #[new(skip)]
75 pub(crate) size_end: f64,
76}
77
78/// A single live particle.
79#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
80pub struct Particle {
81 /// The current world-space position.
82 #[get(type(copy))]
83 #[get_mut(pub(crate))]
84 #[set(pub(crate))]
85 pub(crate) position: Vector2D,
86 /// The current velocity in world units per second.
87 #[get(type(copy))]
88 #[get_mut(pub(crate))]
89 #[set(pub(crate))]
90 pub(crate) velocity: Vector2D,
91 /// The time this particle has been alive, in seconds.
92 #[get(type(copy))]
93 #[get_mut(pub(crate))]
94 #[set(pub(crate))]
95 pub(crate) age: f64,
96 /// The total lifetime of this particle, in seconds.
97 #[get(type(copy))]
98 #[set(pub(crate))]
99 pub(crate) lifetime: f64,
100}
101
102/// A point emitter that continuously (or in bursts) spawns particles,
103/// integrates their motion, and records them into a `DrawList` as colored
104/// circles whose color and size interpolate over each particle's lifetime.
105#[derive(Clone, Data, Debug, New, PartialEq)]
106pub struct ParticleEmitter {
107 /// The world-space emission point.
108 #[get(type(copy))]
109 #[get_mut(pub(crate))]
110 #[set(pub(crate))]
111 pub(crate) position: Vector2D,
112 /// The emitter configuration.
113 #[get(type(copy))]
114 #[set(pub(crate))]
115 pub(crate) config: ParticleConfig,
116 /// All currently live particles.
117 #[get(type(clone))]
118 #[get_mut(pub(crate))]
119 #[set(pub(crate))]
120 #[new(skip)]
121 pub(crate) particles: Vec<Particle>,
122 /// The fractional particle spawn budget carried between updates.
123 #[get(type(copy))]
124 #[get_mut(pub(crate))]
125 #[set(pub(crate))]
126 #[new(skip)]
127 pub(crate) emit_accumulator: f64,
128 /// Whether continuous emission is currently enabled.
129 #[get(type(copy))]
130 #[set(pub(crate))]
131 #[new(skip)]
132 pub(crate) active: bool,
133 /// The emitter's deterministic random generator.
134 #[get(type(copy))]
135 #[get_mut(pub(crate))]
136 #[set(pub(crate))]
137 #[new(skip)]
138 pub(crate) rng: ParticleRng,
139}