Deluge Firmware 1.3.0
Build date: 2026.08.23
Loading...
Searching...
No Matches
deluge::hid::display::Rainfall Class Reference

A field of falling diagonal rain streaks, in the visual grammar of the Deluge logo. More...

#include <rainfall.h>

Collaboration diagram for deluge::hid::display::Rainfall:

Classes

struct  Block
 A single square block of a streak. More...
struct  Streak
 A streak's shape and position, without its motion. Exposed so the spacing rule can be unit-tested against hand-built cases. More...
struct  Drop
 The whole logo, as one object. More...

Public Member Functions

 Rainfall ()
 Construct a field that is already populated, so the first rendered frame is not empty.
void scatter ()
 Re-seed every drop across the panel.
void advance ()
 Advance every drop one frame, respawning any whose tail has left the panel.
size_t lengthOf (size_t drop) const
 Cells in a drop's streak.
Block cellAt (size_t drop, size_t cell) const
 One block of a drop's streak.
float speedOf (size_t drop) const
 Speed of a drop, px per frame. Exposed for tests.

Static Public Member Functions

static constexpr float minGapFor (int32_t size)
 Minimum distance allowed between two live drops of the same block size, px.
static float separationSquared (const Streak &a, const Streak &b)
 Squared distance between two streaks, px squared.

Static Public Attributes

static constexpr float kLogoSmallScaleChance = 0.10f
 Chance a logo is emitted at the smallest cell scale.
Panel geometry

Mirrors OLED_MAIN_WIDTH_PIXELS, OLED_MAIN_HEIGHT_PIXELS and OLED_MAIN_TOPMOST_PIXEL. screensaver.cpp static_asserts these against the real macros, so they cannot drift.

static constexpr int32_t kWidth = 128
static constexpr int32_t kHeight = 48
static constexpr int32_t kTopmost = 5
static constexpr int32_t kVisibleHeight = kHeight - kTopmost
Tuning
static constexpr size_t kNumDrops = 34
 Drops in the field at once.
static constexpr float kSpeedFar = 0.5f
 Speed of the most distant drops, px per frame, applied to both axes. On the kSpeedStep grid.
static constexpr float kSpeedNear = 1.5f
 Speed of the nearest drops, px per frame, applied to both axes. On the kSpeedStep grid.
static constexpr float kSpeedStep = 0.25f
 Granularity every drop's speed is snapped to, px per frame.
static constexpr float kSpawnFromTop = static_cast<float>(kWidth) / static_cast<float>(kWidth + kVisibleHeight)
 Chance that a respawning drop enters through the top edge rather than the left.
static constexpr int32_t kSpawnAttempts = 6
 Candidate positions tried per spawn before settling for the roomiest one found.

Private Member Functions

uint32_t nextRandom ()
 Step the linear congruential generator.
float nextRandomFloat ()
 Step the linear congruential generator, mapped to [0, 1).
void roll (Drop &drop)
 Give a drop a fresh depth, and the size, speed and length that follow from it.
void place (Drop &drop, bool seeded)
 Position a drop, either scattered across the panel or entering an edge.
void emitLogo ()
 Start a logo falling, picking its scale, speed and entry edge.
void advanceLogo ()
 Move the live logo, retire it once clear, and run the emission countdown.
int32_t nextLogoInterval ()
void spawn (size_t index, bool seeded)
 Replace drops_[index], honouring the minimum-spacing rule.

Static Private Member Functions

static constexpr float reachOf (int32_t size, int32_t length)
 Distance a streak's tail trails behind its head, on each axis, in pixels.
static Streak streakOf (const Drop &drop)
 Extract a drop's shape and position as a Streak, for spacing checks.

Private Attributes

std::array< Drop, kNumDropsdrops_ {}
Logo logo_ {}
int32_t logoCountdown_ {kLogoIntervalMinFrames}
uint32_t rngState_ {0x1EAF7A11}

Logo emission

Every few minutes one complete Deluge logo falls through the field and leaves. It is one object rather than seven drops: the bootloader's logo is exactly seven diagonal runs of three or four cells – the rain's own vocabulary, arranged – so its 25 cells sit at fixed offsets from a single origin.

static constexpr size_t kLogoCells = 25
 Cells in the logo's grid, and the width and height of that grid in cells.
static constexpr int32_t kLogoGridWidth = 11
static constexpr int32_t kLogoGridHeight = 10
static constexpr int32_t kLogoIntervalMinFrames = 2400
 Shortest and longest wait between logo emissions, in frames.
