use macroquad::color::Color;
use macroquad::math::Vec3;
use macroquad::prelude::draw_line_3d;
use std::slice::{Iter, IterMut};
use std::time::Instant;
use crate::particle_sys::ParticleSys;
use crate::util::{check_period, map_color_decay};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Particle {
location: Vec3,
end_location: Vec3,
color: Color,
length: f32,
sloped: bool,
start_time: Instant,
}
impl Particle {
pub fn new(
(x, y, z): (f32, f32, f32),
(r, g, b, a): (f32, f32, f32, f32),
size: f32,
length: f32,
sloped: bool,
) -> Result<Self, String> {
let l = Vec3::new(x, y, z);
let el = l + Vec3::splat(size);
check_period(length)?;
Ok(Particle {
location: l,
end_location: el,
color: Color::new(r, g, b, a),
length,
sloped,
start_time: Instant::now(),
})
}
pub fn new_line(
(x, y, z): (f32, f32, f32),
(xe, ye, ze): (f32, f32, f32),
(r, g, b, a): (f32, f32, f32, f32),
length: f32,
sloped: bool,
) -> Result<Self, String> {
check_period(length)?;
Ok(Particle {
location: Vec3::new(x, y, z),
end_location: Vec3::new(xe, ye, ze),
color: Color::new(r, g, b, a),
length,
sloped,
start_time: Instant::now(),
})
}
#[inline]
pub fn add_location(mut self, x: f32, y: f32, z: f32) -> Self {
self.location.x += x;
self.location.y += y;
self.location.z += z;
self
}
#[inline]
pub fn sub_location(mut self, x: f32, y: f32, z: f32) -> Self {
self.location.x -= x;
self.location.y -= y;
self.location.z -= z;
self
}
#[inline]
pub fn set_location(&mut self, x: f32, y: f32, z: f32) {
self.location = Vec3::new(x, y, z);
}
#[inline]
pub fn set_color(&mut self, r: f32, g: f32, b: f32, a: f32) {
self.color = Color::new(r, g, b, a);
}
#[inline]
pub fn draw(&mut self) -> bool {
let current_time = self.start_time.elapsed().as_secs_f32();
if self.sloped {
let color = map_color_decay(self.color, current_time, self.length);
draw_line_3d(self.location, self.end_location, color);
} else {
draw_line_3d(self.location, self.end_location, self.color);
}
current_time > self.length
}
pub fn reset(&mut self) {
self.start_time = Instant::now();
}
}
impl ParticleSys for Particle {
type T = Particle;
fn is_active(&self) -> bool {
true
}
fn is_looping(&self) -> bool {
false
}
fn is_initialized(&mut self) -> bool {
true
}
fn reset_time(&mut self) {
self.reset()
}
fn elapsed_time(&mut self) -> Option<f32> {
Some(self.start_time.elapsed().as_secs_f32())
}
fn setup(&mut self, _should_loop: bool, _p: Option<f32>) -> Result<(), String> {
self.reset();
Ok(())
}
fn tear_down(&mut self) {}
fn next_frame(&mut self, _time: Option<f32>) -> Result<bool, String> {
Ok(self.draw())
}
fn iter(&self) -> Option<Iter<'_, Self::T>> {
None
}
fn iter_mut(&mut self) -> Option<IterMut<'_, Self::T>> {
None
}
fn with_period(mut self, p: f32) -> Result<Self, String> {
check_period(p)?;
self.length = p;
Ok(self)
}
}
impl Default for Particle {
fn default() -> Self {
Particle::new((0., 0., 0.), (0., 0., 0., 1.), 0.01, 1., false).unwrap()
}
}