Skip to main content

motion_content/particles/
events.rs

1//! Stable event policies and sub-emitter inheritance, independent of rendering.
2
3use super::*;
4
5/// Fixed-boundary particle event.
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub enum ParticleEventKind {
8    /// First admission of a live particle.
9    Birth,
10    /// Half-open lifetime expiry, including kill-on-hit.
11    Death,
12    /// Geometric or population contact.
13    Collision,
14    /// First boundary at/after an absolute age in seconds.
15    AgeReached(f32),
16}
17
18/// Event payload ordered by stable nonrecycled particle ID.
19#[derive(Clone, Copy, Debug, PartialEq)]
20pub struct ParticleEvent {
21    /// Exact simulation boundary time (including prewarm offset).
22    pub time: Time,
23    /// Particle state at the event.
24    pub particle: Particle,
25    /// Trigger kind.
26    pub kind: ParticleEventKind,
27}
28
29/// Parent properties added to an emitter's local child record.
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
31pub enum Inherit {
32    /// Keep emitter-authored values.
33    None,
34    /// Translate local emission to the event location.
35    #[default]
36    Position,
37    /// Translate and add parent velocity.
38    Velocity,
39    /// Translate and multiply parent premultiplied color.
40    Color,
41    /// Translate, add velocity and inherit color.
42    All,
43}
44
45/// Event-triggered emitter. It receives local step zero and Time::ZERO for each trigger.
46#[derive(Clone)]
47pub struct SubEmitter {
48    /// Birth/death/contact/age trigger.
49    pub on: ParticleEventKind,
50    /// Immutable local emitter definition.
51    pub emitter: Arc<dyn ParticleEmitter>,
52    /// Parent property policy.
53    pub inherit: Inherit,
54    /// Probability in 0..=1, keyed by parent/event/rule.
55    pub probability: f32,
56}
57
58impl SubEmitter {
59    pub(super) fn validate(&self) -> Result<(), ValidationError> {
60        if !self.probability.is_finite() || !(0.0..=1.0).contains(&self.probability) {
61            return Err(invalid(
62                "sub_emitter",
63                "Probability must be finite in 0..=1",
64            ));
65        }
66        if let ParticleEventKind::AgeReached(age) = self.on
67            && (!age.is_finite() || age <= 0.0 || age > 3600.0)
68        {
69            return Err(invalid(
70                "sub_emitter",
71                "Age threshold must be finite in (0,3600]",
72            ));
73        }
74        Ok(())
75    }
76}