Skip to content

Instantly share code, notes, and snippets.

@randrews
Created March 27, 2023 04:57
Show Gist options
  • Save randrews/da71f67e0ac2968819c3b7635f9b364a to your computer and use it in GitHub Desktop.
Save randrews/da71f67e0ac2968819c3b7635f9b364a to your computer and use it in GitHub Desktop.
Bouncing circle in Rust
use std::arch::aarch64::float32x4x4_t;
use std::f32::consts::PI;
use std::thread::sleep;
use std::time::Duration;
use pixels::{Pixels, PixelsBuilder};
use pixels::wgpu::TextureFormat;
use raqote::{DrawOptions, DrawTarget, Path, PathBuilder, SolidSource, Source};
use winit::dpi::LogicalSize;
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
// The state of the ball-bouncing system: current coordinates and velocity vector
struct State {
x: f32, y: f32,
v: f32, a: f32
}
impl State {
// The velocity is in pixels / sec; the angle is radians
fn new() -> Self {
Self {
x: 100.0, y: 100.0,
v: 500.0, a: PI / 3.0
}
}
// Update our position based on the velocity vector and a time delta
fn update(&mut self, dt: f32) {
// Move the thing based on the vector
self.x += self.a.cos() * self.v * dt;
self.y += self.a.sin() * self.v * dt;
// If we're offscreen, then try and bounce.
let mut collide = false;
if self.x < 0.0 {
self.x = 0.0; collide = true
}
if self.x >= 640.0 {
self.x = 639.0; collide = true
}
if self.y < 0.0 {
self.y = 0.0; collide = true
}
if self.y >= 640.0 {
self.y = 639.0; collide = true
}
// This is just wrong. The actual reflection angle wouldn't be this, but it's fine for
// this because the point is just to animate a circle doing _something_
if collide {
self.a -= PI / 2.0
}
}
// Draw out circle on a drawtarget
fn draw(&self, draw_target: &mut DrawTarget) {
// First, clear it
draw_target.clear(SolidSource::from_unpremultiplied_argb(0xff, 0x20, 0xa0, 0xb0));
// Build a path that's our circle
let mut pb = PathBuilder::new();
pb.arc(self.x, self.y, 30.0, 0.0, PI * 2.0);
// Fill that path with red
draw_target.fill(&pb.finish(),
&Source::Solid(SolidSource::from_unpremultiplied_argb(0xff, 0xc0, 0, 0)),
&DrawOptions::new());
}
}
fn main() -> ! {
// Make an event loop and a window
// (start up winit, in other words)
let mut event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_inner_size(LogicalSize { width: 640, height: 640 })
.with_title("Bouncy!").build(&event_loop)
.expect("Failed to create window! :(");
// Create a raqote draw target
// This is what we'll draw geometry on
let mut draw_target = DrawTarget::new(640, 640);
// Create a surface texture for pixels to render
let mut texture = pixels::SurfaceTexture::new(
(640.0 * window.scale_factor()) as u32,
(640.0 * window.scale_factor()) as u32,
&window);
// Create a pixels, specifying that the texture it's rendering will be in bgra / srgb
// because that's what raqote's draw_target will be in
let mut pixels = PixelsBuilder::new(640, 640, texture)
.texture_format(TextureFormat::Bgra8UnormSrgb)
.build().expect("Failed to open drawing context D:");
let mut state = State::new();
// Run an event loop that just waits for a CloseRequested and then bails.
event_loop.run(move |event, target, control_flow| {
match event {
Event::WindowEvent { event, .. } => {
match event {
// They want the window to close; kill the app
WindowEvent::CloseRequested => { *control_flow = ControlFlow::ExitWithCode(0) }
_ => {}
}
}
// We've handled all the events: update the display a tick
Event::MainEventsCleared => {
// This delay gives about 30 fps
let delay = 0.03;
// Update the state and redraw it
state.update(delay);
state.draw(&mut draw_target);
// Copy the raqote target to the pixels buffer and thence to the GPU with render
pixels.frame_mut().copy_from_slice(draw_target.get_data_u8());
pixels.render().expect("Failed to render frame :O");
// Sleep for another frame
sleep(Duration::from_millis((delay * 1000.0) as u64));
}
_ => {}
}
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment