// PixelRun: a run of `count` pixels of a single `color` (0xRRGGBB).
// Adjacent runs of the same color merge by summing their counts.
// Demonstrates a numeric custom type with no Sliceable.
///|
struct PixelRun {
color : Int
count : Int
} derive(Debug, Eq)
///|
impl @rle.Mergeable for PixelRun with fn can_merge(a : PixelRun, b : PixelRun) -> Bool {
a.color == b.color
}
///|
impl @rle.Mergeable for PixelRun with fn merge(a : PixelRun, b : PixelRun) -> PixelRun {
{ color: a.color, count: a.count + b.count }
}
///|
impl @rle.HasLength for PixelRun with fn length(self : PixelRun) -> Int {
self.count
}
///|
/// span() delegates to length() here — both return pixel count.
/// Override span() only when it should differ from length (e.g., CRDT tombstones).
impl @rle.Spanning for PixelRun with fn span(self : PixelRun) -> Int {
self.count
}
///|
let red : Int = 0xFF0000
///|
let green : Int = 0x00FF00
///|
let blue : Int = 0x0000FF
///|
fn pixel(color : Int, count : Int) -> PixelRun {
{ color, count }
}
///|
fn print_pixel(run : PixelRun) -> Unit {
println("{color: \{run.color}, count: \{run.count}}")
}
///|
fn print_pixel_opt(run : PixelRun?) -> Unit {
match run {
Some(run) => println("Some({color: \{run.color}, count: \{run.count}})")
None => println("None")
}
}
///|
fn print_run_pos(pos : @rle.RunPos?) -> Unit {
match pos {
Some(pos) => println("Some({run: \{pos.run}, offset: \{pos.offset}})")
None => println("None")
}
}
///|
fn print_position(position : Int?) -> Unit {
match position {
Some(position) => println("Some(\{position})")
None => println("None")
}
}
///|
fn main {
println("=== Same-Color Merge ===")
let rle : @rle.Rle[PixelRun] = @rle.Rle::Rle()
rle.append(pixel(red, 10)) |> @debug.debug //=> Ok(())
rle.append(pixel(red, 5)) |> @debug.debug //=> Ok(())
rle.length() |> println //=> 1 (merged)
print_pixel_opt(rle.get(0)) //=> Some({color: 16711680, count: 15})
println("\n=== Multi-Color Scanline ===")
let rle2 : @rle.Rle[PixelRun] = @rle.Rle::Rle()
rle2.append(pixel(red, 10)) |> @debug.debug
rle2.append(pixel(green, 20)) |> @debug.debug
rle2.append(pixel(blue, 30)) |> @debug.debug
rle2.length() |> println //=> 3
rle2.span() |> println //=> 60 (10+20+30)
println("\n=== Find Run at Position ===")
// red: 0-9, green: 10-29, blue: 30-59
print_run_pos(rle2.find(0)) //=> Some({run: 0, offset: 0})
print_run_pos(rle2.find(25)) //=> Some({run: 1, offset: 15})
print_run_pos(rle2.find(59)) //=> Some({run: 2, offset: 29})
println("\n=== Value At Position ===")
match rle2.value_at(25) {
Ok(run) => print_pixel(run) //=> {color: 65280, count: 20} (green)
Err(e) => e |> @debug.debug
}
println("\n=== Iter All Runs ===")
rle2.iter().each(fn(r) { println("\{r.color} x\{r.count}") })
//=> 16711680 x10 (red)
//=> 65280 x20 (green)
//=> 255 x30 (blue)
println("\n=== From Array (batch merge) ===")
let pixels = [
pixel(red, 5),
pixel(red, 3),
pixel(green, 10),
pixel(green, 10),
pixel(blue, 1),
]
let rle3 = @rle.Rle::from_array(pixels)
rle3.length() |> println //=> 3 (red=8, green=20, blue=1)
print_pixel_opt(rle3.get(0)) //=> Some({color: 16711680, count: 8})
print_pixel_opt(rle3.get(1)) //=> Some({color: 65280, count: 20})
print_pixel_opt(rle3.get(2)) //=> Some({color: 255, count: 1})
println("\n=== Cursor ===")
let rle4 : @rle.Rle[PixelRun] = @rle.Rle::Rle()
rle4.append(pixel(red, 10)) |> @debug.debug
rle4.append(pixel(green, 20)) |> @debug.debug
let cursor = rle4.cursor()
cursor.advance(10) |> println //=> true (moved past red)
print_position(cursor.position()) //=> Some(10)
print_pixel_opt(cursor.current_item()) //=> Some({color: 65280, count: 20}) (green)
}