Skip to content

[ON HOLD] FEATURE: Display state isolation and crossing opacity fade helper #33

Description

@FabioRoss

What problem are you trying to solve?

Display code reads currentSector (or the equivalent) to show which sector the
driver is in. But during a crossing event, the library is in a buffering phase:
the vehicle is near the line, GPS fixes are being accumulated, and the sector
number may already have advanced internally while interpolation is still pending.

If display code reads currentSector during this window, it sees an intermediate
state — the sector display jumps and flickers as the driver approaches a line,
before the crossing is even confirmed.

A second display problem: after a confirmed crossing, the UI wants to show a
"sector crossed!" notification that fades out naturally over ~1 second. Without
help from the library, the user has to record the crossingTimestamp themselves
and compute the fade value every render cycle.

Proposed solution

===============================================================================
PROPOSED SOLUTION — PART A: Display state isolation

Maintain a separate currentSectorDisplay integer that is ONLY updated when
a confirmed crossing callback fires (post-interpolation), not during approach
buffering.

// Public member — always safe to read for display purposes
int currentSectorDisplay = 0;

Update rule:

  • currentSector is updated as soon as approach begins (internal use)
  • currentSectorDisplay is updated only inside the sector-crossed callback
    path, after LINE_DETECT_COMPLETED and successful interpolation

Display code reads currentSectorDisplay. Library internals use currentSector.
No user-side state machine needed; no polling, no flicker.

This follows the same principle as double-buffering in graphics: write to the
back buffer freely, flip to front only when complete.

===============================================================================
PROPOSED SOLUTION — PART B: Crossing opacity fade helper

uint8_t getSectorCrossedOPAFade() const;

Returns a value 0–255 representing how "fresh" the last sector crossing is:

if (getCurrentSectorTime() == 0) return 0;     // race not started
if (getCurrentSectorTime() >= 1000) return 0;  // notification expired
return 255 * (1.0f - getCurrentSectorTime() / 1000.0f);  // fade 255→0 over 1s

Usage: Animate a "crossed!" overlay on an OLED or TFT by using this value as
the alpha/brightness of a screen element. No user timer needed — the library
owns the sector start time, so it can compute the fade directly.

Example on a U8g2 OLED display:
uint8_t alpha = lapTimer.getSectorCrossedOPAFade();
if (alpha > 0) {
drawSectorCrossedOverlay(alpha);
}

The function is intentionally simple — 3 lines — and costs zero extra RAM.
It's a read-only computation over state the library already maintains.
Optionally pass fade time in ms as parameter.

Alternatives considered

  1. User records crossing timestamp themselves — current workaround.
    Requires storing an unsigned long in sketch, computing age every frame,
    knowing when to reset it. Trivial but duplicated across every project.

  2. User polls getCrossing() to detect transitions — getCrossing() is true
    DURING the approach zone, not after confirmation. Polling its falling edge
    would get the end of buffering but not the confirmed interpolated crossing.

  3. User reads currentSector directly — works between crossings, flickers at
    every approach. Most beginner projects suffer from this.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions