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 7The 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
Selkie::Widget::LineChart ā connects points with lines (also braille)
Selkie::Widget::Heatmap ā for 2D data on a regular grid
Selkie::Plot::Palette ā colourblind-safe series palettes
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 MuHook 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 MuReplace 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 StrCompute 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 UIntCompute 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.