Skip to content

Commit

Permalink
Added the ability to set the seed of the RNG
Browse files Browse the repository at this point in the history
  • Loading branch information
Toyz committed Jan 1, 2025
1 parent 0a7b621 commit ab8cd7d
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "glitch"
version = "0.3.1"
version = "0.4.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
16 changes: 7 additions & 9 deletions src/eval.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use image::{DynamicImage, GenericImageView, Rgba};
use rand::prelude::ThreadRng;
use rand::Rng;
use rand::{Rng, RngCore};
use std::collections::HashMap;

use crate::parser::Token;
Expand Down Expand Up @@ -57,7 +56,7 @@ pub struct EvalContext {
pub fn eval(
ctx: EvalContext,
input: &DynamicImage,
mut rng: ThreadRng,
rng: &mut Box<dyn RngCore>,
) -> Result<Rgba<u8>, String> {
let EvalContext {
tokens,
Expand Down Expand Up @@ -261,7 +260,7 @@ pub fn eval(
let v_r = if let Some(v) = v_r {
*v
} else {
let colors = gen_random_position(neg as i32, num as i32, &mut rng);
let colors = gen_random_position(neg as i32, num as i32, rng);
let rgb = rgb_from_colors(&colors);
if !ignore_state {
saved.v_r.get_or_insert(HashMap::new()).insert(num, rgb);
Expand Down Expand Up @@ -304,8 +303,7 @@ pub fn eval(
let yu = three_rule(y, height);
stack.push(RgbSum::new(yu, yu, yu));
}
/*
'r' => {
/*'r' => {
let v_r = match saved.v_r {
Some(v_r) => v_r,
None => {
Expand All @@ -326,7 +324,7 @@ pub fn eval(
let v_t = match saved.v_t {
Some(v_t) => v_t,
None => {
let colors = gen_random_position(-2, 2, &mut rng);
let colors = gen_random_position(-2, 2, rng);

let rgb = rgb_from_colors(&colors);
if !ignore_state {
Expand All @@ -342,7 +340,7 @@ pub fn eval(
let v_g = match saved.v_g {
Some(v_g) => v_g,
None => {
let colors = gen_random_position(0i32, width as i32, &mut rng);
let colors = gen_random_position(0i32, width as i32, rng);

let rgb = rgb_from_colors(&colors);
if !ignore_state {
Expand Down Expand Up @@ -594,7 +592,7 @@ fn min(vals: [u8; 8]) -> u8 {
vals.iter().cloned().min().unwrap_or_default()
}

fn gen_random_position(min: i32, max: i32, rng: &mut ThreadRng) -> [(i32, i32); 3] {
fn gen_random_position(min: i32, max: i32, rng: &mut Box<dyn RngCore>) -> [(i32, i32); 3] {
let mut positions = [(0, 0); 3];
for i in positions.iter_mut() {
i.0 = rng.gen_range(min..=max);
Expand Down
60 changes: 45 additions & 15 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
clippy::cognitive_complexity
)]

use std::fs;
use crate::eval::EvalContext;
use crate::parser::Token;
use clap::Parser;
use console::{style, Emoji};
use gif::{Encoder, Repeat};
Expand All @@ -16,15 +17,15 @@ use image::{
ImageFormat, Pixel,
};
use indicatif::{ProgressBar, ProgressStyle};
use rand::prelude::StdRng;
use rand::{RngCore, SeedableRng};
use rayon::prelude::*;
use std::fs;
use std::io::{BufReader, BufWriter, Read};
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use std::time::Duration;

use crate::eval::EvalContext;
use crate::parser::Token;

mod bounds;
mod eval;
mod parser;
Expand Down Expand Up @@ -54,6 +55,10 @@ struct Args {
/// Enable verbose output
#[arg(short, long, default_value = "false")]
verbose: bool,

/// Seed for the random number generator (Default: Current time)
#[arg(short, long)]
seed: Option<u64>,
}

static LOOKING_GLASS: Emoji<'_, '_> = Emoji("🔍 ", "");
Expand All @@ -62,9 +67,10 @@ static IMAGE: Emoji<'_, '_> = Emoji("🗃️ ", "");
static ERROR: Emoji<'_, '_> = Emoji("❌ ", "");
static OK: Emoji<'_, '_> = Emoji("✅ ", "");
static EYE: Emoji<'_, '_> = Emoji("👁️ ", "");
static SEED: Emoji<'_, '_> = Emoji("🌱 ", "");

fn main() -> anyhow::Result<()> {
let args = Args::parse();
let mut args = Args::parse();
// If we want to pass the arguments to a function, we need to clone them
if args.input.starts_with("http") {
// use the writer
Expand All @@ -81,6 +87,17 @@ fn main() -> anyhow::Result<()> {
);
}

// Determine which RNG to use based on the provided seed
let seed = get_random_seed(&args);
args.seed = Some(seed);
let mut rng: Box<dyn RngCore> = Box::new(StdRng::seed_from_u64(seed));

println!(
"{} Using Seed: {}",
SEED,
style(seed).bold().cyan()
);

println!(
"{} Parsing {} Expression{}...",
LOOKING_GLASS,
Expand Down Expand Up @@ -124,7 +141,7 @@ fn main() -> anyhow::Result<()> {
parsed.push((e.to_string(), tokens));
}

handle_image(&args, &parsed)?;
handle_image(&args, &parsed, &mut rng)?;
Ok(())
}

Expand All @@ -138,6 +155,7 @@ fn download_image(url: &str) -> anyhow::Result<Vec<u8>> {
fn handle_image(
args: &Args,
parsed: &[(String, Vec<Token>)],
rand: &mut Box<dyn RngCore>,
) -> anyhow::Result<(), anyhow::Error> {
let img = match &args.input {
file if file.starts_with("http") => download_image(&args.input)?,
Expand Down Expand Up @@ -195,26 +213,23 @@ fn handle_image(

spinner.set_message(format!("{} Processing mode: 󰸭 {}", IMAGE, style("PNG").bold().cyan()));

let out = process(img, parsed, args.no_state)?;
let out = process(img, parsed, args, rand)?;
out.save_with_format(output.clone(), format)?;
}
ImageFormat::Jpeg => {
let img = image::load_from_memory(&img)?;

spinner.set_message(format!("{} Processing mode: 󰸭 {}", IMAGE, style("JPEG").bold().cyan()));

let out = process(img, parsed, args.no_state)?;
let out = process(img, parsed, args, rand)?;
out.save_with_format(output.clone(), format)?;
}
ImageFormat::Gif => {

let mut reader = std::io::Cursor::new(img);
let decoder = GifDecoder::new(&mut reader)?;
let [w, h] = [decoder.dimensions().0, decoder.dimensions().1];
let frames = decoder.into_frames().collect_frames()?;



let output = std::fs::File::create(output.clone())?;
let mut img_writer = BufWriter::new(output);
let mut encoder = Encoder::new(&mut img_writer, w as u16, h as u16, &[])?;
Expand All @@ -224,12 +239,15 @@ fn handle_image(

spinner.set_message(format!("{} Processing mode: 󰸭 {} with {} frames", IMAGE, style("GIF").bold().cyan(), style(frames.len()).bold().cyan()));

let seed = args.seed.unwrap();
(0..frames.len()).into_par_iter().for_each(|i| {
let mut rng: Box<dyn RngCore> = Box::new(StdRng::seed_from_u64(seed));

let frame = frames.get(i).expect("Failed to get frame").to_owned();
let delay = frame.delay().numer_denom_ms().0 as u16;
let img = frame.into_buffer();
let out =
process(img.into(), parsed, args.no_state).expect("Failed to process frame");
process(img.into(), parsed, args, &mut rng).expect("Failed to process frame");
let mut bytes = out.as_bytes().to_vec();

let mut new_frame = gif::Frame::from_rgba_speed(w as u16, h as u16, &mut bytes, 10);
Expand Down Expand Up @@ -278,7 +296,8 @@ fn handle_image(
fn process(
mut img: DynamicImage,
expressions: &[(String, Vec<Token>)],
no_state: bool,
args: &Args,
rand: &mut Box<dyn RngCore>, // Accept boxed RNG here
) -> anyhow::Result<DynamicImage> {
let mut output_image = DynamicImage::new(img.width(), img.height(), img.color());

Expand Down Expand Up @@ -309,10 +328,10 @@ fn process(
rgba: colors.0,
saved_rgb: [sr, sg, sb],
position: (x, y),
ignore_state: no_state,
ignore_state: args.no_state,
},
&img,
rand::thread_rng(),
rand,
)
.expect("Failed to evaluate");

Expand All @@ -332,4 +351,15 @@ fn process(

fn strip_windows_prefix(path: &Path) -> PathBuf {
path.to_str().and_then(|s| s.strip_prefix(r"\\?\")).map_or_else(|| path.to_path_buf(), PathBuf::from)
}

fn get_random_seed(args: &Args) -> u64 {
if args.seed.is_none() {
return std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards")
.as_nanos() as u64;
}

args.seed.unwrap()
}

0 comments on commit ab8cd7d

Please sign in to comment.