moonlit/src/util.rs

198 lines
4.7 KiB
Rust

use bevy::{ecs::schedule::ScheduleLabel, prelude::*};
use bevy_mod_debugdump::{render_graph, render_graph_dot, schedule_graph, schedule_graph_dot};
use core::{fmt, ops};
use std::{borrow::Borrow, fs, path::Path};
mod basis;
mod transform_f64;
mod vector2;
mod vector2_i32;
mod vector3;
mod vector3_i32;
pub use basis::*;
pub use transform_f64::*;
pub use vector2::*;
pub use vector2_i32::*;
pub use vector3::*;
pub use vector3_i32::*;
pub trait VectorComponent:
Sized
+ Copy
+ PartialOrd
+ Reflect
+ fmt::Display
+ ops::Add<Output = Self>
+ ops::Neg<Output = Self>
+ ops::Sub<Output = Self>
+ ops::Mul<Output = Self>
+ ops::Div<Output = Self>
+ num_traits::identities::Zero
+ num_traits::identities::One
+ num_traits::sign::Signed
{
fn min(self, b: Self) -> Self;
fn max(self, b: Self) -> Self;
}
impl<T> VectorComponent for T
where
T: Sized
+ Copy
+ PartialOrd
+ Reflect
+ fmt::Display
+ ops::Neg<Output = T>
+ ops::Add<Output = T>
+ ops::Sub<Output = T>
+ ops::Mul<Output = T>
+ ops::Div<Output = T>
+ num_traits::identities::Zero
+ num_traits::identities::One
+ num_traits::sign::Signed,
{
fn min(self, b: Self) -> Self {
if self < b {
self
} else {
b
}
}
fn max(self, b: Self) -> Self {
if self > b {
self
} else {
b
}
}
}
pub fn lerp<
T: Copy
+ ops::Add<Output = T>
+ ops::Sub<Output = T>
+ ops::Mul<Output = T>
+ num_traits::identities::One,
>(
a: T,
b: T,
t: T,
) -> T {
a * (T::one() - t) + b * t
}
pub fn inverse_lerp<T: Copy + ops::Sub<Output = T> + ops::Div<Output = T>>(
a: T,
b: T,
value: T,
) -> T {
(value - a) / (b - a)
}
pub fn vec2_lerp(a: Vec2, b: Vec2, t: f32) -> Vec2 {
Vec2 {
x: lerp(a.x, b.x, t),
y: lerp(a.y, b.y, t),
}
}
pub fn vec3_lerp(a: Vec3, b: Vec3, t: f32) -> Vec3 {
Vec3 {
x: lerp(a.x, b.x, t),
y: lerp(a.y, b.y, t),
z: lerp(a.z, b.z, t),
}
}
pub fn move_towards_vec2(from: Vec2, to: Vec2, amount: f32) -> Vec2 {
let diff = to - from;
let length = diff.length();
if length <= f32::EPSILON {
return from;
}
from + diff.normalize() * length.min(amount)
}
pub fn move_towards_vec3(from: Vec3, to: Vec3, amount: f32) -> Vec3 {
let diff = to - from;
let length = diff.length();
if length <= f32::EPSILON {
return from;
}
from + diff.normalize() * length.min(amount)
}
/// Get the intersection point (if any) of 2d lines a and b.
/// Lines are defined by 2 points on the line
pub fn vec2_intersection(a1: Vec2, a2: Vec2, b1: Vec2, b2: Vec2) -> Option<Vec2> {
let a_dir = a2 - a1;
let b_dir = b2 - b1;
let determinant = a_dir.perp_dot(b_dir);
if determinant.abs() <= f32::EPSILON {
return None;
}
Some(
Vec2 {
x: a_dir.x * (b1.x * b2.y - b1.y * b2.x) - (a1.x * a2.y - a1.y * a2.x) * b_dir.x,
y: (a1.x * a2.y - a1.y * a2.x) * -b_dir.y + a_dir.y * (b1.x * b2.y - b1.y * b2.x),
} / determinant,
)
}
pub fn loop_value(from: f32, to: f32, value: f32) -> f32 {
let range = to - from;
if !range.is_normal() {
return from;
}
value - inverse_lerp(from, to, value).floor() * range
}
pub fn create_app_graphs(app: &mut App) {
let schedules: Vec<Box<dyn ScheduleLabel>> = app
.world
.borrow()
.resource::<Schedules>()
.iter()
.map(|(label, _)| label.dyn_clone())
.collect();
println!("Writing {} schedule graphs", schedules.len());
schedules.iter().for_each(|schedule_label| {
let path = Path::new("graphs").join(format!("schedule_{schedule_label:?}.dot"));
write_schedule_graph(app, &path, &schedule_label.to_owned());
println!("\t- {}", path.to_string_lossy())
});
println!("Writing render graph");
{
let path = Path::new("graphs").join("render.dot");
write_render_graph(app, &path);
println!("\t- {}", path.to_string_lossy());
}
}
fn write_schedule_graph(app: &mut App, path: &Path, schedule: &dyn ScheduleLabel) {
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(
path,
schedule_graph_dot(
app,
schedule.dyn_clone(),
&schedule_graph::settings::Settings::default(),
),
)
.unwrap();
}
fn write_render_graph(app: &mut App, path: &Path) {
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(
path,
render_graph_dot(app, &render_graph::settings::Settings::default()),
)
.unwrap();
}