csci5607/assignment-0/src/main.rs

112 lines
2.9 KiB
Rust
Raw Normal View History

2023-01-21 08:55:19 +00:00
use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
};
2023-01-21 08:03:11 +00:00
2023-01-21 08:55:19 +00:00
use anyhow::{bail, Context, Result};
2023-01-21 08:22:22 +00:00
use clap::{Parser, ValueEnum};
2023-01-21 08:55:19 +00:00
use lazy_static::lazy_static;
use regex::Regex;
2023-01-21 08:22:22 +00:00
use turbulence::generate_turbulence;
2023-01-21 08:03:11 +00:00
use crate::value_noise::generate_noise;
2023-01-21 08:55:19 +00:00
mod image;
2023-01-21 08:22:22 +00:00
mod turbulence;
2023-01-21 08:03:11 +00:00
mod value_noise;
mod vec2;
2023-01-21 08:55:19 +00:00
/// Noise image generator.
2023-01-21 08:03:11 +00:00
#[derive(Parser)]
2023-01-21 08:55:19 +00:00
#[clap(author, version, about, long_about = None)]
2023-01-21 08:03:11 +00:00
struct Opt {
2023-01-21 08:55:19 +00:00
/// 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.
2023-01-21 08:22:22 +00:00
#[clap(long = "algorithm", default_value = "turbulence")]
algorithm: Algorithm,
2023-01-21 08:55:19 +00:00
/// The output image path.
2023-01-21 08:22:22 +00:00
#[clap(short = 'o', long = "out", default_value = "out.ppm")]
output_path: PathBuf,
}
#[derive(ValueEnum, Clone)]
enum Algorithm {
2023-01-21 08:55:19 +00:00
/// Generates noise, which is linearly interpolated randomness.
2023-01-21 08:22:22 +00:00
Noise,
2023-01-21 08:55:19 +00:00
/// Fractal sum made with signed noise, used to resemble smoke.
2023-01-21 08:22:22 +00:00
Turbulence,
2023-01-21 08:03:11 +00:00
}
fn main() -> Result<()> {
2023-01-21 08:22:22 +00:00
let opt = Opt::parse();
2023-01-21 08:03:11 +00:00
2023-01-21 08:55:19 +00:00
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
2023-01-21 08:22:22 +00:00
let rng = rand::thread_rng();
2023-01-21 08:03:11 +00:00
2023-01-21 08:55:19 +00:00
// Pick the algorithm to use based on the option passed via command line
2023-01-21 08:22:22 +00:00
let ppm = match opt.algorithm {
2023-01-21 08:55:19 +00:00
Algorithm::Noise => generate_noise(width, height, rng),
Algorithm::Turbulence => generate_turbulence(width, height, rng),
2023-01-21 08:22:22 +00:00
};
2023-01-21 08:03:11 +00:00
2023-01-21 08:55:19 +00:00
// Write the PPM image to the output file specified
2023-01-21 08:22:22 +00:00
{
2023-01-21 08:55:19 +00:00
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)
})?;
2023-01-21 08:22:22 +00:00
}
2023-01-21 08:03:11 +00:00
2023-01-21 08:22:22 +00:00
Ok(())
2023-01-21 08:03:11 +00:00
}
2023-01-21 08:55:19 +00:00
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))
}