csci5607/assignment-0/src/main.rs
2023-02-20 22:05:32 -06:00

111 lines
2.9 KiB
Rust

use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
};
use anyhow::{bail, Context, Result};
use clap::{Parser, ValueEnum};
use lazy_static::lazy_static;
use regex::Regex;
use turbulence::generate_turbulence;
use crate::value_noise::generate_noise;
mod image;
mod turbulence;
mod value_noise;
mod vec2;
/// Noise image generator.
#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Opt {
/// Path to the input file to use.
///
/// The input file should follow this format:
///
/// imsize [width] [height]
///
/// Where `imsize' is a keyword, and `width' and `height' are integer values
/// denoting the desired size of the image to be generated.
#[clap()]
input_path: PathBuf,
/// The algorithm to use for pattern generation.
#[clap(long = "algorithm", default_value = "turbulence")]
algorithm: Algorithm,
/// The output image path.
#[clap(short = 'o', long = "out", default_value = "out.ppm")]
output_path: PathBuf,
}
#[derive(ValueEnum, Clone)]
enum Algorithm {
/// Generates noise, which is linearly interpolated randomness.
Noise,
/// Fractal sum made with signed noise, used to resemble smoke.
Turbulence,
}
fn main() -> Result<()> {
let opt = Opt::parse();
let (width, height) = parse_input_file(&opt.input_path)?;
// We will use the default thread-local rng. Future work could include
// allowing the rng to be seedable from the command line
let rng = rand::thread_rng();
// Pick the algorithm to use based on the option passed via command line
let ppm = match opt.algorithm {
Algorithm::Noise => generate_noise(width, height, rng),
Algorithm::Turbulence => generate_turbulence(width, height, rng),
};
// Write the PPM image to the output file specified
{
let file = File::create(&opt.output_path).with_context(|| {
format!("Could not create file {:?}.", opt.output_path)
})?;
ppm.write(file).with_context(|| {
format!("Could not write to file {:?}.", opt.output_path)
})?;
}
Ok(())
}
lazy_static! {
static ref INPUT_PATTERN: Regex =
Regex::new(r"imsize\s+(?P<width>\d+)\s+(?P<height>\d+)")
.expect("Failed to compile regex.");
}
/// Parse the input file, returning the target image dimensions
fn parse_input_file(path: impl AsRef<Path>) -> Result<(usize, usize)> {
let path = path.as_ref();
// Read the contents from the file
let contents = {
let mut contents = String::new();
let mut file = File::open(path)?;
file.read_to_string(&mut contents)?;
contents
};
// Parse the contents using regex
let matches = match INPUT_PATTERN.captures(&contents) {
Some(v) => v,
None => bail!("Invalid input file syntax."),
};
// Extract the parsed captures and convert to int
let width = matches.name("width").unwrap().as_str().parse()?;
let height = matches.name("height").unwrap().as_str().parse()?;
Ok((width, height))
}