commit 5935d51e08668a0d6407541a9873c769f006e097 Author: iceyrazor Date: Fri Jul 4 05:31:27 2025 -0500 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a081228 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,206 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "c_vec" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8" + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "fireworks" +version = "0.1.0" +dependencies = [ + "rand", + "sdl2", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.174" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "sdl2" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b498da7d14d1ad6c839729bd4ad6fc11d90a57583605f3b4df2cd709a9cd380" +dependencies = [ + "bitflags 1.3.2", + "c_vec", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "951deab27af08ed9c6068b7b0d05a93c91f0a8eb16b6b816a5e73452a43521d3" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.9.1", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d53a8a2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "fireworks" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.9.1" +sdl2 = { version = "0.37.0", features = ["gfx"] } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d54d7cc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 iceyrazor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b492d2b --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# fireworks in sdl2 rust + +![firework gif](./fireworks.gif?raw=true) + +followed this video [Coding Challenge #27: Fireworks](https://youtu.be/CKeyIbT3vXI) +but instead of p5 javascript. i did it in sdl2 rust + +its 4th of july so i decided to upload it. why not :p + +``no clue if this compiles on windows`` + +## requirements + +- sdl2 +- sdl2_gfx diff --git a/fireworks.gif b/fireworks.gif new file mode 100644 index 0000000..03d52bf Binary files /dev/null and b/fireworks.gif differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b77bd93 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,372 @@ +// followed this video [Coding Challenge #27: Fireworks](https://youtu.be/CKeyIbT3vXI) +// but instead of p5 javascript. i did it in sdl2 rust +extern crate sdl2; + +use sdl2::{gfx::primitives::DrawRenderer, rect::Rect}; +use sdl2::pixels::Color; +use sdl2::event::Event; +use sdl2::keyboard::Keycode; +use sdl2::render::WindowCanvas; +use std::time::Duration; +use rand::{rng, Rng}; + + +//HSV to rgv. +//i did not make this +//only translated it from my c version i found from a stranger +struct HSV { + h: f64, + s: f64, + v: f64, +} +impl HSV{ + fn new(h: f64, s: f64, v: f64) -> Self{ + Self{ + h: h, + s: s, + v: v, + } + } +} + +#[derive(Clone,Copy)] +struct IRGB { + r: f64, + g: f64, + b: f64, +} +impl IRGB { + fn new(r: f64, g: f64, b: f64) -> Self{ + Self{ + r: r, + g: g, + b: b, + } + } + + fn hsvtorgb(&mut self, hsv: &mut HSV){ + let mut r = 0.0; + let mut g = 0.0; + let mut b = 0.0; + + if hsv.s == 0.0 { + r = hsv.v; + g = hsv.v; + b = hsv.v; + } else { + let mut i: f64 = 0.0; + let mut f: f64 = 0.0; + let mut p: f64 = 0.0; + let mut q: f64 = 0.0; + let mut t: f64 = 0.0; + + if hsv.h == 360.0 { + hsv.h = 0.0; + } else { + hsv.h = hsv.h / 60.0; + + i = hsv.h.trunc(); + f = hsv.h - i; + + p = hsv.v * (1.0 - hsv.s); + q = hsv.v * (1.0 - (hsv.s * f)); + t = hsv.v * (1.0 - (hsv.s * (1.0 - f))); + + match i { + 0.0=>{ + r = hsv.v; + g = t; + b = p; + }, + 1.0=>{ + r = q; + g = hsv.v; + b = p; + }, + 2.0=>{ + r = p; + g = hsv.v; + b = t; + }, + 3.0=>{ + r = p; + g = q; + b = hsv.v; + }, + 4.0=>{ + r = t; + g = p; + b = hsv.v; + }, + _=>{ + r = hsv.v; + g = p; + b = q; + }, + } + } + } + + self.r = r * 255.0; + self.g = g * 255.0; + self.b = b * 255.0; + } +} + +fn magnitude(x: f64,y: f64) -> f64 { + ((x).powi(2) + (y).powi(2)).sqrt() +} + +struct PointG{ + x: f64, + y: f64, +} +impl PointG{ + fn new(x: f64, y: f64) -> Self { + Self{ + x: x, + y: y, + } + } + + fn add(&mut self, addvec: &PointG){ + self.x = self.x + addvec.x; + self.y = self.y + addvec.y; + } + + fn mul(&mut self, addvec: &PointG){ + self.x = self.x * addvec.x; + self.y = self.y * addvec.y; + } + + fn set_mag(&mut self, mag: &f64){ + let getmag: f64 = magnitude(self.x, self.y); + self.x = (self.x / getmag) * mag; + self.y = (self.y / getmag) * mag; + } +} + +struct Gvars{ + mouse: PointG, + screen_size: PointG, +} +impl Gvars{ + fn new(sw: f64, sh: f64) -> Self{ + Self { + mouse: PointG::new(0.0,0.0), + screen_size: PointG::new(sw,sh), + } + } +} + +// Particle +struct Particle{ + pos: PointG, + vel: PointG, + acc: PointG, + firework: bool, + lifespan: i16, + hue: IRGB, +} + +impl Particle{ + fn new(x: f64, y: f64, hu: IRGB, firework: bool) -> Self{ + let mut initvel=PointG::new(0.0,rng().random_range(-18.0..-9.0)); + if firework==true { + initvel=PointG::new(rng().random_range(-1.0..1.0),rng().random_range(-1.0..1.0)); + initvel.set_mag(&rng().random_range(1.0..10.0)); + } + Self { + pos: PointG::new(x,y), + vel: initvel, + acc: PointG::new(0.0,0.0), + firework: firework, + lifespan: 255, + hue: hu, + } + } + + fn apply_force(&mut self, force: &PointG){ + self.acc.add(&force); + } + + fn done(&mut self) -> bool{ + if self.lifespan <= 0 { + return true + } + false + } + + fn update(&mut self){ + if self.firework { + self.vel.mul(&PointG::new(0.9,0.9)); + self.lifespan = self.lifespan - 4; + + if self.lifespan <= 0 { + self.lifespan = 0; + } + } + self.vel.add(&self.acc); + self.pos.add(&self.vel); + + self.acc.x = 0.0; + self.acc.y = 0.0; + } + + fn show(&mut self, canvas: &mut WindowCanvas, gvars: &mut Gvars){ + if self.firework { + canvas.filled_circle(self.pos.x as i16, self.pos.y as i16, 1, Color::RGBA(self.hue.r as u8,self.hue.g as u8,self.hue.b as u8,self.lifespan as u8)).err(); + } else { + canvas.filled_circle(self.pos.x as i16, self.pos.y as i16, 3, Color::RGBA(self.hue.r as u8,self.hue.g as u8,self.hue.b as u8,255)).err(); + } + } +} + +// Firework +struct Firework{ + firework: Particle, + exploded: bool, + particles: Vec, + hue: IRGB, +} + +impl Firework{ + fn new(x: f64, y: f64) -> Self{ + let mut hu = IRGB::new(0.0,0.0,0.0); + hu.hsvtorgb(&mut HSV::new(rng().random_range(0.0..360.0),1.0,1.0)); + Self { + firework: Particle::new(x,y,hu,false), + exploded: false, + particles: Vec::new(), + hue: hu, + } + } + + fn done(&mut self) -> bool{ + if self.exploded && self.particles.len() == 0{ + return true + } + false + } + + fn update(&mut self){ + if !self.exploded { + self.firework.apply_force(&PointG::new(0.0,0.2)); + self.firework.update(); + + if self.firework.vel.y >= 0.0 { + self.exploded=true; + self.explode(); + } + } + + for i in (0..self.particles.len()).rev() { + self.particles[i].apply_force(&PointG::new(0.0,0.2)); + self.particles[i].update(); + if self.particles[i].done() { + self.particles.remove(i); + } + } + } + + fn explode(&mut self){ + for _ in 0..100{ + let p = Particle::new(self.firework.pos.x,self.firework.pos.y,self.hue,true); + self.particles.push(p); + } + } + + fn show(&mut self, canvas: &mut WindowCanvas, gvars: &mut Gvars){ + if !self.exploded { + self.firework.show(canvas,gvars); + } + + for obj in &mut self.particles{ + obj.show(canvas,gvars); + } + } +} + + +// Canvas Draw +fn draw(canvas: &mut WindowCanvas, obj_arr: &mut Vec, gvars: &mut Gvars){ + canvas.set_draw_color(Color::RGBA(0,0,0,50)); + + //swap these lines out if screen is glitchy + canvas.fill_rect(Rect::new(0,0,800,800)).err(); + //canvas.clear(); + + + if rng().random_range(0.0..1.0) < 0.08 { + let obj = Firework::new(rng().random_range(0.0..gvars.screen_size.x as f64), gvars.screen_size.y as f64); + obj_arr.push(obj); + } + + for i in (0..obj_arr.len()).rev() { + obj_arr[i].update(); + obj_arr[i].show(canvas,gvars); + if obj_arr[i].done() { + obj_arr.remove(i); + } + } + + canvas.present(); +} + + + +fn main() { + let sdl2_context = sdl2::init().unwrap(); + let video_subsystem = sdl2_context.video().unwrap(); + + + let width: u32=800; + let height: u32=800; + + let window = video_subsystem.window("fireworks", width, height) + .position_centered() + .build() + .unwrap(); + + let mut canvas = window.into_canvas().build().unwrap(); + canvas.default_pixel_format(); + canvas.set_blend_mode(sdl2::render::BlendMode::Blend); + + let mut gvars = Gvars::new(width as f64, height as f64); + + let mut obj_arr: Vec = Vec::new(); + + + let obj = Firework::new(rng().random_range(0.0..width as f64), height as f64); + obj_arr.push(obj); + + + canvas.set_draw_color(Color::RGB(0,0,0)); + canvas.clear(); + + let mut event_pump = sdl2_context.event_pump().unwrap(); + draw(&mut canvas, &mut obj_arr, &mut gvars); + + 'running: loop { + draw(&mut canvas, &mut obj_arr, &mut gvars); + let state = event_pump.mouse_state(); + gvars.mouse.x=state.x() as f64; + gvars.mouse.y=state.y() as f64; + //draw(&mut canvas, &mut obj, &state.x(), &state.y()); + for event in event_pump.poll_iter() { + match event { + Event::Quit {..} | + Event::KeyDown { keycode: Some(Keycode::Escape), .. } => { + break 'running + }, + _ => {} + } + } + + ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)); + } +} + +// *~challenges +// shaped fireworks such as a heart +// variable amount of particles