diff options
| -rw-r--r-- | src/main.rs | 334 |
1 files changed, 269 insertions, 65 deletions
diff --git a/src/main.rs b/src/main.rs index 0df8566..825dbb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use winit::{ event::*, event_loop::EventLoop, + keyboard::{KeyCode, PhysicalKey}, }; use wgpu::util::DeviceExt; @@ -32,6 +33,69 @@ impl Vertex { } } +#[derive(Clone)] +struct Viewport { + x: f32, + y: f32, + width: f32, + height: f32, +} + +struct GraphView { + viewport: Viewport, + lines: Vec<Vec<Vertex>>, + show_grid: bool, +} + +impl GraphView { + fn new(viewport: Viewport) -> Self { + Self { + viewport, + lines: Vec::new(), + show_grid: true, + } + } + + fn generate_grid_lines(&self) -> Vec<Vertex> { + let mut vertices = Vec::new(); + let grid_color = [0.3, 0.7, 0.9]; + + // Vertical grid lines (10 divisions) + for i in 0..=10 { + let x = -1.0 + (i as f32 / 10.0) * 2.0; + vertices.push(Vertex { position: [x, -1.0], color: grid_color }); + vertices.push(Vertex { position: [x, 1.0], color: grid_color }); + } + + // Horizontal grid lines (10 divisions) + for i in 0..=10 { + let y = -1.0 + (i as f32 / 10.0) * 2.0; + vertices.push(Vertex { position: [-1.0, y], color: grid_color }); + vertices.push(Vertex { position: [1.0, y], color: grid_color }); + } + + vertices + } + + fn generate_border(&self) -> Vec<Vertex> { + let border_color = [0.6, 0.7, 0.7]; + vec![ + // Top border + Vertex { position: [-1.0, 1.0], color: border_color }, + Vertex { position: [1.0, 1.0], color: border_color }, + // Right border + Vertex { position: [1.0, 1.0], color: border_color }, + Vertex { position: [1.0, -1.0], color: border_color }, + // Bottom border + Vertex { position: [1.0, -1.0], color: border_color }, + Vertex { position: [-1.0, -1.0], color: border_color }, + // Left border + Vertex { position: [-1.0, -1.0], color: border_color }, + Vertex { position: [-1.0, 1.0], color: border_color }, + ] + } +} + struct State { surface: wgpu::Surface<'static>, device: wgpu::Device, @@ -39,11 +103,11 @@ struct State { config: wgpu::SurfaceConfiguration, size: winit::dpi::PhysicalSize<u32>, window: std::sync::Arc<winit::window::Window>, - render_pipeline: wgpu::RenderPipeline, + line_pipeline: wgpu::RenderPipeline, + line_list_pipeline: wgpu::RenderPipeline, vertex_buffer: wgpu::Buffer, - num_vertices: u32, time: f32, - lines: Vec<Vec<Vertex>>, + graphs: Vec<GraphView>, } impl State { @@ -111,8 +175,8 @@ impl State { push_constant_ranges: &[], }); - let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), + let line_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Line Strip Pipeline"), layout: Some(&render_pipeline_layout), vertex: wgpu::VertexState { module: &shader, @@ -149,14 +213,58 @@ impl State { cache: None, }); + let line_list_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Line List Pipeline"), + layout: Some(&render_pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::LineList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + cache: None, + }); + // Create initial empty buffer (will be updated each frame) let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Vertex Buffer"), - size: (std::mem::size_of::<Vertex>() * 100 * 50) as u64, // 50 lines, 100 points each + size: (std::mem::size_of::<Vertex>() * 100 * 100) as u64, // Larger buffer for multiple views usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); + // Create 2 graph views side-by-side + let graphs = vec![ + GraphView::new(Viewport { x: 0.0, y: 0.0, width: 0.5, height: 1.0 }), + GraphView::new(Viewport { x: 0.5, y: 0.0, width: 0.5, height: 1.0 }), + ]; + Self { surface, device, @@ -164,11 +272,11 @@ impl State { config, size, window, - render_pipeline, + line_pipeline, + line_list_pipeline, vertex_buffer, - num_vertices: 0, time: 0.0, - lines: Vec::new(), + graphs, } } @@ -184,58 +292,45 @@ impl State { fn update(&mut self) { self.time += 0.016; // ~60fps - // Add new line every 10 frames - if (self.time * 60.0) as u32 % 10 == 0 && self.lines.len() < 50 { - let mut line = Vec::new(); - let phase = self.time; - let freq = 2.0 + (self.time * 0.5).sin() * 1.0; - - for i in 0..100 { - let x = (i as f32 / 100.0) * 2.0 - 1.0; - let y = ((i as f32) * 0.1 * freq + phase).sin() * 0.3; - - // Color gradient based on time - let hue = (self.time * 0.1) % 1.0; - let color = [ - (hue * 6.0).sin().abs(), - ((hue + 0.33) * 6.0).sin().abs(), - ((hue + 0.66) * 6.0).sin().abs(), - ]; - - line.push(Vertex { - position: [x, y], - color, - }); + // Update each graph independently + for (graph_idx, graph) in self.graphs.iter_mut().enumerate() { + // Add new line every 10 frames + if (self.time * 60.0) as u32 % 10 == 0 && graph.lines.len() < 50 { + let mut line = Vec::new(); + let phase = self.time + (graph_idx as f32 * 2.0); + let freq = 2.0 + (self.time * 0.5 + graph_idx as f32).sin() * 1.0; + + for i in 0..100 { + let x = (i as f32 / 100.0) * 2.0 - 1.0; + let y = ((i as f32) * 0.1 * freq + phase).sin() * 0.3; + + // Different color per graph + let hue = (self.time * 0.1 + graph_idx as f32 * 0.5) % 1.0; + let color = [ + (hue * 6.0).sin().abs(), + ((hue + 0.33) * 6.0).sin().abs(), + ((hue + 0.66) * 6.0).sin().abs(), + ]; + + line.push(Vertex { + position: [x, y], + color, + }); + } + graph.lines.push(line); } - self.lines.push(line); - } - // Scroll lines down - for (idx, line) in self.lines.iter_mut().enumerate() { - let scroll_offset = idx as f32 * 0.04; - for vertex in line.iter_mut() { - vertex.position[1] -= 0.01; + // Scroll lines down + for line in graph.lines.iter_mut() { + for vertex in line.iter_mut() { + vertex.position[1] -= 0.01; + } } - } - - // Remove lines that have scrolled off screen - self.lines.retain(|line| { - line.first().map(|v| v.position[1] > -1.1).unwrap_or(false) - }); - - // Update vertex buffer - let mut all_vertices = Vec::new(); - for line in &self.lines { - all_vertices.extend_from_slice(line); - } - if !all_vertices.is_empty() { - self.queue.write_buffer( - &self.vertex_buffer, - 0, - bytemuck::cast_slice(&all_vertices), - ); - self.num_vertices = all_vertices.len() as u32; + // Remove lines that have scrolled off screen + graph.lines.retain(|line| { + line.first().map(|v| v.position[1] > -1.1).unwrap_or(false) + }); } } @@ -251,6 +346,63 @@ impl State { label: Some("Render Encoder"), }); + // Collect all vertex data for all graphs + struct DrawData { + viewport: Viewport, + border_offset: usize, + border_count: usize, + grid_offset: usize, + grid_count: usize, + show_grid: bool, + lines_offset: usize, + lines_count: usize, + num_lines: usize, + } + + let mut all_vertices = Vec::new(); + let mut draw_data = Vec::new(); + + for graph in &self.graphs { + let border_vertices = graph.generate_border(); + let border_offset = all_vertices.len(); + all_vertices.extend_from_slice(&border_vertices); + let border_count = border_vertices.len(); + + let grid_offset = all_vertices.len(); + let grid_vertices = graph.generate_grid_lines(); + all_vertices.extend_from_slice(&grid_vertices); + let grid_count = grid_vertices.len(); + + let lines_offset = all_vertices.len(); + let mut line_vertices = Vec::new(); + for line in &graph.lines { + line_vertices.extend_from_slice(line); + } + all_vertices.extend_from_slice(&line_vertices); + let lines_count = line_vertices.len(); + + draw_data.push(DrawData { + viewport: graph.viewport.clone(), + border_offset, + border_count, + grid_offset, + grid_count, + show_grid: graph.show_grid, + lines_offset, + lines_count, + num_lines: graph.lines.len(), + }); + } + + // Write all vertices at once + if !all_vertices.is_empty() { + self.queue.write_buffer( + &self.vertex_buffer, + 0, + bytemuck::cast_slice(&all_vertices), + ); + } + { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), @@ -272,15 +424,46 @@ impl State { timestamp_writes: None, }); - render_pass.set_pipeline(&self.render_pipeline); - render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + // Render each graph view + for data in &draw_data { + // Set viewport for this graph + render_pass.set_viewport( + data.viewport.x * self.size.width as f32, + data.viewport.y * self.size.height as f32, + data.viewport.width * self.size.width as f32, + data.viewport.height * self.size.height as f32, + 0.0, + 1.0, + ); + + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + + // Draw border + render_pass.set_pipeline(&self.line_list_pipeline); + render_pass.draw( + data.border_offset as u32..(data.border_offset + data.border_count) as u32, + 0..1, + ); + + // Draw grid if enabled + if data.show_grid { + render_pass.set_pipeline(&self.line_list_pipeline); + render_pass.draw( + data.grid_offset as u32..(data.grid_offset + data.grid_count) as u32, + 0..1, + ); + } - // Draw each line separately for proper line strip rendering - let points_per_line = 100; - for i in 0..self.lines.len() { - let start = (i * points_per_line) as u32; - let end = start + points_per_line as u32; - render_pass.draw(start..end, 0..1); + // Draw waterfall lines + if data.lines_count > 0 { + render_pass.set_pipeline(&self.line_pipeline); + let points_per_line = 100; + for i in 0..data.num_lines { + let start = (data.lines_offset + i * points_per_line) as u32; + let end = start + points_per_line as u32; + render_pass.draw(start..end, 0..1); + } + } } } @@ -316,6 +499,27 @@ fn main() { WindowEvent::Resized(physical_size) => { state.resize(*physical_size); } + WindowEvent::KeyboardInput { + event: key_event, + .. + } => { + if key_event.state == winit::event::ElementState::Pressed { + if let PhysicalKey::Code(keycode) = key_event.physical_key { + match keycode { + KeyCode::KeyG => { + println!("Grid toggle pressed"); + // Toggle grid for all graphs + for graph in &mut state.graphs { + graph.show_grid = !graph.show_grid; + println!("Grid now: {}", graph.show_grid); + } + } + KeyCode::Escape => control_flow.exit(), + _ => {} + } + } + } + } WindowEvent::RedrawRequested => { state.update(); match state.render() { |
