1use crate::bounds::BoundingBox;
5use crate::style::{FillStyle, FontStyle, MarkerShape, StrokeStyle};
6
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9pub enum MarkKey {
10 Index(u64),
12 Name(String),
14}
15
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
18pub enum Interpolation {
19 #[default]
21 Linear,
22 StepBefore,
24 StepAfter,
26 Monotone,
28}
29
30#[derive(Clone, Debug)]
32pub struct LineMark {
33 pub points: Vec<[f32; 2]>,
35 pub stroke: StrokeStyle,
37 pub interpolation: Interpolation,
39}
40
41#[derive(Clone, Debug)]
43pub struct RectMark {
44 pub bounds: BoundingBox,
46 pub fill: FillStyle,
48 pub stroke: StrokeStyle,
50 pub corner_radius: f32,
52}
53
54#[derive(Clone, Debug)]
56pub struct PointMark {
57 pub center: [f32; 2],
59 pub size: f32,
61 pub shape: MarkerShape,
63 pub fill: FillStyle,
65 pub stroke: StrokeStyle,
67}
68
69#[derive(Clone, Debug)]
71pub struct AreaMark {
72 pub upper: Vec<[f32; 2]>,
74 pub lower: Vec<[f32; 2]>,
76 pub fill: FillStyle,
78 pub stroke: StrokeStyle,
80}
81
82#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
84pub enum TextAnchor {
85 #[default]
87 Start,
88 Middle,
90 End,
92}
93
94#[derive(Clone, Debug)]
96pub struct TextMark {
97 pub position: [f32; 2],
99 pub text: String,
101 pub font: FontStyle,
103 pub fill: FillStyle,
105 pub angle: f32,
107 pub anchor: TextAnchor,
109}
110
111#[derive(Clone, Debug)]
113pub struct ArcMark {
114 pub center: [f32; 2],
116 pub inner_radius: f32,
118 pub outer_radius: f32,
120 pub start_angle: f32,
122 pub end_angle: f32,
124 pub fill: FillStyle,
126 pub stroke: StrokeStyle,
128}
129
130#[derive(Clone, Debug)]
132pub struct RuleMark {
133 pub segments: Vec<([f32; 2], [f32; 2])>,
135 pub stroke: StrokeStyle,
137}
138
139#[derive(Clone, Debug)]
141pub struct PathMark {
142 pub commands: Vec<PathCommand>,
144 pub fill: FillStyle,
146 pub stroke: StrokeStyle,
148}
149
150#[derive(Clone, Copy, Debug)]
152pub enum PathCommand {
153 MoveTo([f32; 2]),
155 LineTo([f32; 2]),
157 CubicTo([f32; 2], [f32; 2], [f32; 2]),
159 QuadTo([f32; 2], [f32; 2]),
161 Close,
163}
164
165#[derive(Clone, Debug)]
167pub struct ImageMark {
168 pub bounds: BoundingBox,
170 pub data: Vec<u8>,
172 pub width: u32,
174 pub height: u32,
176}
177
178pub enum Mark {
180 Line(LineMark),
182 Rect(RectMark),
184 Point(PointMark),
186 Area(AreaMark),
188 Text(TextMark),
190 Arc(ArcMark),
192 Rule(RuleMark),
194 Path(PathMark),
196 Image(ImageMark),
198}
199
200#[derive(Clone, Debug)]
202pub enum BatchAttr<T> {
203 Uniform(T),
205 Varying(Vec<T>),
207}
208
209impl<T: Clone> BatchAttr<T> {
210 pub fn get(&self, index: usize) -> &T {
212 match self {
213 Self::Uniform(v) => v,
214 Self::Varying(v) => {
215 debug_assert!(
216 index < v.len(),
217 "BatchAttr index {index} out of bounds (len {})",
218 v.len()
219 );
220 &v[index]
221 }
222 }
223 }
224
225 pub fn len(&self) -> usize {
227 match self {
228 Self::Uniform(_) => 1,
229 Self::Varying(v) => v.len(),
230 }
231 }
232
233 pub fn is_empty(&self) -> bool {
235 match self {
236 Self::Uniform(_) => false,
237 Self::Varying(v) => v.is_empty(),
238 }
239 }
240}
241
242pub enum MarkBatch {
244 Points {
246 positions: Vec<[f32; 2]>,
248 sizes: BatchAttr<f32>,
250 fills: BatchAttr<FillStyle>,
252 shape: MarkerShape,
254 strokes: BatchAttr<StrokeStyle>,
256 },
257 Rules {
259 segments: Vec<([f32; 2], [f32; 2])>,
261 stroke: StrokeStyle,
263 },
264 Rects {
266 rects: Vec<BoundingBox>,
268 fills: BatchAttr<FillStyle>,
270 strokes: BatchAttr<StrokeStyle>,
272 corner_radius: f32,
274 },
275}
276
277impl MarkBatch {
278 pub fn points(
280 positions: Vec<[f32; 2]>,
281 sizes: BatchAttr<f32>,
282 fills: BatchAttr<FillStyle>,
283 shape: MarkerShape,
284 strokes: BatchAttr<StrokeStyle>,
285 ) -> Result<Self, String> {
286 let batch = Self::Points {
287 positions,
288 sizes,
289 fills,
290 shape,
291 strokes,
292 };
293 batch.validate()?;
294 Ok(batch)
295 }
296
297 pub fn rects(
299 rects: Vec<BoundingBox>,
300 fills: BatchAttr<FillStyle>,
301 strokes: BatchAttr<StrokeStyle>,
302 corner_radius: f32,
303 ) -> Result<Self, String> {
304 let batch = Self::Rects {
305 rects,
306 fills,
307 strokes,
308 corner_radius,
309 };
310 batch.validate()?;
311 Ok(batch)
312 }
313
314 pub fn validate(&self) -> Result<(), String> {
316 match self {
317 Self::Points {
318 positions,
319 sizes,
320 fills,
321 strokes,
322 ..
323 } => {
324 let n = positions.len();
325 check_varying_len("sizes", sizes, n)?;
326 check_varying_len("fills", fills, n)?;
327 check_varying_len("strokes", strokes, n)?;
328 }
329 Self::Rects {
330 rects,
331 fills,
332 strokes,
333 ..
334 } => {
335 let n = rects.len();
336 check_varying_len("fills", fills, n)?;
337 check_varying_len("strokes", strokes, n)?;
338 }
339 Self::Rules { .. } => {}
340 }
341 Ok(())
342 }
343}
344
345fn check_varying_len<T: Clone>(
346 name: &str,
347 attr: &BatchAttr<T>,
348 expected: usize,
349) -> Result<(), String> {
350 if let BatchAttr::Varying(v) = attr {
351 if v.len() != expected {
352 return Err(format!(
353 "BatchAttr '{name}' has {} elements, expected {expected}",
354 v.len()
355 ));
356 }
357 }
358 Ok(())
359}