#![allow(clippy::cast_precision_loss)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
#![allow(clippy::cast_possible_wrap)]
use dotmax::animation::AnimationLoop;
use dotmax::primitives::draw_circle_filled;
const WIDTH: usize = 80;
const HEIGHT: usize = 24;
const GRAVITY: f64 = 0.3; const BOUNCE_DAMPING: f64 = 0.85; const INITIAL_VX: f64 = 2.5; const INITIAL_VY: f64 = 0.0;
struct Ball {
x: f64,
y: f64,
vx: f64,
vy: f64,
radius: u32,
}
impl Ball {
const fn new(width: usize, _height: usize) -> Self {
Self {
x: (width * 2 / 2) as f64, y: 20.0, vx: INITIAL_VX,
vy: INITIAL_VY,
radius: 6,
}
}
fn update(&mut self, max_x: f64, max_y: f64) {
self.vy += GRAVITY;
self.x += self.vx;
self.y += self.vy;
let radius_f64 = f64::from(self.radius);
let min_x = radius_f64;
let max_bound_x = max_x - radius_f64;
if self.x <= min_x {
self.x = min_x;
self.vx = -self.vx * BOUNCE_DAMPING;
} else if self.x >= max_bound_x {
self.x = max_bound_x;
self.vx = -self.vx * BOUNCE_DAMPING;
}
let min_y = radius_f64;
let max_bound_y = max_y - radius_f64;
if self.y <= min_y {
self.y = min_y;
self.vy = -self.vy * BOUNCE_DAMPING;
} else if self.y >= max_bound_y {
self.y = max_bound_y;
self.vy = -self.vy * BOUNCE_DAMPING;
self.vx *= 0.98;
}
}
fn draw(&self, grid: &mut dotmax::BrailleGrid) {
let _ = draw_circle_filled(
grid,
self.x as i32,
self.y as i32,
self.radius,
);
}
}
fn main() -> Result<(), dotmax::DotmaxError> {
let dot_width = (WIDTH * 2) as f64;
let dot_height = (HEIGHT * 4) as f64;
let mut ball = Ball::new(WIDTH, HEIGHT);
AnimationLoop::new(WIDTH, HEIGHT)
.fps(60) .on_frame(move |frame, buffer| {
ball.update(dot_width, dot_height);
ball.draw(buffer);
draw_fps_indicator(buffer, frame)?;
Ok(true) })
.run()
}
fn draw_fps_indicator(
grid: &mut dotmax::BrailleGrid,
frame: u64,
) -> Result<(), dotmax::DotmaxError> {
let dot_index = (frame % 4) as usize;
for i in 0..4 {
if i <= dot_index {
grid.set_dot(2 + i * 2, 2)?;
}
}
Ok(())
}