static constexpr int32_t kLogoIntervalMaxFrames = 7200
bool logoActive () const
Block logoCellAt (size_t cell) const
 One cell of the logo.
int32_t logoScale () const
 Cell scale of the live logo, in pixels. Exposed for tests.
float logoSpeed () const
 Speed of the live logo, px per frame. Exposed for tests.
void forceLogo ()
 Emit a logo on the next advance(), whatever the countdown says.

Detailed Description

A field of falling diagonal rain streaks, in the visual grammar of the Deluge logo.

Each drop is a rigid streak of three or four square cells stepping down-right at 45 degrees, exactly as the streaks in the bootloader's logo do. Nothing ever draws the logo itself.

Note
Deliberately has no display dependency, so the motion maths is unit-testable on the host and the ASCII preview harness can drive it. That is why the panel dimensions below are repeated rather than included – cpu_specific.h is target-only.

Member Function Documentation

◆ cellAt()

Rainfall::Block deluge::hid::display::Rainfall::cellAt ( size_t drop,
size_t cell ) const
nodiscard

One block of a drop's streak.

Parameters
dropDrop index, less than kNumDrops.
cellCell index, less than lengthOf(drop). Cell 0 leads, at the bottom-right.
Returns
The block's unclipped position and pixel size.

◆ forceLogo()

void deluge::hid::display::Rainfall::forceLogo ( )
inline

Emit a logo on the next advance(), whatever the countdown says.

Note
For the preview harness: the interval is minutes long, so the animation would otherwise be uninspectable without waiting it out. Does nothing if one is already up.

◆ lengthOf()

size_t deluge::hid::display::Rainfall::lengthOf ( size_t drop) const
inlinenodiscard

Cells in a drop's streak.

Parameters
dropDrop index, less than kNumDrops.
Returns
3 or 4, matching the streak lengths in the bootloader's logo.

◆ logoActive()

bool deluge::hid::display::Rainfall::logoActive ( ) const
inlinenodiscard
Returns
True while a logo is on or approaching the panel.

◆ logoCellAt()

Rainfall::Block deluge::hid::display::Rainfall::logoCellAt ( size_t cell) const
nodiscard

One cell of the logo.

Precondition
logoActive() is true.
Parameters
cellCell index, less than kLogoCells.
Returns
The cell's unclipped block position and pixel size.

◆ logoScale()

int32_t deluge::hid::display::Rainfall::logoScale ( ) const
inlinenodiscard

Cell scale of the live logo, in pixels. Exposed for tests.

Returns
1, 2 or 3; zero when no logo is active.

◆ logoSpeed()

float deluge::hid::display::Rainfall::logoSpeed ( ) const
inlinenodiscard

Speed of the live logo, px per frame. Exposed for tests.

Returns
The logo's speed, always a multiple of kSpeedStep; zero when none is active.

◆ minGapFor()

constexpr float deluge::hid::display::Rainfall::minGapFor ( int32_t size)
inlinestaticnodiscardconstexpr

Minimum distance allowed between two live drops of the same block size, px.

Note
Only same-size pairs are checked. A near streak passing over a far one is the parallax reading correctly, and preventing it would need per-frame work.
Parameters
sizeBlock size in pixels: 1, 2 or 3.
Returns
The minimum permitted distance in pixels.

◆ nextLogoInterval()

int32_t deluge::hid::display::Rainfall::nextLogoInterval ( )
private
Returns
A fresh wait before the next emission, in frames.

◆ nextRandom()

uint32_t deluge::hid::display::Rainfall::nextRandom ( )
private

Step the linear congruential generator.

Returns
The new RNG state.

◆ nextRandomFloat()

float deluge::hid::display::Rainfall::nextRandomFloat ( )
private

Step the linear congruential generator, mapped to [0, 1).

Returns
The next LCG output, mapped to [0, 1).

◆ place()

void deluge::hid::display::Rainfall::place ( Drop & drop,
bool seeded )
private

Position a drop, either scattered across the panel or entering an edge.

Parameters
dropThe drop to position in place.
seededTrue to place it anywhere on the panel, false to enter the top or left edge.

◆ reachOf()

constexpr float deluge::hid::display::Rainfall::reachOf ( int32_t size,
int32_t length )
inlinestaticnodiscardconstexprprivate

Distance a streak's tail trails behind its head, on each axis, in pixels.

Parameters
sizeBlock size in pixels: 1, 2 or 3.
lengthStreak length in cells: 3 or 4.
Returns
The tail's trailing distance, in pixels.

◆ roll()

