Created
March 27, 2023 04:57
-
-
Save randrews/da71f67e0ac2968819c3b7635f9b364a to your computer and use it in GitHub Desktop.
Bouncing circle in Rust
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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