66 clippy:: cognitive_complexity
77) ]
88
9- use std:: fs;
9+ use crate :: eval:: EvalContext ;
10+ use crate :: parser:: Token ;
1011use clap:: Parser ;
1112use console:: { style, Emoji } ;
1213use gif:: { Encoder , Repeat } ;
@@ -16,15 +17,15 @@ use image::{
1617 ImageFormat , Pixel ,
1718} ;
1819use indicatif:: { ProgressBar , ProgressStyle } ;
20+ use rand:: prelude:: StdRng ;
21+ use rand:: { RngCore , SeedableRng } ;
1922use rayon:: prelude:: * ;
23+ use std:: fs;
2024use std:: io:: { BufReader , BufWriter , Read } ;
2125use std:: path:: { Path , PathBuf } ;
2226use std:: sync:: Mutex ;
2327use std:: time:: Duration ;
2428
25- use crate :: eval:: EvalContext ;
26- use crate :: parser:: Token ;
27-
2829mod bounds;
2930mod eval;
3031mod parser;
@@ -54,6 +55,10 @@ struct Args {
5455 /// Enable verbose output
5556 #[ arg( short, long, default_value = "false" ) ]
5657 verbose : bool ,
58+
59+ /// Seed for the random number generator (Default: Current time)
60+ #[ arg( short, long) ]
61+ seed : Option < u64 > ,
5762}
5863
5964static LOOKING_GLASS : Emoji < ' _ , ' _ > = Emoji ( "🔍 " , "" ) ;
@@ -62,9 +67,10 @@ static IMAGE: Emoji<'_, '_> = Emoji("🗃️ ", "");
6267static ERROR : Emoji < ' _ , ' _ > = Emoji ( "❌ " , "" ) ;
6368static OK : Emoji < ' _ , ' _ > = Emoji ( "✅ " , "" ) ;
6469static EYE : Emoji < ' _ , ' _ > = Emoji ( "👁️ " , "" ) ;
70+ static SEED : Emoji < ' _ , ' _ > = Emoji ( "🌱 " , "" ) ;
6571
6672fn main ( ) -> anyhow:: Result < ( ) > {
67- let args = Args :: parse ( ) ;
73+ let mut args = Args :: parse ( ) ;
6874 // If we want to pass the arguments to a function, we need to clone them
6975 if args. input . starts_with ( "http" ) {
7076 // use the writer
@@ -81,6 +87,17 @@ fn main() -> anyhow::Result<()> {
8187 ) ;
8288 }
8389
90+ // Determine which RNG to use based on the provided seed
91+ let seed = get_random_seed ( & args) ;
92+ args. seed = Some ( seed) ;
93+ let mut rng: Box < dyn RngCore > = Box :: new ( StdRng :: seed_from_u64 ( seed) ) ;
94+
95+ println ! (
96+ "{} Using Seed: {}" ,
97+ SEED ,
98+ style( seed) . bold( ) . cyan( )
99+ ) ;
100+
84101 println ! (
85102 "{} Parsing {} Expression{}..." ,
86103 LOOKING_GLASS ,
@@ -124,7 +141,7 @@ fn main() -> anyhow::Result<()> {
124141 parsed. push ( ( e. to_string ( ) , tokens) ) ;
125142 }
126143
127- handle_image ( & args, & parsed) ?;
144+ handle_image ( & args, & parsed, & mut rng ) ?;
128145 Ok ( ( ) )
129146}
130147
@@ -138,6 +155,7 @@ fn download_image(url: &str) -> anyhow::Result<Vec<u8>> {
138155fn handle_image (
139156 args : & Args ,
140157 parsed : & [ ( String , Vec < Token > ) ] ,
158+ rand : & mut Box < dyn RngCore > ,
141159) -> anyhow:: Result < ( ) , anyhow:: Error > {
142160 let img = match & args. input {
143161 file if file. starts_with ( "http" ) => download_image ( & args. input ) ?,
@@ -195,26 +213,23 @@ fn handle_image(
195213
196214 spinner. set_message ( format ! ( "{} Processing mode: {}" , IMAGE , style( "PNG" ) . bold( ) . cyan( ) ) ) ;
197215
198- let out = process ( img, parsed, args. no_state ) ?;
216+ let out = process ( img, parsed, args, rand ) ?;
199217 out. save_with_format ( output. clone ( ) , format) ?;
200218 }
201219 ImageFormat :: Jpeg => {
202220 let img = image:: load_from_memory ( & img) ?;
203221
204222 spinner. set_message ( format ! ( "{} Processing mode: {}" , IMAGE , style( "JPEG" ) . bold( ) . cyan( ) ) ) ;
205223
206- let out = process ( img, parsed, args. no_state ) ?;
224+ let out = process ( img, parsed, args, rand ) ?;
207225 out. save_with_format ( output. clone ( ) , format) ?;
208226 }
209227 ImageFormat :: Gif => {
210-
211228 let mut reader = std:: io:: Cursor :: new ( img) ;
212229 let decoder = GifDecoder :: new ( & mut reader) ?;
213230 let [ w, h] = [ decoder. dimensions ( ) . 0 , decoder. dimensions ( ) . 1 ] ;
214231 let frames = decoder. into_frames ( ) . collect_frames ( ) ?;
215232
216-
217-
218233 let output = std:: fs:: File :: create ( output. clone ( ) ) ?;
219234 let mut img_writer = BufWriter :: new ( output) ;
220235 let mut encoder = Encoder :: new ( & mut img_writer, w as u16 , h as u16 , & [ ] ) ?;
@@ -224,12 +239,15 @@ fn handle_image(
224239
225240 spinner. set_message ( format ! ( "{} Processing mode: {} with {} frames" , IMAGE , style( "GIF" ) . bold( ) . cyan( ) , style( frames. len( ) ) . bold( ) . cyan( ) ) ) ;
226241
242+ let seed = args. seed . unwrap ( ) ;
227243 ( 0 ..frames. len ( ) ) . into_par_iter ( ) . for_each ( |i| {
244+ let mut rng: Box < dyn RngCore > = Box :: new ( StdRng :: seed_from_u64 ( seed) ) ;
245+
228246 let frame = frames. get ( i) . expect ( "Failed to get frame" ) . to_owned ( ) ;
229247 let delay = frame. delay ( ) . numer_denom_ms ( ) . 0 as u16 ;
230248 let img = frame. into_buffer ( ) ;
231249 let out =
232- process ( img. into ( ) , parsed, args. no_state ) . expect ( "Failed to process frame" ) ;
250+ process ( img. into ( ) , parsed, args, & mut rng ) . expect ( "Failed to process frame" ) ;
233251 let mut bytes = out. as_bytes ( ) . to_vec ( ) ;
234252
235253 let mut new_frame = gif:: Frame :: from_rgba_speed ( w as u16 , h as u16 , & mut bytes, 10 ) ;
@@ -278,7 +296,8 @@ fn handle_image(
278296fn process (
279297 mut img : DynamicImage ,
280298 expressions : & [ ( String , Vec < Token > ) ] ,
281- no_state : bool ,
299+ args : & Args ,
300+ rand : & mut Box < dyn RngCore > , // Accept boxed RNG here
282301) -> anyhow:: Result < DynamicImage > {
283302 let mut output_image = DynamicImage :: new ( img. width ( ) , img. height ( ) , img. color ( ) ) ;
284303
@@ -309,10 +328,10 @@ fn process(
309328 rgba : colors. 0 ,
310329 saved_rgb : [ sr, sg, sb] ,
311330 position : ( x, y) ,
312- ignore_state : no_state,
331+ ignore_state : args . no_state ,
313332 } ,
314333 & img,
315- rand:: thread_rng ( ) ,
334+ rand,
316335 )
317336 . expect ( "Failed to evaluate" ) ;
318337
@@ -332,4 +351,15 @@ fn process(
332351
333352fn strip_windows_prefix ( path : & Path ) -> PathBuf {
334353 path. to_str ( ) . and_then ( |s| s. strip_prefix ( r"\\?\" ) ) . map_or_else ( || path. to_path_buf ( ) , PathBuf :: from)
354+ }
355+
356+ fn get_random_seed ( args : & Args ) -> u64 {
357+ if args. seed . is_none ( ) {
358+ return std:: time:: SystemTime :: now ( )
359+ . duration_since ( std:: time:: UNIX_EPOCH )
360+ . expect ( "Time went backwards" )
361+ . as_nanos ( ) as u64 ;
362+ }
363+
364+ args. seed . unwrap ( )
335365}
0 commit comments