1use crate::canvas::CanvasDimensions;
2use crate::error::AvengerWgpuError;
3use crate::marks::instanced_mark::{InstancedMarkBatch, InstancedMarkShader};
4use avenger::marks::path::PathTransform;
5use avenger::marks::symbol::SymbolMark;
6use itertools::izip;
7use lyon::lyon_tessellation::{
8 BuffersBuilder, FillVertex, FillVertexConstructor, StrokeVertex, StrokeVertexConstructor,
9};
10use lyon::tessellation::geometry_builder::VertexBuffers;
11use lyon::tessellation::{FillOptions, FillTessellator, StrokeOptions, StrokeTessellator};
12use wgpu::{Extent3d, VertexBufferLayout};
13
14const FILL_KIND: u32 = 0;
15const STROKE_KIND: u32 = 1;
16
17#[repr(C)]
18#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
19pub struct SymbolUniform {
20 pub size: [f32; 2],
21 pub origin: [f32; 2],
22 pub scale: f32,
23 _pad: [f32; 5],
24}
25
26impl SymbolUniform {
27 pub fn new(dimensions: CanvasDimensions, origin: [f32; 2]) -> Self {
28 Self {
29 size: dimensions.size,
30 scale: dimensions.scale,
31 origin,
32 _pad: [0.0, 0.0, 0.0, 0.0, 0.0],
33 }
34 }
35}
36
37#[repr(C)]
38#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
39pub struct SymbolVertex {
40 pub position: [f32; 2],
41 pub normal: [f32; 2],
42 pub kind: u32,
43 pub shape_index: u32,
44}
45
46const VERTEX_ATTRIBUTES: [wgpu::VertexAttribute; 4] = wgpu::vertex_attr_array![
47 0 => Float32x2, 1 => Float32x2, 2 => Uint32, 3 => Uint32, ];
52
53impl SymbolVertex {
54 pub fn desc() -> VertexBufferLayout<'static> {
55 VertexBufferLayout {
56 array_stride: std::mem::size_of::<SymbolVertex>() as wgpu::BufferAddress,
57 step_mode: wgpu::VertexStepMode::Vertex,
58 attributes: &VERTEX_ATTRIBUTES,
59 }
60 }
61}
62
63#[repr(C)]
64#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
65pub struct SymbolInstance {
66 pub position: [f32; 2],
67 pub fill_color: [f32; 4],
68 pub stroke_color: [f32; 4],
69 pub stroke_width: f32,
70 pub relative_scale: f32,
71 pub angle: f32,
72 pub shape_index: u32,
73}
74
75const INSTANCE_ATTRIBUTES: [wgpu::VertexAttribute; 7] = wgpu::vertex_attr_array![
78 4 => Float32x2, 5 => Float32x4, 6 => Float32x4, 7 => Float32, 8 => Float32, 9 => Float32, 10 => Uint32, ];
86
87impl SymbolInstance {
88 pub fn from_spec(
89 mark: &SymbolMark,
90 max_size: f32,
91 ) -> (Vec<SymbolInstance>, Option<image::DynamicImage>, Extent3d) {
92 let max_scale = max_size.sqrt();
93 let stroke_width = mark.stroke_width.unwrap_or(0.0);
94 let mut instances: Vec<SymbolInstance> = Vec::new();
95 for (x, y, fill, size, stroke, angle, shape_index) in izip!(
96 mark.x_iter(),
97 mark.y_iter(),
98 mark.fill_iter(),
99 mark.size_iter(),
100 mark.stroke_iter(),
101 mark.angle_iter(),
102 mark.shape_index_iter(),
103 ) {
104 instances.push(SymbolInstance {
105 position: [*x, *y],
106 fill_color: fill.color_or_transparent(),
107 stroke_color: stroke.color_or_transparent(),
108 stroke_width,
109 relative_scale: (*size).sqrt() / max_scale,
110 angle: *angle,
111 shape_index: (*shape_index) as u32,
112 });
113 }
114
115 (instances, None, Default::default())
116 }
117}
118
119pub struct SymbolShader {
120 verts: Vec<SymbolVertex>,
121 indices: Vec<u16>,
122 instances: Vec<SymbolInstance>,
123 uniform: SymbolUniform,
124 batches: Vec<InstancedMarkBatch>,
125 texture_size: Extent3d,
126 shader: String,
127 vertex_entry_point: String,
128 fragment_entry_point: String,
129}
130
131impl SymbolShader {
132 pub fn from_symbol_mark(
133 mark: &SymbolMark,
134 dimensions: CanvasDimensions,
135 origin: [f32; 2],
136 ) -> Result<Self, AvengerWgpuError> {
137 let shapes = &mark.shapes;
138 let max_size = mark.max_size();
139 let max_scale = max_size.sqrt();
140 let mut verts: Vec<SymbolVertex> = Vec::new();
141 let mut indices: Vec<u16> = Vec::new();
142 for (shape_index, shape) in shapes.iter().enumerate() {
143 let shape_index = shape_index as u32;
144 let path = shape.as_path();
145
146 let scaled_path = path
148 .as_ref()
149 .clone()
150 .transformed(&PathTransform::scale(max_scale, max_scale));
151
152 let mut buffers: VertexBuffers<SymbolVertex, u16> = VertexBuffers::new();
153 let mut builder = BuffersBuilder::new(&mut buffers, VertexPositions { shape_index });
154
155 let mut fill_tessellator = FillTessellator::new();
157 let fill_options = FillOptions::default().with_tolerance(0.1);
158 fill_tessellator.tessellate_path(&scaled_path, &fill_options, &mut builder)?;
159
160 if mark.stroke_width.is_some() {
162 let mut stroke_tessellator = StrokeTessellator::new();
163 let stroke_options = StrokeOptions::default()
164 .with_tolerance(0.1)
165 .with_line_width(1.0);
166 stroke_tessellator.tessellate_path(&scaled_path, &stroke_options, &mut builder)?;
167 }
168
169 let index_offset = verts.len() as u16;
170 verts.extend(buffers.vertices);
171 indices.extend(buffers.indices.into_iter().map(|i| i + index_offset));
172 }
173 let (instances, img, texture_size) = SymbolInstance::from_spec(mark, max_size);
174 let batches = vec![InstancedMarkBatch {
175 instances_range: 0..instances.len() as u32,
176 image: img,
177 }];
178 Ok(Self {
179 verts,
180 indices,
181 instances,
182 uniform: SymbolUniform::new(dimensions, origin),
183 batches,
184 texture_size,
185 shader: include_str!("symbol.wgsl").into(),
186 vertex_entry_point: "vs_main".to_string(),
187 fragment_entry_point: "fs_main".to_string(),
188 })
189 }
190}
191
192impl InstancedMarkShader for SymbolShader {
193 type Instance = SymbolInstance;
194 type Vertex = SymbolVertex;
195 type Uniform = SymbolUniform;
196
197 fn verts(&self) -> &[Self::Vertex] {
198 self.verts.as_slice()
199 }
200
201 fn indices(&self) -> &[u16] {
202 self.indices.as_slice()
203 }
204
205 fn instances(&self) -> &[Self::Instance] {
206 self.instances.as_slice()
207 }
208
209 fn uniform(&self) -> Self::Uniform {
210 self.uniform
211 }
212
213 fn batches(&self) -> &[InstancedMarkBatch] {
214 self.batches.as_slice()
215 }
216
217 fn texture_size(&self) -> Extent3d {
218 self.texture_size
219 }
220
221 fn shader(&self) -> &str {
222 self.shader.as_str()
223 }
224
225 fn vertex_entry_point(&self) -> &str {
226 self.vertex_entry_point.as_str()
227 }
228
229 fn fragment_entry_point(&self) -> &str {
230 self.fragment_entry_point.as_str()
231 }
232
233 fn instance_desc(&self) -> wgpu::VertexBufferLayout<'static> {
234 wgpu::VertexBufferLayout {
235 array_stride: std::mem::size_of::<SymbolInstance>() as wgpu::BufferAddress,
236 step_mode: wgpu::VertexStepMode::Instance,
237 attributes: &INSTANCE_ATTRIBUTES,
238 }
239 }
240
241 fn vertex_desc(&self) -> VertexBufferLayout<'static> {
242 SymbolVertex::desc()
243 }
244}
245
246pub struct VertexPositions {
247 shape_index: u32,
248}
249
250impl FillVertexConstructor<SymbolVertex> for VertexPositions {
251 fn new_vertex(&mut self, vertex: FillVertex) -> SymbolVertex {
252 SymbolVertex {
255 position: [vertex.position().x, -vertex.position().y],
256 normal: [0.0, 0.0],
257 kind: FILL_KIND,
258 shape_index: self.shape_index,
259 }
260 }
261}
262
263impl StrokeVertexConstructor<SymbolVertex> for VertexPositions {
264 fn new_vertex(&mut self, vertex: StrokeVertex) -> SymbolVertex {
265 SymbolVertex {
268 position: [vertex.position().x, -vertex.position().y],
269 normal: [vertex.normal().x, -vertex.normal().y],
270 kind: STROKE_KIND,
271 shape_index: self.shape_index,
272 }
273 }
274}