use crate::{Boundary, Contextful, FieldwiseClamp, ParticleInit};
use rand::{Rng, RngExt as _};
use std::ops::{Add, Mul, Sub};
#[derive(Copy, Clone, Default, Debug, PartialEq, PartialOrd)]
pub struct Vector2f {
pub x: f64,
pub y: f64,
}
impl Vector2f {
pub const fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
}
impl FieldwiseClamp for Vector2f {
fn clamp(&self, min: Self, max: Self) -> Self {
Self::new(self.x.clamp(min.x, max.x), self.y.clamp(min.y, max.y))
}
}
impl Add for Vector2f {
type Output = Self;
fn add(self, rhs: Self) -> Self {
Self::new(self.x + rhs.x, self.y + rhs.y)
}
}
impl Sub for Vector2f {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
Self::new(self.x - rhs.x, self.y - rhs.y)
}
}
impl Mul<f64> for Vector2f {
type Output = Self;
fn mul(self, rhs: f64) -> Self {
Self::new(self.x * rhs, self.y * rhs)
}
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct Bounds {
pub min: Vector2f,
pub max: Vector2f,
}
#[derive(Copy, Clone, Default, Debug, PartialEq)]
pub struct RectBoundary {
pub bounds: Bounds,
}
impl Contextful for RectBoundary {
type TContext = ();
}
impl Boundary for RectBoundary {
type T = Vector2f;
fn handle(&self, pos: Vector2f) -> Vector2f {
pos.clamp(self.bounds.min, self.bounds.max)
}
}
pub struct RandomInit {
pub particle_count: usize,
pub bounds: Bounds,
}
impl ParticleInit for RandomInit {
type T = Vector2f;
fn init_pos<R: Rng>(&self, rng: &mut R) -> Vec<Vector2f> {
(0..self.particle_count)
.map(|_| sample(&self.bounds, rng))
.collect()
}
fn init_vel<R: Rng>(&self, rng: &mut R) -> Vec<Vector2f> {
(0..self.particle_count)
.map(|_| sample(&self.bounds, rng) * 0.5)
.collect()
}
}
fn sample<R: Rng>(b: &Bounds, rng: &mut R) -> Vector2f {
Vector2f::new(
rng.random_range(b.min.x..b.max.x),
rng.random_range(b.min.y..b.max.y),
)
}