summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgrothedev <grothedev@gmail.com>2025-10-01 23:38:25 -0400
committergrothedev <grothedev@gmail.com>2025-10-01 23:38:25 -0400
commita4cb0a0eb9adf4761189829d544fec187b8b4499 (patch)
tree05e0e556f6210b075c5f1614346f83b8d55b49ab
parent047761dfae9c0f71c69d8259d93d65eed8f02064 (diff)
got multiple subwindows, toggleable gridlines, borders
-rw-r--r--src/main.rs334
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() {