Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve event loop #21

Merged
merged 1 commit into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 = "motion"
version = "0.1.4"
version = "0.1.5"
edition = "2021"
authors = ["Juanperias"]
description = "A bare metal physics engine."
Expand Down
4 changes: 1 addition & 3 deletions examples/event_loop_example/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 examples/event_loop_example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ version = "0.1.0"
edition = "2021"

[dependencies]
motion = "*"
motion = { path = "*" }
4 changes: 2 additions & 2 deletions examples/event_loop_example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
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) {
thread::sleep(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);
}
2 changes: 0 additions & 2 deletions examples/raqote_example/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 examples/raqote_example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
140 changes: 78 additions & 62 deletions examples/raqote_example/src/main.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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
*/

Expand All @@ -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)
Expand All @@ -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,
);
}
101 changes: 99 additions & 2 deletions src/event_loop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
/// ```
///
Expand All @@ -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| {
Expand All @@ -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<F, SF>(&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,
},
}
}
}
Loading