void deluge::hid::display::Rainfall::roll ( Drop & drop)
private

Give a drop a fresh depth, and the size, speed and length that follow from it.

Parameters
dropThe drop to roll and populate in place.

◆ scatter()

void deluge::hid::display::Rainfall::scatter ( )

Re-seed every drop across the panel.

Note
Does not reseed the LCG, so successive calls produce different fields.

◆ separationSquared()

float deluge::hid::display::Rainfall::separationSquared ( const Streak & a,
const Streak & b )
staticnodiscard

Squared distance between two streaks, px squared.

Both are parallel segments at 45 degrees, so this is closed-form: the offset across travel if the two overlap along travel, otherwise the hypotenuse of the across- and along-gaps.

Note
Squared, so the caller compares against a squared minimum and no sqrt is needed.
Parameters
aOne streak.
bThe other streak.
Returns
The squared distance between them, in pixels squared.

◆ spawn()

void deluge::hid::display::Rainfall::spawn ( size_t index,
bool seeded )
private

Replace drops_[index], honouring the minimum-spacing rule.

Parameters
indexSlot in drops_ to replace.
seededForwarded to place(): true to scatter anywhere, false to enter an edge.

◆ speedOf()

float deluge::hid::display::Rainfall::speedOf ( size_t drop) const
inlinenodiscard

Speed of a drop, px per frame. Exposed for tests.

Parameters
dropDrop index, less than kNumDrops.
Returns
The drop's speed, always a multiple of kSpeedStep.

◆ streakOf()

Streak deluge::hid::display::Rainfall::streakOf ( const Drop & drop)
inlinestaticnodiscardprivate

Extract a drop's shape and position as a Streak, for spacing checks.

Parameters
dropThe drop to convert.
Returns
The equivalent Streak.

Member Data Documentation

◆ kLogoIntervalMinFrames

int32_t deluge::hid::display::Rainfall::kLogoIntervalMinFrames = 2400
staticconstexpr

Shortest and longest wait between logo emissions, in frames.

Note
2 to 6 minutes at the screensaver's 50ms tick. Deliberately long: a sighting should be a surprise, so a short idle period usually shows none at all.

◆ kLogoSmallScaleChance

float deluge::hid::display::Rainfall::kLogoSmallScaleChance = 0.10f
staticconstexpr

Chance a logo is emitted at the smallest cell scale.

Note
Weighted away from scale 1: there the logo is single pixels moving at the far layer's own speed, so the surrounding drizzle camouflages it and it reads as a dense patch of rain rather than as the mark. Acceptable as the rare case, not as a third of them.

◆ kSpawnFromTop

float deluge::hid::display::Rainfall::kSpawnFromTop = static_cast<float>(kWidth) / static_cast<float>(kWidth + kVisibleHeight)
staticconstexpr

Chance that a respawning drop enters through the top edge rather than the left.

The ratio of the two edge lengths, which is what makes the flux uniform across the panel. Spawning only along the top starves the lower-right: everything drifts right, so a drop seeded past x = kWidth - kVisibleHeight leaves the right edge before it reaches the bottom.

◆ kSpeedNear

float deluge::hid::display::Rainfall::kSpeedNear = 1.5f
staticconstexpr

Speed of the nearest drops, px per frame, applied to both axes. On the kSpeedStep grid.

Note
Above 1 px/frame a drop is not drawn at every position it passes through, so it visibly skips: measured at 24.7% of frames at 1.25 px/frame and 49.8% at 1.5. That is accepted deliberately, in exchange for rain that moves at this pace. Capping here at 1.0 removes the skipping but makes the fastest drops a third slower, and shortening kFrameIntervalMS to compensate speeds the whole field up – both were tried and both were worse. Slower, smoother rain is a real option; it is just not the one chosen.

◆ kSpeedStep

float deluge::hid::display::Rainfall::kSpeedStep = 0.25f
staticconstexpr

Granularity every drop's speed is snapped to, px per frame.

Note
Must stay a negative power of two, so that speeds and positions are exactly representable and a drop's step cadence cannot drift. See roll() for why a coarse, small-denominator speed is what keeps the motion smooth rather than juddery.

◆ logoCountdown_

int32_t deluge::hid::display::Rainfall::logoCountdown_ {kLogoIntervalMinFrames}
private

Frames until the next emission. Only counts down while no logo is active, so two can never overlap and the gap is measured between sightings rather than between starts.


The documentation for this class was generated from the following files:
  • src/deluge/hid/display/rainfall.h
  • src/deluge/hid/display/rainfall.cpp