208 lines
5.1 KiB
Rust
208 lines
5.1 KiB
Rust
use minifb::{Key, Window, WindowOptions};
|
|
use std::thread;
|
|
use std::time::{Duration, Instant};
|
|
use std::sync::{Arc, Mutex};
|
|
use rand::Rng;
|
|
use teleprof::instrument;
|
|
|
|
const WIDTH: usize = 800;
|
|
const HEIGHT: usize = 600;
|
|
const BALL_RADIUS: usize = 20;
|
|
|
|
struct Ball {
|
|
x: f32,
|
|
y: f32,
|
|
vx: f32,
|
|
vy: f32,
|
|
color: u32,
|
|
}
|
|
|
|
impl Ball {
|
|
fn new() -> Self {
|
|
Self {
|
|
x: 400.0,
|
|
y: 300.0,
|
|
vx: 200.0,
|
|
vy: 150.0,
|
|
color: 0xFF6464FF,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
// Start the telemetry window
|
|
teleprof::start();
|
|
|
|
// Name the main thread
|
|
teleprof::set_thread_name("Main");
|
|
|
|
println!("Bouncing Ball Demo");
|
|
println!("The ball window should appear alongside the profiler");
|
|
println!("Press Space in profiler window to pause");
|
|
println!("Mouse wheel to zoom, drag to pan in profiler");
|
|
println!("Press Escape in either window to quit");
|
|
println!();
|
|
|
|
let mut window = Window::new(
|
|
"Bouncing Ball",
|
|
WIDTH,
|
|
HEIGHT,
|
|
WindowOptions::default(),
|
|
)
|
|
.expect("Failed to create window");
|
|
|
|
window.set_target_fps(30);
|
|
|
|
let ball = Arc::new(Mutex::new(Ball::new()));
|
|
let mut framebuffer = vec![0u32; WIDTH * HEIGHT];
|
|
|
|
let frame_time = Duration::from_millis(33);
|
|
let mut frame_count = 0;
|
|
|
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
|
let frame_start = Instant::now();
|
|
|
|
main_frame(&ball, &mut framebuffer, &mut window, &mut frame_count, frame_time);
|
|
|
|
let elapsed = frame_start.elapsed();
|
|
if elapsed < frame_time {
|
|
thread::sleep(frame_time - elapsed);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[instrument]
|
|
fn main_frame(
|
|
ball: &Arc<Mutex<Ball>>,
|
|
framebuffer: &mut [u32],
|
|
window: &mut Window,
|
|
frame_count: &mut u32,
|
|
frame_time: Duration,
|
|
) {
|
|
// Check if paused
|
|
if teleprof::PAUSE.try_lock().is_err() {
|
|
println!("Paused!");
|
|
while teleprof::PAUSE.try_lock().is_err() {
|
|
thread::sleep(Duration::from_millis(100));
|
|
}
|
|
println!("Resumed!");
|
|
}
|
|
|
|
// Update physics
|
|
let hit_wall = update_physics(ball, frame_time.as_secs_f32());
|
|
|
|
// If we hit a wall, spawn a thread to pick a new color
|
|
if hit_wall {
|
|
let ball_clone = Arc::clone(ball);
|
|
thread::spawn(move || {
|
|
teleprof::set_thread_name("ColorPicker");
|
|
pick_new_color(ball_clone);
|
|
});
|
|
}
|
|
|
|
// Render
|
|
render(ball, framebuffer);
|
|
|
|
// Update window
|
|
window
|
|
.update_with_buffer(framebuffer, WIDTH, HEIGHT)
|
|
.expect("Failed to update window");
|
|
|
|
*frame_count += 1;
|
|
if *frame_count % 30 == 0 {
|
|
print_status(ball, *frame_count);
|
|
}
|
|
}
|
|
|
|
#[instrument]
|
|
fn update_physics(ball: &Arc<Mutex<Ball>>, dt: f32) -> bool {
|
|
let mut ball = ball.lock().unwrap();
|
|
|
|
ball.x += ball.vx * dt;
|
|
ball.y += ball.vy * dt;
|
|
|
|
let mut hit_wall = false;
|
|
|
|
let radius = BALL_RADIUS as f32;
|
|
if ball.x - radius < 0.0 || ball.x + radius > WIDTH as f32 {
|
|
ball.vx = -ball.vx;
|
|
ball.x = ball.x.clamp(radius, WIDTH as f32 - radius);
|
|
hit_wall = true;
|
|
}
|
|
|
|
if ball.y - radius < 0.0 || ball.y + radius > HEIGHT as f32 {
|
|
ball.vy = -ball.vy;
|
|
ball.y = ball.y.clamp(radius, HEIGHT as f32 - radius);
|
|
hit_wall = true;
|
|
}
|
|
|
|
thread::sleep(Duration::from_millis(5));
|
|
|
|
hit_wall
|
|
}
|
|
|
|
#[instrument]
|
|
fn pick_new_color(ball: Arc<Mutex<Ball>>) {
|
|
thread::sleep(Duration::from_millis(10));
|
|
|
|
let mut rng = rand::thread_rng();
|
|
let r = rng.gen_range(50..255);
|
|
let g = rng.gen_range(50..255);
|
|
let b = rng.gen_range(50..255);
|
|
|
|
let color = ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
|
|
|
|
let mut ball = ball.lock().unwrap();
|
|
ball.color = color;
|
|
|
|
println!(" → New color selected: RGB({}, {}, {})", r, g, b);
|
|
}
|
|
|
|
#[instrument]
|
|
fn render(ball: &Arc<Mutex<Ball>>, framebuffer: &mut [u32]) {
|
|
clear_background(framebuffer);
|
|
draw_ball(ball, framebuffer);
|
|
submit_frame();
|
|
}
|
|
|
|
#[instrument]
|
|
fn clear_background(framebuffer: &mut [u32]) {
|
|
framebuffer.fill(0x2A2A2AFF);
|
|
}
|
|
|
|
#[instrument]
|
|
fn draw_ball(ball: &Arc<Mutex<Ball>>, framebuffer: &mut [u32]) {
|
|
let ball = ball.lock().unwrap();
|
|
|
|
let cx = ball.x as i32;
|
|
let cy = ball.y as i32;
|
|
let radius = BALL_RADIUS as i32;
|
|
|
|
for dy in -radius..=radius {
|
|
for dx in -radius..=radius {
|
|
if dx * dx + dy * dy <= radius * radius {
|
|
let x = cx + dx;
|
|
let y = cy + dy;
|
|
|
|
if x >= 0 && x < WIDTH as i32 && y >= 0 && y < HEIGHT as i32 {
|
|
let idx = y as usize * WIDTH + x as usize;
|
|
framebuffer[idx] = ball.color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[instrument]
|
|
fn submit_frame() {
|
|
thread::sleep(Duration::from_millis(2));
|
|
}
|
|
|
|
#[instrument]
|
|
fn print_status(ball: &Arc<Mutex<Ball>>, frame: u32) {
|
|
let ball = ball.lock().unwrap();
|
|
println!(
|
|
"Frame {}: Ball at ({:.1}, {:.1})",
|
|
frame, ball.x, ball.y
|
|
);
|
|
} |