From 9a255f9a7da1394f782f3293ba3110780444e7d3 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Sun, 8 Mar 2026 18:22:11 +0100 Subject: [PATCH] trying to animate --- TODO.md | 8 ++++ src/main.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++---- src/shader.wgsl | 22 +++++++++- 3 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 TODO.md diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..c23e4e6 --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +# TODOs + +Nicely animate the transition into the voronoi diagram. +Actually making a cool animation with movement likely requires knowing the distance to the voronoi center of any point. +To know this we will likely have to construct the voronoi diagram on the CPU beforehand and having it as geometry. +To do this, we probably need to generate it in 3D (using something like Bowyer-Watson) and then project it into 2D somehow (using magic) and then do things. + +Actually use a good gradient that doesn't look kinda bad. diff --git a/src/main.rs b/src/main.rs index 081057c..3e23269 100644 --- a/src/main.rs +++ b/src/main.rs @@ -106,6 +106,12 @@ struct App { pointers: HashMap, layer_surfaces: Vec, } +#[derive(Debug, PartialEq)] +enum AnimationState { + Descending { last_time: Option }, + Paused, + Ascending { last_time: Option }, +} struct OutputSurface { // must be first to be dropped before the Wayland surface @@ -115,6 +121,71 @@ struct OutputSurface { width: u32, height: u32, voronoi_progress: f32, + animation_running: AnimationState, +} + +const VORONOI_SPEED: f32 = 1000.; + +impl OutputSurface { + fn request_frame_if_necessary(&self, qh: &QueueHandle) { + if self.animation_running != AnimationState::Paused { + self.layer_surface + .wl_surface() + .frame(qh, self.layer_surface.wl_surface().clone()); + } + } + + fn tick_animation(&mut self, new_time: u32) { + match self.animation_running { + AnimationState::Paused => {} + AnimationState::Descending { last_time: None } => { + self.animation_running = AnimationState::Descending { + last_time: Some(new_time), + } + } + AnimationState::Ascending { last_time: None } => { + self.animation_running = AnimationState::Ascending { + last_time: Some(new_time), + } + } + AnimationState::Ascending { + last_time: Some(last_time), + } => { + let delta_time = new_time - last_time; + + self.voronoi_progress = f32::min( + self.voronoi_progress + delta_time as f32 / VORONOI_SPEED, + 1.0, + ); + + if self.voronoi_progress >= 1.0 { + self.animation_running = AnimationState::Paused; + } else { + self.animation_running = AnimationState::Ascending { + last_time: Some(new_time), + }; + } + } + AnimationState::Descending { + last_time: Some(last_time), + } => { + let delta_time = new_time - last_time; + + self.voronoi_progress = f32::max( + self.voronoi_progress - delta_time as f32 / VORONOI_SPEED, + 0.0, + ); + + if self.voronoi_progress <= 0.0 { + self.animation_running = AnimationState::Paused; + } else { + self.animation_running = AnimationState::Descending { + last_time: Some(new_time), + }; + } + } + } + } } impl ProvidesRegistryState for App { @@ -167,6 +238,7 @@ impl OutputHandler for App { width: 0, height: 0, voronoi_progress: 0.0, + animation_running: AnimationState::Paused, }); } Err(err) => error!( @@ -231,11 +303,26 @@ impl CompositorHandler for App { fn frame( &mut self, _conn: &Connection, - _qh: &QueueHandle, - _surface: &wayland_client::protocol::wl_surface::WlSurface, - _time: u32, + qh: &QueueHandle, + wl_surface: &wayland_client::protocol::wl_surface::WlSurface, + time: u32, ) { - dbg!("yeet"); + let Some(surface) = self + .layer_surfaces + .iter_mut() + .find(|surface| surface.layer_surface.wl_surface() == wl_surface) + else { + return; + }; + + surface.tick_animation(time); + surface + .gpu + .set_voronoi_progress(&self.gpu, surface.voronoi_progress); + surface.request_frame_if_necessary(qh); + surface.gpu.draw(&self.gpu); + + dbg!((&surface.animation_running, surface.voronoi_progress)); } fn surface_enter( @@ -371,7 +458,7 @@ impl PointerHandler for App { fn pointer_frame( &mut self, _conn: &Connection, - _qh: &QueueHandle, + qh: &QueueHandle, _pointer: &wayland_client::protocol::wl_pointer::WlPointer, events: &[smithay_client_toolkit::seat::pointer::PointerEvent], ) { @@ -419,22 +506,28 @@ impl PointerHandler for App { PointerEventKind::Press { button: BTN_RIGHT, .. } => { - surface.voronoi_progress = 1.0; + surface.animation_running = AnimationState::Ascending { last_time: None }; surface .gpu .set_voronoi_progress(&self.gpu, surface.voronoi_progress); + + surface.request_frame_if_necessary(qh); + surface.gpu.draw(&self.gpu); } PointerEventKind::Release { button: BTN_RIGHT, .. } | PointerEventKind::Leave { .. } => { - surface.voronoi_progress = 0.0; + surface.animation_running = AnimationState::Descending { last_time: None }; surface .gpu .set_voronoi_progress(&self.gpu, surface.voronoi_progress); + + surface.request_frame_if_necessary(qh); + surface.gpu.draw(&self.gpu); } _ => (), diff --git a/src/shader.wgsl b/src/shader.wgsl index d86c545..97d8be7 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -47,15 +47,33 @@ fn fs_main(@builtin(position) pos: vec4) -> @location(0) vec4 { } } var voronoi_color = best; - - color = mix(color, voronoi_color, input.voronoi_progress); + var distance_to_center = sqrt(best_score); // a value like 0.1 is pretty good in the grand scheme + var badness = clamp(distance_to_center * 5, 0, 0.5); // 0..1, 1=terrible 0=perfect + // badness determines at which point in the animation progress the animation should start. + // if it's 0.3, we start the animation at 0.3 and end it at 1.0 + // so we need to scale and shift input progress (0.3=>0.0, 0.65=>0.5, 1.0=>1.0) + var voronoi_progress_shifted = transform_progress(input.voronoi_progress, badness); + + var final_progress = clamp(voronoi_progress_shifted, 0, 1); + // mix with red to see how terrible it looks + color = mix(color, voronoi_color, final_progress); + + //color.x = distance_to_center; // keep it in sync with the cpu implementation var srgbcolor = oklab_to_linear_srgb(color); return vec4(srgbcolor.x, srgbcolor.y, srgbcolor.z, 1.0); } +fn transform_progress(x: f32, badness: f32) -> f32 { + return ((x) - badness) * (1 / (1 - badness)); +} +fn wobble(x: f32) -> f32 { + var wobble_factor = 0.0; + return sin(x * wobble_factor) / 2.0 + 1; +} + // keep it in sync with the cpu implementation fn diff_colors(oklab_a: vec3f, oklab_b: vec3f) -> f32 { var diff = oklab_a - oklab_b;