1mod camera;
2mod edges;
3mod halo;
4mod hit;
5mod labels;
6mod nodes;
7pub use camera::{Camera, CameraUniform};
8pub use hit::{contains, pick, HitTarget};
9pub use labels::LabelDraw;
10
11use edges::EdgePipeline;
12use halo::HaloPipeline;
13use labels::Labels;
14use nodes::NodePipeline;
15use wgpu::util::DeviceExt;
16
17#[derive(Debug, Clone, Copy, PartialEq)]
19pub struct NodeInstanceData {
20 pub pos: [f32; 2],
21 pub radius: f32,
22 pub opacity: f32,
23 pub shape: u32, pub color: [f32; 4],
25}
26
27#[derive(Clone)]
29pub struct EdgeLineData {
30 pub a: [f32; 2],
31 pub b: [f32; 2],
32 pub color: [f32; 4],
33 pub width: f32,
34 pub opacity: f32,
35}
36
37#[derive(Clone)]
39pub struct HaloInstanceData {
40 pub pos: [f32; 2],
41 pub radius: f32,
42 pub color: [f32; 4],
43 pub softness: f32,
44 pub phase: f32,
47}
48
49pub struct Renderer {
50 device: wgpu::Device,
51 queue: wgpu::Queue,
52 camera: Camera,
53 camera_buf: wgpu::Buffer,
54 camera_bind_group: wgpu::BindGroup,
55 nodes: NodePipeline,
56 node_bounds: Option<([f32; 2], [f32; 2])>,
60 edges: EdgePipeline,
61 halos: HaloPipeline,
62 labels: Labels,
63}
64
65impl Renderer {
66 pub fn new(device: wgpu::Device, queue: wgpu::Queue, format: wgpu::TextureFormat, width: u32, height: u32) -> Self {
67 let camera = Camera::new(width as f32, height as f32);
68 let camera_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
69 label: Some("camera"),
70 contents: bytemuck::bytes_of(&camera.uniform()),
71 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
72 });
73 let camera_bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
74 label: Some("camera-bgl"),
75 entries: &[wgpu::BindGroupLayoutEntry {
76 binding: 0,
77 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
78 ty: wgpu::BindingType::Buffer {
79 ty: wgpu::BufferBindingType::Uniform,
80 has_dynamic_offset: false,
81 min_binding_size: None,
82 },
83 count: None,
84 }],
85 });
86 let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
87 label: Some("camera-bg"),
88 layout: &camera_bgl,
89 entries: &[wgpu::BindGroupEntry { binding: 0, resource: camera_buf.as_entire_binding() }],
90 });
91
92 let nodes = NodePipeline::new(&device, format, &camera_bgl);
93 let edges = EdgePipeline::new(&device, format, &camera_bgl);
94 let halos = HaloPipeline::new(&device, format, &camera_bgl);
95 let labels = Labels::new(&device, &queue, format);
96
97 Self {
98 device,
99 queue,
100 camera,
101 camera_buf,
102 camera_bind_group,
103 nodes,
104 node_bounds: None,
105 edges,
106 halos,
107 labels,
108 }
109 }
110
111 pub fn resize(&mut self, width: u32, height: u32) {
112 self.camera.viewport = [width as f32, height as f32];
113 }
114
115 pub fn camera_mut(&mut self) -> &mut Camera { &mut self.camera }
116 pub fn camera_ref(&self) -> &Camera { &self.camera }
117
118 pub fn set_fade(&mut self, fade: f32) {
119 self.camera.fade = fade.clamp(0.0, 1.0);
120 }
121
122 pub fn configure_surface(&self, surface: &wgpu::Surface, config: &wgpu::SurfaceConfiguration) {
127 surface.configure(&self.device, config);
128 }
129
130 pub fn set_nodes(&mut self, nodes: &[NodeInstanceData]) {
131 self.nodes.upload(&self.device, &self.queue, nodes);
132 self.node_bounds = if nodes.is_empty() {
135 None
136 } else {
137 let mut min = [f32::MAX, f32::MAX];
138 let mut max = [f32::MIN, f32::MIN];
139 for n in nodes {
140 min[0] = min[0].min(n.pos[0] - n.radius);
141 min[1] = min[1].min(n.pos[1] - n.radius);
142 max[0] = max[0].max(n.pos[0] + n.radius);
143 max[1] = max[1].max(n.pos[1] + n.radius);
144 }
145 Some((min, max))
146 };
147 }
148
149 pub fn set_labels(&mut self, labels: &[LabelDraw]) {
152 self.labels.set_labels(labels);
153 }
154
155 pub fn set_edges(&mut self, edges: &[EdgeLineData]) {
156 self.edges.upload(&self.device, &self.queue, edges);
157 }
158
159 pub fn set_halos(&mut self, halos: &[HaloInstanceData]) {
160 self.halos.upload(&self.device, &self.queue, halos);
161 }
162
163 pub fn tick(&mut self, dt_ms: f32, dur_ms: f32) {
165 self.camera.tick(dt_ms, dur_ms);
166 }
167
168 pub fn glide_bounds(&mut self, min: [f32; 2], max: [f32; 2]) {
170 self.camera.glide_bounds(min, max);
171 }
172
173 pub fn fit_view(&mut self) {
175 if let Some((min, max)) = self.node_bounds {
176 self.camera.fit_bounds(min, max);
177 }
178 }
179
180 fn upload_camera(&self) {
181 self.queue.write_buffer(&self.camera_buf, 0, bytemuck::bytes_of(&self.camera.uniform()));
182 }
183
184 pub fn render(&mut self, view: &wgpu::TextureView) -> Result<(), String> {
186 self.upload_camera();
187 self.labels.prepare(&self.device, &self.queue, &self.camera)?;
188 let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("frame") });
189 let label_res;
190 {
191 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
192 label: Some("main-pass"),
193 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
194 view,
195 resolve_target: None,
196 ops: wgpu::Operations {
197 load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.05, g: 0.06, b: 0.09, a: 1.0 }),
198 store: wgpu::StoreOp::Store,
199 },
200 })],
201 depth_stencil_attachment: None,
202 timestamp_writes: None,
203 occlusion_query_set: None,
204 });
205 self.edges.draw(&mut rpass, &self.camera_bind_group);
206 self.halos.draw(&mut rpass, &self.camera_bind_group);
207 self.nodes.draw(&mut rpass, &self.camera_bind_group);
208 label_res = self.labels.draw(&mut rpass);
209 }
210 label_res?;
211 self.queue.submit(Some(encoder.finish()));
212 Ok(())
213 }
214}