From 4a8e947ac5b51a3ca9bec81d6d0b35d82944531a Mon Sep 17 00:00:00 2001 From: Juanperias Date: Fri, 1 Nov 2024 15:52:10 -0400 Subject: [PATCH] feat: add EventLoopBuilder --- Cargo.lock | 2 +- Cargo.toml | 2 +- examples/event_loop_example/Cargo.lock | 4 +- examples/event_loop_example/Cargo.toml | 2 +- examples/event_loop_example/src/main.rs | 4 +- examples/raqote_example/Cargo.lock | 2 - examples/raqote_example/Cargo.toml | 2 +- examples/raqote_example/src/main.rs | 140 +++++++++++++----------- src/event_loop/mod.rs | 101 ++++++++++++++++- 9 files changed, 184 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 26bb85f..084c600 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,4 +4,4 @@ version = 3 [[package]] name = "motion" -version = "0.1.4" +version = "0.1.5" diff --git a/Cargo.toml b/Cargo.toml index 742d28a..38dde1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "motion" -version = "0.1.4" +version = "0.1.5" edition = "2021" authors = ["Juanperias"] description = "A bare metal physics engine." diff --git a/examples/event_loop_example/Cargo.lock b/examples/event_loop_example/Cargo.lock index 1c92a4b..1f2460e 100644 --- a/examples/event_loop_example/Cargo.lock +++ b/examples/event_loop_example/Cargo.lock @@ -11,6 +11,4 @@ dependencies = [ [[package]] name = "motion" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ad54e1f417c3110eb7f8cd6dd5b96e3c87136b7441b96fa7c796e30fcdb735" +version = "0.1.4" diff --git a/examples/event_loop_example/Cargo.toml b/examples/event_loop_example/Cargo.toml index 26a01cd..e8dd40e 100644 --- a/examples/event_loop_example/Cargo.toml +++ b/examples/event_loop_example/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] -motion = "*" +motion = { path = "*" } diff --git a/examples/event_loop_example/src/main.rs b/examples/event_loop_example/src/main.rs index a893a41..5949e5b 100644 --- a/examples/event_loop_example/src/main.rs +++ b/examples/event_loop_example/src/main.rs @@ -1,6 +1,6 @@ use std::{thread, time::Duration}; -use motion::event_loop::{EventLoop, EventLoopConfig}; +use motion::event_loop::EventLoopBuilder; // The definition of this function depends on the context in which motion is used fn sleep(duration: Duration) { @@ -8,7 +8,7 @@ fn sleep(duration: Duration) { } fn main() { - let el = EventLoop::new(EventLoopConfig { fps: 1 }); + let el = EventLoopBuilder::new().fps(1).build(); el.start(|_config| println!("Hello! in the event loop"), sleep); } diff --git a/examples/raqote_example/Cargo.lock b/examples/raqote_example/Cargo.lock index 01c0a9c..cb1d003 100644 --- a/examples/raqote_example/Cargo.lock +++ b/examples/raqote_example/Cargo.lock @@ -303,8 +303,6 @@ dependencies = [ [[package]] name = "motion" version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4d6a2daf29878349215175e013ded9ab4c0ff332b72865c8ed115b66416f16d" [[package]] name = "nix" diff --git a/examples/raqote_example/Cargo.toml b/examples/raqote_example/Cargo.toml index 44cbfd0..fb4fe98 100644 --- a/examples/raqote_example/Cargo.toml +++ b/examples/raqote_example/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" [dependencies] minifb = { version = "0.27.0", features = ["wayland"] } -motion = "0.1.4" +motion = { path = "*" } # by default raqote asks for the fontconfig.pc lib installed but in this example it is not necessary. raqote = { version = "0.8.5", features = [ "pathfinder_geometry", diff --git a/examples/raqote_example/src/main.rs b/examples/raqote_example/src/main.rs index 5a53837..97ffc8f 100644 --- a/examples/raqote_example/src/main.rs +++ b/examples/raqote_example/src/main.rs @@ -1,11 +1,9 @@ -use std::{ - f32::consts::PI, - time::{Duration, Instant}, -}; +use std::{f32::consts::PI, thread, time::Duration}; use minifb::{Window, WindowOptions}; use motion::{ collision::{shape::Shape, Collision2d}, + event_loop::EventLoopBuilder, forces::r#move::{move2d, Direction2d}, formulas::elastic_collision, obj::obj_2d::Object2dBuilder, @@ -16,7 +14,11 @@ use raqote::{DrawOptions, DrawTarget, PathBuilder, SolidSource, Source}; const WIDTH: usize = 800; const HEIGHT: usize = 500; -/* +fn sleep(time: Duration) { + thread::sleep(time); +} + +/* *this is an example of elastic collisions using motion for more information see their wikipedia page: https://en.wikipedia.org/wiki/Elastic_collision */ @@ -33,9 +35,7 @@ fn main() { let size = window.get_size(); let mut dt = DrawTarget::new(size.0 as i32, size.1 as i32); - let fps = 60.0; - let delta_time = 1.0 / fps; - let frame_duration = Duration::from_secs_f32(delta_time as f32); + let mut obj1 = Object2dBuilder::new() .position(vec2(10.0, 100.0)) .radius(20.0) @@ -56,58 +56,74 @@ fn main() { .velocity(vec2(0.0, 0.0)) .build(); - loop { - let frame_start = Instant::now(); - dt.clear(SolidSource::from_unpremultiplied_argb( - 0xff, 0xff, 0xff, 0xff, - )); - let mut pb = PathBuilder::new(); - obj1.apply(&move2d(Direction2d::X, delta_time, delta_time)); - obj2.apply(&move2d(Direction2d::X, delta_time, delta_time)); - let collide = Collision2d::new(obj1, obj2); - - if collide.collider() { - let u = - elastic_collision::calculate(obj1.velocity, obj1.mass, obj2.mass, obj2.velocity); - let u2 = - elastic_collision::calculate(obj2.velocity, obj2.mass, obj1.mass, obj1.velocity); - - obj1.velocity = u; - obj1.acceleration = u; - - obj2.velocity = u2; - obj2.acceleration = u2; - } - - pb.arc(obj1.vec.x, obj1.vec.y, obj1.radius, 0.0, 2.0 * PI); - let path = pb.finish(); - dt.fill( - &path, - &Source::Solid(SolidSource::from_unpremultiplied_argb( - 0xff, 0x00, 0x00, 0x00, - )), - &DrawOptions::new(), - ); - - let mut pb2 = PathBuilder::new(); - - pb2.arc(obj2.vec.x, obj2.vec.y, obj2.radius, 0.0, 2.0 * PI); - let path2 = pb2.finish(); - - dt.fill( - &path2, - &Source::Solid(SolidSource::from_unpremultiplied_argb( - 0xff, 0x00, 0x00, 0x00, - )), - &DrawOptions::new(), - ); - - window - .update_with_buffer(dt.get_data(), size.0, size.1) - .unwrap(); - let frame_time = frame_start.elapsed(); - if frame_time < frame_duration { - ::std::thread::sleep(frame_duration - frame_time); - } - } + let el = EventLoopBuilder::new().fps(60).build(); + + el.start_mut( + move |config| { + dt.clear(SolidSource::from_unpremultiplied_argb( + 0xff, 0xff, 0xff, 0xff, + )); + let mut pb = PathBuilder::new(); + obj1.apply(&move2d( + Direction2d::X, + config.delta_time, + config.delta_time, + )); + obj2.apply(&move2d( + Direction2d::X, + config.delta_time, + config.delta_time, + )); + let collide = Collision2d::new(obj1, obj2); + + if collide.collider() { + let u = elastic_collision::calculate( + obj1.velocity, + obj1.mass, + obj2.mass, + obj2.velocity, + ); + let u2 = elastic_collision::calculate( + obj2.velocity, + obj2.mass, + obj1.mass, + obj1.velocity, + ); + + obj1.velocity = u; + obj1.acceleration = u; + + obj2.velocity = u2; + obj2.acceleration = u2; + } + + pb.arc(obj1.vec.x, obj1.vec.y, obj1.radius, 0.0, 2.0 * PI); + let path = pb.finish(); + dt.fill( + &path, + &Source::Solid(SolidSource::from_unpremultiplied_argb( + 0xff, 0x00, 0x00, 0x00, + )), + &DrawOptions::new(), + ); + + let mut pb2 = PathBuilder::new(); + + pb2.arc(obj2.vec.x, obj2.vec.y, obj2.radius, 0.0, 2.0 * PI); + let path2 = pb2.finish(); + + dt.fill( + &path2, + &Source::Solid(SolidSource::from_unpremultiplied_argb( + 0xff, 0x00, 0x00, 0x00, + )), + &DrawOptions::new(), + ); + + window + .update_with_buffer(dt.get_data(), size.0, size.1) + .unwrap(); + }, + sleep, + ); } diff --git a/src/event_loop/mod.rs b/src/event_loop/mod.rs index 755f53b..6f43837 100644 --- a/src/event_loop/mod.rs +++ b/src/event_loop/mod.rs @@ -5,6 +5,8 @@ use core::time::Duration; pub struct EventLoopConfig { /// Frames per second for the event loop. pub fps: u32, + /// Delta time calculated from the fps. + pub delta_time: f32, } /// The main structure for the event loop, holding its configuration. @@ -19,7 +21,7 @@ impl EventLoop { /// # Examples /// /// ``` - /// let config = EventLoopConfig { fps: 60 }; + /// let config = EventLoopConfig { fps: 60, delta_time: 1.0 / 60.0 }; /// let event_loop = EventLoop::new(config); /// ``` /// @@ -40,7 +42,7 @@ impl EventLoop { /// # Examples /// /// ``` - /// let config = EventLoopConfig { fps: 60 }; + /// let config = EventLoopConfig { fps: 60, delta_time: 1.0 / 60.0 }; /// let event_loop = EventLoop::new(config); /// /// event_loop.start(|config| { @@ -66,4 +68,99 @@ impl EventLoop { sleep(Duration::from_secs(u64::from(dt))); } } + + /// Starts the event loop with a mutable closure, allowing modification of the loop configuration. + /// + /// # Examples + /// + /// ``` + /// let config = EventLoopConfig { fps: 60, delta_time: 1.0 / 60.0 }; + /// let event_loop = EventLoop::new(config); + /// + /// event_loop.start_mut(|config| { + /// // Your code here + /// }, |duration| { + /// // Sleep for the duration + /// std::thread::sleep(duration); + /// }); + /// ``` + /// + /// # Parameters + /// + /// - `code`: A mutable closure that takes `EventLoopConfig` and contains the code to run each frame. + /// - `sleep`: A closure that takes `Duration` and handles sleeping between frames. + pub fn start_mut(&self, mut code: F, sleep: SF) + where + F: FnMut(EventLoopConfig), + SF: Fn(Duration), + { + let dt = 1 / self.config.fps; + loop { + code(self.config); + sleep(Duration::from_secs(u64::from(dt))); + } + } +} + +/// Builder pattern for constructing an `EventLoop`. +#[derive(Debug)] +pub struct EventLoopBuilder { + config: EventLoopConfig, +} + +impl EventLoopBuilder { + /// Creates a new `EventLoopBuilder` with default configuration. + /// + /// # Examples + /// + /// ``` + /// let builder = EventLoopBuilder::new(); + /// ``` + pub fn new() -> Self { + EventLoopBuilder { + config: EventLoopConfig { + fps: 60, + delta_time: 1.0 / 60.0, + }, + } + } + + /// Sets the frames per second (fps) for the event loop. + /// + /// # Parameters + /// + /// - `fps`: The frames per second to set. + /// + /// # Returns + /// + /// The updated `EventLoopBuilder` instance. + pub fn fps(mut self, fps: u32) -> Self { + self.config = EventLoopConfig { + fps, + delta_time: 1.0 / fps as f32, + }; + self + } + + /// Builds the `EventLoop` with the specified configuration. + /// + /// # Returns + /// + /// A new `EventLoop` instance. + pub fn build(&self) -> EventLoop { + EventLoop { + config: self.config, + } + } +} + +impl Default for EventLoopBuilder { + fn default() -> Self { + EventLoopBuilder { + config: EventLoopConfig { + fps: 60, + delta_time: 1.0 / 60.0, + }, + } + } }