Selkie--Widget--ScatterPlot

NAME

Selkie::Widget::ScatterPlot - 2D point plot using braille sub-cell dots

SYNOPSIS

use Selkie::Widget::ScatterPlot;
use Selkie::Sizing;

# Single-series scatter — auto-derives axis ranges from the data.
# Points are Pairs (x => y) so Raku doesn't flatten the list.
my @points = (1..50).map: { (rand * 100) => (rand * 100) };
my $sp = Selkie::Widget::ScatterPlot.new(
    series => [
        { label => 'samples', points => @points },
    ],
    sizing => Sizing.flex,
);

# Multi-series with explicit colours
my $sp2 = Selkie::Widget::ScatterPlot.new(
    series => [
        { label => 'group A', points => @group-a, color => 0xE69F00 },
        { label => 'group B', points => @group-b, color => 0x56B4E9 },
    ],
    sizing => Sizing.flex,
);

# Tip: use Pair (x => y), [x, y] arrays, or hash {x => , y => } per
# point. Don't use bare lists `(x, y)` — Raku flattens them in
# array context and your single-point scatter becomes two
# independent values.

# Reactive
my $live = Selkie::Widget::ScatterPlot.new(
    store-path => <viz scatter-data>,
    sizing     => Sizing.flex,
);

DESCRIPTION

A scatter plot of 2D points. Uses Unicode braille (U+2800-U+28FF) for sub-cell resolution: each terminal cell holds a 2Ɨ4 grid of dot positions (8 dots per cell). A 50-cell-wide plot can resolve 100 distinct x-positions, and a 20-cell-tall plot can resolve 80 distinct y-positions.

The braille dot grid

Each braille codepoint encodes which of 8 sub-cell dots are filled:

0 3
1 4
2 5
6 7

The codepoint is U+2800 + bit-pattern, where bit N controls dot N. A cell with all 8 dots filled is ⣿ (U+28FF). A cell with no dots is ā € (U+2800).

Multi-series colour collision

Each braille cell renders with a single foreground colour. When two series have dots in the same 2Ɨ4 sub-cell window, the cell's colour is determined by the :overlap setting:

  • z-order (default) — the last-drawn series wins the cell's colour. The earlier series' dots are still drawn but they take the later series' colour.

This is a documented limitation of single-foreground terminal rendering. For non-overlapping multi-series, the colour assignment is always correct. For overlapping data, prefer faceted layouts (separate scatter plots per series) over single-plot overlay.

Range

Each axis range auto-derives from the data extent. Pass explicit :x-min, :x-max, :y-min, :y-max to fix any of them. Useful when streaming so the axes don't jitter as new points expand the range.

EXAMPLES

Single cluster

my @cluster = (1..50).map: {
    (50 + rand * 20 - 10, 50 + rand * 20 - 10);
};
my $sp = Selkie::Widget::ScatterPlot.new(
    series => [{ label => 'cluster', points => @cluster }],
    x-min  => 0, x-max => 100,
    y-min  => 0, y-max => 100,
    sizing => Sizing.flex,
);

Two clusters with distinct colours

my $sp = Selkie::Widget::ScatterPlot.new(
    series => [
        { label => 'cluster A', points => @a, color => 0xE69F00 },
        { label => 'cluster B', points => @b, color => 0x009E73 },
    ],
    sizing => Sizing.flex,
);

SEE ALSO

has Positional @.series

List of series. Each series is a hash with label (Str), points (list of (x, y) pairs), and optional color (UInt RGB).

has Positional[Str] @.store-path

Reactive store path. Mutually exclusive with series.

has Str $.palette

Series-color palette. Used when individual series don't specify color.

has Real $.x-min

Optional explicit X axis bounds. Auto-derived when unset.

has Real $.y-min

Optional explicit Y axis bounds. Auto-derived when unset.

has Str $.overlap

How to handle cells where multiple series have dots. Currently only z-order is supported (last-drawn wins the colour).

has Str $.empty-message

Message rendered when there are no points. The default is the expected startup state for monitoring dashboards. Set to the empty string to suppress.

method on-store-attached

method on-store-attached(
    $store
) returns Mu

Hook called when the widget is attached to a store. Wires up a subscription against :store-path so the chart re-renders on state changes. No-op in :series mode.

