#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Config {
x_sample_count: usize,
y_sample_count: usize,
radius: f32,
}
impl Config {
pub const fn new(x_sample_count: usize, y_sample_count: usize, radius: f32) -> Self {
assert!(
x_sample_count > 0 && y_sample_count > 0,
"antialiasing sample counts must be positive"
);
assert!(
radius.is_finite()
&& radius > 0.0
&& (!x_sample_count.is_multiple_of(2) || radius >= 0.5 / x_sample_count as f32)
&& (!y_sample_count.is_multiple_of(2) || radius >= 0.5 / y_sample_count as f32),
"filter radius must be finite and positive, and at least 0.5 / \
samples on any axis with an even sample count, so every pixel has a \
sub-sample within the filter"
);
Self {
x_sample_count,
y_sample_count,
radius,
}
}
pub const fn radius(&self) -> f32 {
self.radius
}
pub const fn x_sample_count(&self) -> usize {
self.x_sample_count
}
pub const fn y_sample_count(&self) -> usize {
self.y_sample_count
}
}
impl Default for Config {
fn default() -> Self {
Self::new(2, 2, 0.5)
}
}
#[cfg(test)]
mod tests {
use super::Config;
#[test]
fn accepts_small_radius_on_odd_grid() {
assert_eq!(Config::new(3, 3, 0.1).radius(), 0.1);
}
#[test]
fn accepts_subpixel_radius_on_finer_grid() {
assert_eq!(Config::new(4, 4, 0.2).radius(), 0.2);
}
#[test]
fn defaults_match_explicit_construction() {
assert_eq!(Config::default(), Config::new(2, 2, 0.5));
}
#[test]
fn exposes_radius() {
assert_eq!(Config::new(2, 2, 0.5).radius(), 0.5);
}
#[test]
#[should_panic]
fn rejects_nonfinite_radius() {
let _ = Config::new(2, 2, f32::INFINITY);
}
#[test]
#[should_panic]
fn rejects_subpixel_radius() {
let _ = Config::new(2, 2, 0.1);
}
#[test]
#[should_panic]
fn rejects_zero_radius() {
let _ = Config::new(3, 3, 0.0);
}
}