Skip to content

Source: particles.h

particles

One home for things that move under forces and get drawn.

Classes

Name Description
FrameTime Converts elapsed time into a per-frame scale, so physics runs at one speed on every target.
Pool

FrameTime

class FrameTime
src/light/powerfunctions/particles.h:25

Converts elapsed time into a per-frame scale, so physics runs at one speed on every target.

Why time is scaled rather than quantised: power-functions.md#particles

Public Methods

inline explicit FrameTime(uint16_t referenceHz = 60) : referenceHz is the rate the effect's numbers are written against; 60 is the convention.

inline uint32_t advance(uint32_t nowMs) : Call once per frame. Returns the scale in 8.8 fixed point (256 == one reference frame).

inline void reset() : Forget the accumulated time, so the next frame starts the clock again.

Public Static Attributes

constexpr uint32_t kOne = 256 : one reference frame

constexpr uint32_t kMaxScale = 256 * 8 : The largest scale a frame may report, which bounds a long stall to eight reference frames.

Pool

struct Pool
src/light/powerfunctions/particles.h:108

Public Attributes

draw::pos_t * x = nullptr : position along the first axis, in sub-pixels

draw::pos_t * y = nullptr : position along the second axis, in sub-pixels

draw::pos_t * vx = nullptr : velocity along the first axis

draw::pos_t * vy = nullptr : velocity along the second axis

uint16_t * ttl = nullptr : Lifetime in reference frames; 0 = dead. SIXTEEN bits, not eight.

uint8_t * hue = nullptr : palette index per particle

uint8_t * size = nullptr : Per-particle radius in WHOLE pixels, 0 = a single sub-pixel splat. OPTIONAL.

uint8_t * acc = nullptr : Sub-unit force accumulator, 3.4 fixed point, one nibble per axis (low = x, high = y).

uint16_t count = 0 : how many slots the arrays hold

uint32_t ageCarry_ = 0 : sub-frame aging remainder (see age())

int64_t gCarry_ = 0 : sub-unit gravity remainder (see gravity())

Public Methods

inline bool valid() const : Whether every lane is allocated and the pool holds room for at least one particle.

inline void clear() : Kill every particle: the state a pool starts in, and what prepare() should leave behind.

inline uint16_t findFree() const : Index of a free slot, or count when the pool is full. Linear.

inline bool spawn(draw::pos_t px, draw::pos_t py, draw::pos_t svx, draw::pos_t svy, uint16_t life, uint8_t color, uint8_t radius = 0) : Bring one particle to life. Returns false when the pool is full, so an emitter can stop rather than overwrite a living particle.

inline void gravity(draw::pos_t g, uint32_t scale = FrameTime::kOne) : Constant acceleration, the usual case being gravity. g is in sub-pixel units per frame².

inline void force(draw::pos_t fx, draw::pos_t fy, uint32_t scale = FrameTime::kOne) : A constant push in any direction: wind, a tilt control, a thrust.

inline void forceSmall(int8_t fx, int8_t fy) : A force too SMALL to move a velocity by one unit per frame, accumulated until it does.

inline void drag(uint8_t k, uint32_t scale = FrameTime::kOne) : Velocity damping, v *= (256 - k) / 256: air resistance.

inline void attract(draw::pos_t ax, draw::pos_t ay, int32_t strength) : Pull every particle toward a point with an inverse-square falloff, clamped near the center so a particle sitting on the attractor does not receive an unbounded impulse.

inline void step(uint32_t scale = FrameTime::kOne) : Advance every live particle by one frame: position from the CURRENT velocity.

inline void stepDriven(uint32_t scale, bool audioReactive, uint16_t live) : step() with a PER-PARTICLE time scale.

template<typename Drive> inline void stepEach(uint32_t scale, Drive drive) : Advance every live particle, taking each one's drive from drive.

inline void age(uint16_t rate = 1, uint32_t scale = FrameTime::kOne) : Count down every particle's life; a particle reaching zero is dead and its slot is reusable.

inline void bounce(draw::pos_t w, draw::pos_t h, uint16_t e, uint8_t roughness = 0, uint32_t seed = 0) : Reflect particles off the walls of a w by h grid, keeping a fraction e of the speed (restitution.

inline void wrap(draw::pos_t w, draw::pos_t h, bool wrapX = true, bool wrapY = true) : Wrap particles around the grid edges: a particle leaving one side re-enters the other.

inline void killOutside(draw::pos_t w, draw::pos_t h, draw::pos_t margin = 0) : Kill any particle that has left the grid, the alternative to bouncing for sparks meant to fly away and vanish.

inline void angleEmit(draw::pos_t px, draw::pos_t py, angle16 angle, draw::pos_t speed, angle16 cone, uint8_t n, uint16_t life, uint8_t color, uint32_t seed) : Emit n particles from a point in a cone around angle, at speed ± spread. The classic spark/firework burst. seed makes the pattern reproducible.

inline void spray(draw::pos_t px, draw::pos_t py, draw::pos_t speed, uint8_t n, uint16_t life, uint8_t color, uint32_t seed) : Emit n particles from a point with random velocities inside a box: a fountain, a spray, a burst of confetti. angleEmit gives a directed cone.

inline void collide(draw::pos_t radius, uint16_t e = 200, uint32_t seed = 0) : Make live particles bounce off each other. radius is the contact distance in sub-pixel units.

inline void render(const draw::Canvas & cv, uint16_t maxLife = 255, RenderStyle style = RenderStyle::Splat) const : Draw every live particle. Additive sub-pixel by default, so overlapping particles brighten and motion is smooth.

inline uint16_t liveCount() const : How many particles are alive: for a status line, or an effect that tops the pool up.

Public Static Methods

static inline draw::pos_t wrapCoord(draw::pos_t v, draw::pos_t span) : Reduce one coordinate into 0..span, in constant time.

More info

Prior art

The WLED Particle System by Damian Schneider, and Reeves for the name. Its vocabulary of emitters, forces and walls over one pool is the shape this follows, written fresh in fixed point.