rdpe/lib.rs
1//! # RDPE - Reaction Diffusion Particle Engine
2//!
3//! GPU-accelerated particle simulations with a simple, declarative API.
4//!
5//! RDPE handles all the GPU complexity (buffers, shaders, spatial hashing) so you can
6//! focus on defining particle behavior through composable rules.
7//!
8//! ## Quick Start
9//!
10//! ```ignore
11//! use rdpe::prelude::*;
12//!
13//! #[derive(Particle, Clone)]
14//! struct Ball {
15//! position: Vec3,
16//! velocity: Vec3,
17//! }
18//!
19//! fn main() {
20//! Simulation::<Ball>::new()
21//! .with_particle_count(10_000)
22//! .with_bounds(1.0)
23//! .with_spawner(|i, _| Ball {
24//! position: Vec3::new(0.0, 0.5, 0.0),
25//! velocity: Vec3::ZERO,
26//! })
27//! .with_rule(Rule::Gravity(9.8))
28//! .with_rule(Rule::BounceWalls { restitution: 1.0 })
29//! .run();
30//! }
31//! ```
32//!
33//! ## Core Concepts
34//!
35//! ### Particles
36//!
37//! Define your particle struct with `#[derive(Particle)]`. Required fields:
38//! - `position: Vec3` - particle position in 3D space
39//! - `velocity: Vec3` - particle velocity
40//!
41//! Optional fields:
42//! - `#[color] color: Vec3` - custom particle color (RGB, 0.0-1.0)
43//! - `particle_type: u32` - for typed interactions (auto-added if not present)
44//! - Any other `f32`, `u32`, `i32`, `Vec2`, `Vec3`, `Vec4` fields
45//!
46//! ### Rules
47//!
48//! Rules define particle behavior. They execute every frame in order:
49//!
50//! ```ignore
51//! .with_rule(Rule::Gravity(9.8)) // Apply forces
52//! .with_rule(Rule::Separate { ... }) // Neighbor interactions
53//! .with_rule(Rule::SpeedLimit { ... }) // Constrain velocity
54//! .with_rule(Rule::Drag(1.0)) // Apply friction
55//! .with_rule(Rule::BounceWalls { restitution: 1.0 }) // Boundary conditions
56//! ```
57//!
58//! ### Typed Interactions
59//!
60//! Use [`ParticleType`] derive for type-safe particle categories:
61//!
62//! ```ignore
63//! #[derive(ParticleType, Clone, Copy, PartialEq)]
64//! enum Species {
65//! Prey,
66//! Predator,
67//! }
68//!
69//! // Predators chase prey
70//! Rule::Chase {
71//! self_type: Species::Predator.into(),
72//! target_type: Species::Prey.into(),
73//! radius: 0.3,
74//! strength: 2.0,
75//! }
76//! ```
77//!
78//! ## Spatial Hashing
79//!
80//! Neighbor-based rules (Separate, Cohere, Align, etc.) use spatial hashing
81//! for efficient neighbor queries. Configure with:
82//!
83//! ```ignore
84//! .with_spatial_config(cell_size, grid_resolution)
85//! ```
86//!
87//! - `cell_size` should be >= your largest interaction radius
88//! - `grid_resolution` must be a power of 2 (16, 32, 64)
89//!
90//! ## Feature Overview
91//!
92//! | Category | Rules |
93//! |----------|-------|
94//! | Physics | [`Rule::Gravity`], [`Rule::Drag`], [`Rule::Acceleration`] |
95//! | Boundaries | [`Rule::BounceWalls`], [`Rule::WrapWalls`] |
96//! | Forces | [`Rule::AttractTo`], [`Rule::RepelFrom`] |
97//! | Movement | [`Rule::Wander`], [`Rule::SpeedLimit`] |
98//! | Flocking | [`Rule::Separate`], [`Rule::Cohere`], [`Rule::Align`] |
99//! | Collision | [`Rule::Collide`] |
100//! | Types | [`Rule::Typed`], [`Rule::Convert`], [`Rule::Chase`], [`Rule::Evade`] |
101//! | Custom | [`Rule::Custom`] for raw WGSL |
102
103mod emitter;
104pub mod error;
105pub mod field;
106mod gpu;
107pub mod input;
108mod interactions;
109pub mod lifecycle;
110pub mod rules;
111pub mod shader_utils;
112mod simulation;
113mod spawn;
114mod spatial;
115pub mod sub_emitter;
116pub mod textures;
117pub mod time;
118mod uniforms;
119pub mod visuals;
120pub mod selection;
121
122pub use bytemuck;
123pub use emitter::Emitter;
124pub use error::{GpuError, SimulationError, TextureError};
125pub use field::{FieldConfig, FieldRegistry, FieldType};
126pub use glam::{Vec2, Vec3, Vec4};
127pub use gpu::GlyphRenderer;
128pub use gpu::VolumeConfig;
129pub use gpu::VolumeRenderState;
130pub use gpu::{FieldSystemGpu, create_particle_field_bind_group_layout};
131pub use interactions::InteractionMatrix;
132pub use lifecycle::Lifecycle;
133pub use rdpe_derive::{MultiParticle, Particle, ParticleType};
134pub use rules::{AgentState, CustomRuleBuilder, Falloff, Rule, Transition};
135pub use simulation::Simulation;
136pub use spawn::SpawnContext;
137pub use sub_emitter::{SpawnTrigger, SubEmitter};
138pub use textures::{AddressMode, FilterMode, TextureConfig, TextureRegistry};
139pub use uniforms::{CustomUniforms, UniformValue, UpdateContext};
140pub use visuals::{BlendMode, ColorMapping, ConfigDiff, GlyphColorMode, GlyphConfig, GlyphMode, HotSwapChange, Palette, ParticleShape, VertexEffect, VisualConfig, WireframeMesh};
141pub use gpu::{AdjacencyGpu, SpatialGpu, SpatialGridViz};
142pub use spatial::{AdjacencyConfig, SpatialConfig};
143
144/// Trait automatically implemented by `#[derive(Particle)]`.
145///
146/// This trait bridges your Rust particle struct to GPU-compatible memory layout.
147/// The derive macro generates:
148/// - A companion `{Name}Gpu` struct with proper alignment/padding
149/// - WGSL struct definition for compute shaders
150/// - Conversion between Rust and GPU representations
151///
152/// # Do Not Implement Manually
153///
154/// This trait should only be derived, never implemented by hand.
155/// The derive macro handles complex GPU memory layout requirements.
156///
157/// # Example
158///
159/// ```ignore
160/// #[derive(Particle, Clone)]
161/// struct MyParticle {
162/// position: Vec3, // Required
163/// velocity: Vec3, // Required
164/// #[color]
165/// color: Vec3, // Optional: custom color
166/// particle_type: u32, // Optional: for typed rules
167/// energy: f32, // Optional: custom data
168/// }
169/// ```
170pub trait ParticleTrait: Clone + Send + Sync {
171 /// GPU-compatible representation with proper memory alignment.
172 ///
173 /// Generated automatically by the derive macro. Includes padding
174 /// fields to satisfy WGSL alignment requirements (vec3 → 16-byte aligned).
175 type Gpu: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable + Send + Sync;
176
177 /// WGSL struct definition for use in compute shaders.
178 ///
179 /// Generated to match the GPU struct layout exactly.
180 const WGSL_STRUCT: &'static str;
181
182 /// Name of the field marked with `#[color]`, if any.
183 ///
184 /// Used by the renderer to determine which field contains particle color.
185 /// If `None`, particles are colored based on position.
186 const COLOR_FIELD: Option<&'static str>;
187
188 /// Byte offset of the color field within the GPU struct.
189 ///
190 /// Used to configure vertex attributes for rendering.
191 const COLOR_OFFSET: Option<u32>;
192
193 /// Byte offset of the `alive` field within the GPU struct.
194 ///
195 /// Used to configure vertex attributes for culling dead particles.
196 /// Always present since lifecycle fields are auto-injected.
197 const ALIVE_OFFSET: u32;
198
199 /// Byte offset of the `scale` field within the GPU struct.
200 ///
201 /// Used to configure vertex attributes for particle sizing.
202 /// Always present since lifecycle fields are auto-injected.
203 const SCALE_OFFSET: u32;
204
205 /// Additional WGSL code prepended to shaders.
206 ///
207 /// Used by `MultiParticle` enums to inject type constants and helper functions.
208 /// Regular particle structs leave this empty.
209 ///
210 /// Example for a multi-particle enum:
211 /// ```wgsl
212 /// const BOID: u32 = 0u;
213 /// const PREDATOR: u32 = 1u;
214 /// fn is_boid(p: Particle) -> bool { return p.particle_type == BOID; }
215 /// ```
216 const EXTRA_WGSL: &'static str = "";
217
218 /// Convert this particle to its GPU representation.
219 ///
220 /// Called once per particle during initialization.
221 fn to_gpu(&self) -> Self::Gpu;
222
223 /// Convert from GPU representation back to this particle type.
224 ///
225 /// Used for CPU readback of particle data (e.g., for inspection).
226 fn from_gpu(gpu: &Self::Gpu) -> Self;
227
228 /// Get field names and their display values for inspection.
229 ///
230 /// Returns a vector of (field_name, formatted_value) pairs for all
231 /// user-defined fields in the particle struct. Used by the built-in
232 /// particle inspector panel.
233 ///
234 /// This is automatically generated by the derive macro and should not
235 /// be implemented manually.
236 fn inspect_fields(&self) -> Vec<(&'static str, String)>;
237
238 /// Render editable UI widgets for all particle fields.
239 ///
240 /// Returns `true` if any field was modified. Used by the built-in
241 /// particle inspector to allow live editing of particle values.
242 ///
243 /// This is automatically generated by the derive macro and should not
244 /// be implemented manually.
245 #[cfg(feature = "egui")]
246 fn render_editable_fields(&mut self, ui: &mut egui::Ui) -> bool;
247}
248
249/// Convenient re-exports for common usage.
250///
251/// # Usage
252///
253/// ```ignore
254/// use rdpe::prelude::*;
255/// ```
256///
257/// This imports:
258/// - [`Simulation`] - the simulation builder
259/// - [`Rule`] - all available rules
260/// - [`Emitter`] - particle emitters
261/// - [`Particle`] - derive macro for particle structs
262/// - [`ParticleType`] - derive macro for type enums
263/// - [`Vec2`], [`Vec3`], [`Vec4`] - glam vector types
264/// - [`ParticleTrait`] - the particle trait (rarely needed directly)
265pub mod prelude {
266 pub use crate::emitter::Emitter;
267 pub use crate::field::{FieldConfig, FieldRegistry, FieldType};
268 pub use crate::gpu::VolumeConfig;
269 pub use crate::input::{Input, KeyCode, MouseButton};
270 pub use crate::interactions::InteractionMatrix;
271 pub use crate::lifecycle::Lifecycle;
272 pub use crate::rules::{AgentState, CustomRuleBuilder, Falloff, Rule, Transition};
273 pub use crate::simulation::Simulation;
274 pub use crate::spawn::SpawnContext;
275 pub use crate::sub_emitter::{SpawnTrigger, SubEmitter};
276 pub use crate::textures::{AddressMode, FilterMode, TextureConfig, TextureRegistry};
277 pub use crate::time::Time;
278 pub use crate::uniforms::{CustomUniforms, UpdateContext};
279 pub use crate::visuals::{BlendMode, ColorMapping, ConfigDiff, HotSwapChange, Palette, ParticleShape, VertexEffect, VisualConfig, WireframeMesh};
280 pub use crate::ParticleTrait;
281 pub use crate::{Vec2, Vec3, Vec4};
282 pub use rdpe_derive::{MultiParticle, Particle, ParticleType};
283 #[cfg(feature = "egui")]
284 pub use crate::selection::{selected_particle, selected_particle_data};
285 #[cfg(feature = "egui")]
286 pub use egui;
287}