#![doc = include_str!("../doc/quality-scales.md")]
use std::fmt;
use std::str::FromStr;
use crate::pixelmap_processor::PixelMapProcessor;
pub type Quality = ProcessingMode;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct IterationParams {
pub photo_width: usize,
pub grid_cell_size: usize,
pub neighborhood_radius: usize,
pub smooth_iterations: usize,
pub clean_max_dist: f32,
}
impl IterationParams {
pub fn apply(&self, processor: &mut PixelMapProcessor) {
processor.iterate(
self.photo_width,
self.grid_cell_size,
self.neighborhood_radius,
self.smooth_iterations,
self.clean_max_dist,
);
}
}
const fn step(photo_width: usize, clean_max_dist: f32) -> IterationParams {
IterationParams {
photo_width,
grid_cell_size: 5,
neighborhood_radius: 5,
smooth_iterations: 2,
clean_max_dist,
}
}
const LOW_STEPS: [IterationParams; 4] = [
step(400, 3.0),
step(400, 2.0),
step(400, 1.0),
step(400, 1.0),
];
const MEDIUM_STEPS: [IterationParams; 10] = [
step(400, 3.0),
step(400, 2.0),
step(400, 1.0),
step(400, 0.5),
step(400, 1.0),
step(400, 2.0),
step(400, 2.0),
step(800, 3.0),
step(800, 1.0),
step(800, 2.0),
];
const HIGH_STEPS: [IterationParams; 13] = [
step(400, 3.0),
step(400, 2.0),
step(400, 1.0),
step(400, 0.5),
step(400, 1.0),
step(400, 2.0),
step(400, 2.0),
step(800, 3.0),
step(800, 1.0),
step(800, 2.0),
step(1600, 1.0),
step(1600, 2.0),
step(1600, 2.0),
];
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProcessingMode {
Low,
Medium,
High,
}
impl ProcessingMode {
pub const ALL: [ProcessingMode; 3] = [
ProcessingMode::Low,
ProcessingMode::Medium,
ProcessingMode::High,
];
pub fn name(&self) -> &'static str {
match self {
ProcessingMode::Low => "low",
ProcessingMode::Medium => "medium",
ProcessingMode::High => "high",
}
}
pub fn photo_width(&self) -> usize {
match self {
ProcessingMode::Low => 400,
ProcessingMode::Medium => 800,
ProcessingMode::High => 1600,
}
}
pub fn steps(&self) -> &'static [IterationParams] {
match self {
ProcessingMode::Low => &LOW_STEPS,
ProcessingMode::Medium => &MEDIUM_STEPS,
ProcessingMode::High => &HIGH_STEPS,
}
}
pub fn run(&self, processor: &mut PixelMapProcessor) {
self.run_with_progress(processor, |_, _| {});
}
pub fn run_with_progress(
&self,
processor: &mut PixelMapProcessor,
mut on_step: impl FnMut(usize, usize),
) {
let steps = self.steps();
for (index, params) in steps.iter().enumerate() {
params.apply(processor);
on_step(index + 1, steps.len());
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseProcessingModeError {
input: String,
}
impl ParseProcessingModeError {
pub fn input(&self) -> &str {
&self.input
}
}
impl fmt::Display for ParseProcessingModeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"invalid processing mode: {}. Use 'low', 'medium', or 'high'.",
self.input
)
}
}
impl std::error::Error for ParseProcessingModeError {}
impl FromStr for ProcessingMode {
type Err = ParseProcessingModeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let name = s.trim().to_lowercase();
ProcessingMode::ALL
.into_iter()
.find(|mode| mode.name() == name)
.ok_or_else(|| ParseProcessingModeError {
input: s.to_owned(),
})
}
}
impl fmt::Display for ProcessingMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}