method set-series

method set-series(
    @new
) returns Mu

Replace the chart's series array. Each series' points are realised into an Array up-front because callers often pass Seqs from .map chains, and a TUI re-renders every frame — an exhausted Seq would produce an empty plot on subsequent renders. Throws when constructed in :store-path mode.

method braille-glyph

method braille-glyph(
    Int $bits where { ... }
) returns Str

Compute the braille codepoint for a given bit pattern (0..255). Pure function, exhaustively unit-testable.

method braille-bit

method braille-bit(
    Int $sub-col where { ... },
    Int $sub-row where { ... }
) returns UInt

Compute the bit position within a braille cell for a sub-cell coordinate. $sub-col is 0 or 1; $sub-row is 0..3. Returns a bit index 0..7 suitable for use with braille-glyph.

Selkie v0.10.0

High-level TUI framework built on Notcurses

Authors

  • Matt Doughty

License

Artistic-2.0

Dependencies

Notcurses::Native:ver<0.4.0+>:auth<zef:apogee>

Test Dependencies

Provides

  • Selkie
  • Selkie::App
  • Selkie::App::Internal::Dispatch
  • Selkie::App::Internal::ErrorLog
  • Selkie::App::Internal::FocusTree
  • Selkie::App::Internal::HitTest
  • Selkie::App::Internal::IdleBudget
  • Selkie::App::Internal::OverlayTree
  • Selkie::App::Internal::RenderLoop
  • Selkie::App::Internal::ScreenModalLifecycle
  • Selkie::App::Internal::Terminal
  • Selkie::App::Internal::TerminalSequences
  • Selkie::Container
  • Selkie::EffectiveBounds
  • Selkie::Event
  • Selkie::Layout::Allocate
  • Selkie::Layout::HBox
  • Selkie::Layout::Split
  • Selkie::Layout::VBox
  • Selkie::Plot::Palette
  • Selkie::Plot::Scaler
  • Selkie::Plot::Ticks
  • Selkie::ScreenManager
  • Selkie::Sizing
  • Selkie::Store
  • Selkie::Style
  • Selkie::Test::Focus
  • Selkie::Test::Keys
  • Selkie::Test::Snapshot
  • Selkie::Test::Snapshot::Harness
  • Selkie::Test::Store
  • Selkie::Test::Supply
  • Selkie::Test::Tree
  • Selkie::Theme
  • Selkie::Trace
  • Selkie::Tree
  • Selkie::Widget
  • Selkie::Widget::Axis
  • Selkie::Widget::BarChart
  • Selkie::Widget::Border
  • Selkie::Widget::Button
  • Selkie::Widget::CardList
  • Selkie::Widget::Checkbox
  • Selkie::Widget::CommandPalette
  • Selkie::Widget::ConfirmModal
  • Selkie::Widget::FileBrowser
  • Selkie::Widget::FocusableByDefault
  • Selkie::Widget::Heatmap
  • Selkie::Widget::HelpOverlay
  • Selkie::Widget::Histogram
  • Selkie::Widget::Image
  • Selkie::Widget::Legend
  • Selkie::Widget::LineChart
  • Selkie::Widget::ListView
  • Selkie::Widget::Modal
  • Selkie::Widget::MultiLineInput
  • Selkie::Widget::PasswordStrength
  • Selkie::Widget::Plot
  • Selkie::Widget::ProgressBar
  • Selkie::Widget::RadioGroup
  • Selkie::Widget::RichText
  • Selkie::Widget::RichText::Span
  • Selkie::Widget::ScatterPlot
  • Selkie::Widget::ScrollView
  • Selkie::Widget::Select
  • Selkie::Widget::Sparkline
  • Selkie::Widget::Spinner
  • Selkie::Widget::TabBar
  • Selkie::Widget::Table
  • Selkie::Widget::Text
  • Selkie::Widget::TextInput
  • Selkie::Widget::TextInput::HighlightSpan
  • Selkie::Widget::TextStream
  • Selkie::Widget::Toast
  • Selkie::Widget::ViewportedCardList

Documentation

The Camelia image is copyright 2009 by Larry Wall. "Raku" is trademark of the Yet Another Society. All rights reserved.