use crate::pipe::Pattern;
pub struct FunctionPattern {
func: Box<dyn Fn(f64, f64) -> [u8; 3] + Send + Sync>,
}
impl FunctionPattern {
#[must_use]
pub fn new<F>(func: F) -> Self
where
F: Fn(f64, f64) -> [u8; 3] + Send + Sync + 'static,
{
Self {
func: Box::new(func),
}
}
}
impl Pattern for FunctionPattern {
fn fill_span(&self, y: i32, x0: i32, x1: i32, out: &mut [u8]) {
let mut off = 0usize;
for x in x0..=x1 {
let rgb = (self.func)(f64::from(x), f64::from(y));
out[off] = rgb[0];
out[off + 1] = rgb[1];
out[off + 2] = rgb[2];
off += 3;
}
}
fn is_static_color(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encodes_x_coordinate_in_output() {
#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "test passes integer x ∈ [10,13]; cast is exact"
)]
let p = FunctionPattern::new(|x, _y| {
let v = x as u8;
[v, v, v]
});
let mut out = vec![0u8; 4 * 3];
p.fill_span(0, 10, 13, &mut out);
for i in 0..4usize {
#[expect(
clippy::cast_possible_truncation,
reason = "i ∈ [0,4); 10 + i ≤ 13 fits in u8"
)]
let expected = (10 + i) as u8;
assert_eq!(out[i * 3], expected, "pixel {i} R");
assert_eq!(out[i * 3 + 1], expected, "pixel {i} G");
assert_eq!(out[i * 3 + 2], expected, "pixel {i} B");
}
}
#[test]
fn constant_function_fills_uniform_color() {
let p = FunctionPattern::new(|_, _| [128, 64, 32]);
let mut out = vec![0u8; 3 * 3];
p.fill_span(5, 0, 2, &mut out);
for i in 0..3usize {
assert_eq!(out[i * 3], 128, "pixel {i} R");
assert_eq!(out[i * 3 + 1], 64, "pixel {i} G");
assert_eq!(out[i * 3 + 2], 32, "pixel {i} B");
}
}
#[test]
fn encodes_y_coordinate_in_output() {
#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "test passes integer y = 42; cast is exact"
)]
let p = FunctionPattern::new(|_x, y| [0, 0, y as u8]);
let mut out = [0u8; 3];
p.fill_span(42, 0, 0, &mut out);
assert_eq!(out[2], 42, "blue channel should encode y");
}
#[test]
fn single_pixel_span_writes_three_bytes() {
let p = FunctionPattern::new(|_, _| [1, 2, 3]);
let mut out = [0u8; 3];
p.fill_span(0, 7, 7, &mut out);
assert_eq!(out, [1, 2, 3]);
}
}