raui_tesselate_renderer/
lib.rs

1use bytemuck::Pod;
2use fontdue::{
3    Font,
4    layout::{
5        CoordinateSystem, HorizontalAlign, Layout as TextLayout, LayoutSettings, TextStyle,
6        VerticalAlign,
7    },
8};
9use raui_core::{
10    Scalar,
11    layout::{CoordsMapping, Layout},
12    renderer::Renderer,
13    widget::{
14        WidgetId,
15        unit::{
16            WidgetUnit,
17            image::{
18                ImageBoxColor, ImageBoxImage, ImageBoxImageScaling, ImageBoxMaterial,
19                ImageBoxProceduralMesh,
20            },
21            text::{TextBoxHorizontalAlign, TextBoxVerticalAlign},
22        },
23        utils::{Color, Rect, Transform, Vec2, lerp},
24    },
25};
26use spitfire_core::{Triangle, VertexStream};
27use spitfire_fontdue::{TextRenderer, TextVertex};
28use std::collections::HashMap;
29
30#[derive(Debug, Clone)]
31pub enum Error {
32    WidgetHasNoLayout(WidgetId),
33    UnsupportedImageMaterial(Box<ImageBoxMaterial>),
34    FontNotFound(String),
35    ImageNotFound(String),
36}
37
38pub trait TesselateVertex: Pod {
39    fn apply(&mut self, position: [f32; 2], tex_coord: [f32; 3], color: [f32; 4]);
40    fn transform(&mut self, matrix: vek::Mat4<f32>);
41}
42
43#[derive(Debug, Clone, PartialEq)]
44pub enum TesselateBatch {
45    Color,
46    Image {
47        id: String,
48    },
49    Text,
50    Procedural {
51        id: String,
52        images: Vec<String>,
53        parameters: HashMap<String, Scalar>,
54    },
55    ClipPush {
56        x: f32,
57        y: f32,
58        w: f32,
59        h: f32,
60    },
61    ClipPop,
62    Debug,
63}
64
65pub trait TesselateResourceProvider {
66    fn image_id_and_uv_and_size_by_atlas_id(&self, id: &str) -> Option<(String, Rect, Vec2)>;
67    fn fonts(&self) -> &[Font];
68    fn font_index_by_id(&self, id: &str) -> Option<usize>;
69}
70
71pub trait TesselateBatchConverter<B> {
72    fn convert(&mut self, batch: TesselateBatch) -> Option<B>;
73}
74
75impl TesselateBatchConverter<TesselateBatch> for () {
76    fn convert(&mut self, batch: TesselateBatch) -> Option<TesselateBatch> {
77        Some(batch)
78    }
79}
80
81#[derive(Debug, Default, Clone, Copy)]
82pub struct TessselateRendererDebug {
83    pub render_non_visual_nodes: bool,
84}
85
86pub struct TesselateRenderer<'a, V, B, P, C>
87where
88    V: TesselateVertex + TextVertex<Color> + Default,
89    B: PartialEq,
90    P: TesselateResourceProvider,
91    C: TesselateBatchConverter<B>,
92{
93    provider: &'a P,
94    converter: &'a mut C,
95    stream: &'a mut VertexStream<V, B>,
96    text_renderer: &'a mut TextRenderer<Color>,
97    transform_stack: Vec<vek::Mat4<Scalar>>,
98    debug: Option<TessselateRendererDebug>,
99}
100
101impl<'a, V, B, P, C> TesselateRenderer<'a, V, B, P, C>
102where
103    V: TesselateVertex + TextVertex<Color> + Default,
104    B: PartialEq,
105    C: TesselateBatchConverter<B>,
106    P: TesselateResourceProvider,
107{
108    pub fn new(
109        provider: &'a P,
110        converter: &'a mut C,
111        stream: &'a mut VertexStream<V, B>,
112        text_renderer: &'a mut TextRenderer<Color>,
113        debug: Option<TessselateRendererDebug>,
114    ) -> Self {
115        Self {
116            provider,
117            converter,
118            stream,
119            text_renderer,
120            transform_stack: Default::default(),
121            debug,
122        }
123    }
124
125    fn push_transform(&mut self, transform: &Transform, rect: Rect) {
126        let size = rect.size();
127        let offset = vek::Vec2::new(rect.left, rect.top);
128        let offset = vek::Mat4::<Scalar>::translation_2d(offset);
129        let pivot = vek::Vec2::new(
130            lerp(0.0, size.x, transform.pivot.x),
131            lerp(0.0, size.y, transform.pivot.y),
132        );
133        let pivot = vek::Mat4::<Scalar>::translation_2d(pivot);
134        let inv_pivot = pivot.inverted();
135        let align = vek::Vec2::new(
136            lerp(0.0, size.x, transform.align.x),
137            lerp(0.0, size.y, transform.align.y),
138        );
139        let align = vek::Mat4::<Scalar>::translation_2d(align);
140        let translate = vek::Mat4::<Scalar>::translation_2d(raui_to_vec2(transform.translation));
141        let rotate = vek::Mat4::<Scalar>::rotation_z(transform.rotation);
142        let scale = vek::Mat4::<Scalar>::scaling_3d(raui_to_vec2(transform.scale).with_z(1.0));
143        let skew = vek::Mat4::<Scalar>::from(vek::Mat2::new(
144            1.0,
145            transform.skew.y.tan(),
146            transform.skew.x.tan(),
147            1.0,
148        ));
149        let matrix = offset * align * pivot * translate * rotate * scale * skew * inv_pivot;
150        self.push_matrix(matrix);
151    }
152
153    fn push_transform_simple(&mut self, rect: Rect) {
154        let offset = vek::Vec2::new(rect.left, rect.top);
155        let offset = vek::Mat4::<Scalar>::translation_2d(offset);
156        self.push_matrix(offset);
157    }
158
159    fn push_matrix(&mut self, matrix: vek::Mat4<Scalar>) {
160        let matrix = self.top_transform() * matrix;
161        self.transform_stack.push(matrix);
162    }
163
164    fn pop_transform(&mut self) {
165        self.transform_stack.pop();
166    }
167
168    fn top_transform(&self) -> vek::Mat4<Scalar> {
169        self.transform_stack.last().cloned().unwrap_or_default()
170    }
171
172    fn make_vertex(position: Vec2, tex_coord: Vec2, page: Scalar, color: Color) -> V {
173        let mut result = V::default();
174        TesselateVertex::apply(
175            &mut result,
176            [position.x, position.y],
177            [tex_coord.x, tex_coord.y, page],
178            [color.r, color.g, color.b, color.a],
179        );
180        result
181    }
182
183    fn make_tiled_triangle_first(offset: usize) -> Triangle {
184        Triangle { a: 0, b: 1, c: 5 }.offset(offset)
185    }
186
187    fn make_tiled_triangle_second(offset: usize) -> Triangle {
188        Triangle { a: 5, b: 4, c: 0 }.offset(offset)
189    }
190
191    fn produce_color_triangles(&mut self, size: Vec2, scale: Vec2, data: &ImageBoxColor) {
192        let matrix = self.top_transform();
193        let tl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, 0.0)));
194        let tr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, 0.0)));
195        let br = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, size.y)));
196        let bl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, size.y)));
197        let c = data.color;
198        match &data.scaling {
199            ImageBoxImageScaling::Stretch => {
200                if let Some(batch) = self.converter.convert(TesselateBatch::Color) {
201                    self.stream.batch_optimized(batch);
202                    self.stream.quad([
203                        Self::make_vertex(tl, Default::default(), 0.0, c),
204                        Self::make_vertex(tr, Default::default(), 0.0, c),
205                        Self::make_vertex(br, Default::default(), 0.0, c),
206                        Self::make_vertex(bl, Default::default(), 0.0, c),
207                    ]);
208                }
209            }
210            ImageBoxImageScaling::Frame(frame) => {
211                let mut d = frame.destination;
212                d.left *= scale.x;
213                d.right *= scale.x;
214                d.top *= scale.y;
215                d.bottom *= scale.y;
216                if d.left + d.right > size.x {
217                    let m = d.left + d.right;
218                    d.left = size.x * d.left / m;
219                    d.right = size.x * d.right / m;
220                }
221                if d.top + d.bottom > size.y {
222                    let m = d.top + d.bottom;
223                    d.top = size.y * d.top / m;
224                    d.bottom = size.y * d.bottom / m;
225                }
226                let til = vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, 0.0)));
227                let tir = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x - d.right, 0.0)));
228                let itr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, d.top)));
229                let ibr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x, size.y - d.bottom)));
230                let bir = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x - d.right, size.y)));
231                let bil = vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, size.y)));
232                let ibl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, size.y - d.bottom)));
233                let itl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(0.0, d.top)));
234                let itil = vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, d.top)));
235                let itir = vec2_to_raui(matrix.mul_point(vek::Vec2::new(size.x - d.right, d.top)));
236                let ibir = vec2_to_raui(
237                    matrix.mul_point(vek::Vec2::new(size.x - d.right, size.y - d.bottom)),
238                );
239                let ibil =
240                    vec2_to_raui(matrix.mul_point(vek::Vec2::new(d.left, size.y - d.bottom)));
241                if let Some(batch) = self.converter.convert(TesselateBatch::Color) {
242                    self.stream.batch_optimized(batch);
243                    self.stream.extend(
244                        [
245                            Self::make_vertex(tl, Default::default(), 0.0, c),
246                            Self::make_vertex(til, Default::default(), 0.0, c),
247                            Self::make_vertex(tir, Default::default(), 0.0, c),
248                            Self::make_vertex(tr, Default::default(), 0.0, c),
249                            Self::make_vertex(itl, Default::default(), 0.0, c),
250                            Self::make_vertex(itil, Default::default(), 0.0, c),
251                            Self::make_vertex(itir, Default::default(), 0.0, c),
252                            Self::make_vertex(itr, Default::default(), 0.0, c),
253                            Self::make_vertex(ibl, Default::default(), 0.0, c),
254                            Self::make_vertex(ibil, Default::default(), 0.0, c),
255                            Self::make_vertex(ibir, Default::default(), 0.0, c),
256                            Self::make_vertex(ibr, Default::default(), 0.0, c),
257                            Self::make_vertex(bl, Default::default(), 0.0, c),
258                            Self::make_vertex(bil, Default::default(), 0.0, c),
259                            Self::make_vertex(bir, Default::default(), 0.0, c),
260                            Self::make_vertex(br, Default::default(), 0.0, c),
261                        ],
262                        [
263                            Self::make_tiled_triangle_first(0),
264                            Self::make_tiled_triangle_second(0),
265                            Self::make_tiled_triangle_first(1),
266                            Self::make_tiled_triangle_second(1),
267                            Self::make_tiled_triangle_first(2),
268                            Self::make_tiled_triangle_second(2),
269                            Self::make_tiled_triangle_first(4),
270                            Self::make_tiled_triangle_second(4),
271                            Self::make_tiled_triangle_first(6),
272                            Self::make_tiled_triangle_second(6),
273                            Self::make_tiled_triangle_first(8),
274                            Self::make_tiled_triangle_second(8),
275                            Self::make_tiled_triangle_first(9),
276                            Self::make_tiled_triangle_second(9),
277                            Self::make_tiled_triangle_first(10),
278                            Self::make_tiled_triangle_second(10),
279                        ]
280                        .into_iter()
281                        .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_first(5)))
282                        .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_second(5))),
283                    );
284                }
285            }
286        }
287    }
288
289    fn produce_image_triangles(
290        &mut self,
291        id: String,
292        uvs: Rect,
293        size: Vec2,
294        rect: Rect,
295        scale: Vec2,
296        data: &ImageBoxImage,
297    ) {
298        let matrix = self.top_transform();
299        let tl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.top)));
300        let tr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.top)));
301        let br = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.bottom)));
302        let bl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.bottom)));
303        let ctl = Vec2 {
304            x: uvs.left,
305            y: uvs.top,
306        };
307        let ctr = Vec2 {
308            x: uvs.right,
309            y: uvs.top,
310        };
311        let cbr = Vec2 {
312            x: uvs.right,
313            y: uvs.bottom,
314        };
315        let cbl = Vec2 {
316            x: uvs.left,
317            y: uvs.bottom,
318        };
319        let c = data.tint;
320        match &data.scaling {
321            ImageBoxImageScaling::Stretch => {
322                if let Some(batch) = self.converter.convert(TesselateBatch::Image { id }) {
323                    self.stream.batch_optimized(batch);
324                    self.stream.quad([
325                        Self::make_vertex(tl, ctl, 0.0, c),
326                        Self::make_vertex(tr, ctr, 0.0, c),
327                        Self::make_vertex(br, cbr, 0.0, c),
328                        Self::make_vertex(bl, cbl, 0.0, c),
329                    ]);
330                }
331            }
332            ImageBoxImageScaling::Frame(frame) => {
333                let inv_size = Vec2 {
334                    x: 1.0 / size.x,
335                    y: 1.0 / size.y,
336                };
337                let mut d = frame.destination;
338                d.left *= scale.x;
339                d.right *= scale.x;
340                d.top *= scale.y;
341                d.bottom *= scale.y;
342                if frame.frame_keep_aspect_ratio {
343                    d.left = (frame.source.left * rect.height()) / size.y;
344                    d.right = (frame.source.right * rect.height()) / size.y;
345                    d.top = (frame.source.top * rect.width()) / size.x;
346                    d.bottom = (frame.source.bottom * rect.width()) / size.x;
347                }
348                if d.left + d.right > rect.width() {
349                    let m = d.left + d.right;
350                    d.left = rect.width() * d.left / m;
351                    d.right = rect.width() * d.right / m;
352                }
353                if d.top + d.bottom > rect.height() {
354                    let m = d.top + d.bottom;
355                    d.top = rect.height() * d.top / m;
356                    d.bottom = rect.height() * d.bottom / m;
357                }
358                let til =
359                    vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.top)));
360                let tir =
361                    vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.top)));
362                let itr =
363                    vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.top + d.top)));
364                let ibr = vec2_to_raui(
365                    matrix.mul_point(vek::Vec2::new(rect.right, rect.bottom - d.bottom)),
366                );
367                let bir = vec2_to_raui(
368                    matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.bottom)),
369                );
370                let bil =
371                    vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.bottom)));
372                let ibl = vec2_to_raui(
373                    matrix.mul_point(vek::Vec2::new(rect.left, rect.bottom - d.bottom)),
374                );
375                let itl =
376                    vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.top + d.top)));
377                let itil = vec2_to_raui(
378                    matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.top + d.top)),
379                );
380                let itir = vec2_to_raui(
381                    matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.top + d.top)),
382                );
383                let ibir = vec2_to_raui(
384                    matrix.mul_point(vek::Vec2::new(rect.right - d.right, rect.bottom - d.bottom)),
385                );
386                let ibil = vec2_to_raui(
387                    matrix.mul_point(vek::Vec2::new(rect.left + d.left, rect.bottom - d.bottom)),
388                );
389                let ctil = Vec2 {
390                    x: uvs.left + frame.source.left * inv_size.x,
391                    y: uvs.top,
392                };
393                let ctir = Vec2 {
394                    x: uvs.right - frame.source.right * inv_size.x,
395                    y: uvs.top,
396                };
397                let citr = Vec2 {
398                    x: uvs.right,
399                    y: uvs.top + frame.source.top * inv_size.y,
400                };
401                let cibr = Vec2 {
402                    x: uvs.right,
403                    y: uvs.bottom - frame.source.bottom * inv_size.y,
404                };
405                let cbir = Vec2 {
406                    x: uvs.right - frame.source.right * inv_size.x,
407                    y: uvs.bottom,
408                };
409                let cbil = Vec2 {
410                    x: uvs.left + frame.source.left * inv_size.x,
411                    y: uvs.bottom,
412                };
413                let cibl = Vec2 {
414                    x: uvs.left,
415                    y: uvs.bottom - frame.source.bottom * inv_size.y,
416                };
417                let citl = Vec2 {
418                    x: uvs.left,
419                    y: uvs.top + frame.source.top * inv_size.y,
420                };
421                let citil = Vec2 {
422                    x: uvs.left + frame.source.left * inv_size.x,
423                    y: uvs.top + frame.source.top * inv_size.y,
424                };
425                let citir = Vec2 {
426                    x: uvs.right - frame.source.right * inv_size.x,
427                    y: uvs.top + frame.source.top * inv_size.y,
428                };
429                let cibir = Vec2 {
430                    x: uvs.right - frame.source.right * inv_size.x,
431                    y: uvs.bottom - frame.source.bottom * inv_size.y,
432                };
433                let cibil = Vec2 {
434                    x: uvs.left + frame.source.left * inv_size.x,
435                    y: uvs.bottom - frame.source.bottom * inv_size.y,
436                };
437                if let Some(batch) = self.converter.convert(TesselateBatch::Image { id }) {
438                    self.stream.batch_optimized(batch);
439                    self.stream.extend(
440                        [
441                            Self::make_vertex(tl, ctl, 0.0, c),
442                            Self::make_vertex(til, ctil, 0.0, c),
443                            Self::make_vertex(tir, ctir, 0.0, c),
444                            Self::make_vertex(tr, ctr, 0.0, c),
445                            Self::make_vertex(itl, citl, 0.0, c),
446                            Self::make_vertex(itil, citil, 0.0, c),
447                            Self::make_vertex(itir, citir, 0.0, c),
448                            Self::make_vertex(itr, citr, 0.0, c),
449                            Self::make_vertex(ibl, cibl, 0.0, c),
450                            Self::make_vertex(ibil, cibil, 0.0, c),
451                            Self::make_vertex(ibir, cibir, 0.0, c),
452                            Self::make_vertex(ibr, cibr, 0.0, c),
453                            Self::make_vertex(bl, cbl, 0.0, c),
454                            Self::make_vertex(bil, cbil, 0.0, c),
455                            Self::make_vertex(bir, cbir, 0.0, c),
456                            Self::make_vertex(br, cbr, 0.0, c),
457                        ],
458                        [
459                            Self::make_tiled_triangle_first(0),
460                            Self::make_tiled_triangle_second(0),
461                            Self::make_tiled_triangle_first(1),
462                            Self::make_tiled_triangle_second(1),
463                            Self::make_tiled_triangle_first(2),
464                            Self::make_tiled_triangle_second(2),
465                            Self::make_tiled_triangle_first(4),
466                            Self::make_tiled_triangle_second(4),
467                            Self::make_tiled_triangle_first(6),
468                            Self::make_tiled_triangle_second(6),
469                            Self::make_tiled_triangle_first(8),
470                            Self::make_tiled_triangle_second(8),
471                            Self::make_tiled_triangle_first(9),
472                            Self::make_tiled_triangle_second(9),
473                            Self::make_tiled_triangle_first(10),
474                            Self::make_tiled_triangle_second(10),
475                        ]
476                        .into_iter()
477                        .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_first(5)))
478                        .chain((!frame.frame_only).then(|| Self::make_tiled_triangle_second(5))),
479                    );
480                }
481            }
482        }
483    }
484
485    fn produce_debug_wireframe(&mut self, size: Vec2) {
486        if let Some(batch) = self.converter.convert(TesselateBatch::Debug) {
487            let rect = Rect {
488                left: 0.0,
489                right: size.x,
490                top: 0.0,
491                bottom: size.y,
492            };
493            let matrix = self.top_transform();
494            let tl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.top)));
495            let tr = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.top)));
496            let br = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.right, rect.bottom)));
497            let bl = vec2_to_raui(matrix.mul_point(vek::Vec2::new(rect.left, rect.bottom)));
498            self.stream.batch_optimized(batch);
499            self.stream.quad([
500                Self::make_vertex(tl, Default::default(), 0.0, Default::default()),
501                Self::make_vertex(tr, Default::default(), 0.0, Default::default()),
502                Self::make_vertex(br, Default::default(), 0.0, Default::default()),
503                Self::make_vertex(bl, Default::default(), 0.0, Default::default()),
504            ]);
505        }
506    }
507
508    fn render_node(
509        &mut self,
510        unit: &WidgetUnit,
511        mapping: &CoordsMapping,
512        layout: &Layout,
513        local: bool,
514    ) -> Result<(), Error> {
515        match unit {
516            WidgetUnit::None | WidgetUnit::PortalBox(_) => Ok(()),
517            WidgetUnit::AreaBox(unit) => {
518                if let Some(item) = layout.items.get(&unit.id) {
519                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
520                    self.push_transform_simple(local_space);
521                    self.render_node(&unit.slot, mapping, layout, true)?;
522                    self.pop_transform();
523                    Ok(())
524                } else {
525                    Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
526                }
527            }
528            WidgetUnit::ContentBox(unit) => {
529                if let Some(item) = layout.items.get(&unit.id) {
530                    let mut items = unit
531                        .items
532                        .iter()
533                        .map(|item| (item.layout.depth, item))
534                        .collect::<Vec<_>>();
535                    items.sort_unstable_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap());
536                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
537                    self.push_transform(&unit.transform, local_space);
538                    if unit.clipping {
539                        let size = local_space.size();
540                        let matrix = self.top_transform();
541                        let tl = matrix.mul_point(vek::Vec2::new(0.0, 0.0));
542                        let tr = matrix.mul_point(vek::Vec2::new(size.x, 0.0));
543                        let br = matrix.mul_point(vek::Vec2::new(size.x, size.y));
544                        let bl = matrix.mul_point(vek::Vec2::new(0.0, size.y));
545                        let x = tl.x.min(tr.x).min(br.x).min(bl.x).round();
546                        let y = tl.y.min(tr.y).min(br.y).min(bl.y).round();
547                        let x2 = tl.x.max(tr.x).max(br.x).max(bl.x).round();
548                        let y2 = tl.y.max(tr.y).max(br.y).max(bl.y).round();
549                        let w = x2 - x;
550                        let h = y2 - y;
551                        if let Some(batch) =
552                            self.converter
553                                .convert(TesselateBatch::ClipPush { x, y, w, h })
554                        {
555                            self.stream.batch(batch);
556                            self.stream.batch_end();
557                        }
558                    }
559                    for (_, item) in items {
560                        self.render_node(&item.slot, mapping, layout, true)?;
561                    }
562                    if unit.clipping
563                        && let Some(batch) = self.converter.convert(TesselateBatch::ClipPop)
564                    {
565                        self.stream.batch(batch);
566                        self.stream.batch_end();
567                    }
568                    self.pop_transform();
569                    Ok(())
570                } else {
571                    Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
572                }
573            }
574            WidgetUnit::FlexBox(unit) => {
575                if let Some(item) = layout.items.get(&unit.id) {
576                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
577                    self.push_transform(&unit.transform, local_space);
578                    for item in &unit.items {
579                        self.render_node(&item.slot, mapping, layout, true)?;
580                    }
581                    self.pop_transform();
582                    Ok(())
583                } else {
584                    Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
585                }
586            }
587            WidgetUnit::GridBox(unit) => {
588                if let Some(item) = layout.items.get(&unit.id) {
589                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
590                    self.push_transform(&unit.transform, local_space);
591                    for item in &unit.items {
592                        self.render_node(&item.slot, mapping, layout, true)?;
593                    }
594                    self.pop_transform();
595                    Ok(())
596                } else {
597                    Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
598                }
599            }
600            WidgetUnit::SizeBox(unit) => {
601                if let Some(item) = layout.items.get(&unit.id) {
602                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
603                    self.push_transform(&unit.transform, local_space);
604                    self.render_node(&unit.slot, mapping, layout, true)?;
605                    self.pop_transform();
606                    Ok(())
607                } else {
608                    Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
609                }
610            }
611            WidgetUnit::ImageBox(unit) => match &unit.material {
612                ImageBoxMaterial::Color(color) => {
613                    if let Some(item) = layout.items.get(&unit.id) {
614                        let local_space = mapping.virtual_to_real_rect(item.local_space, local);
615                        self.push_transform(&unit.transform, local_space);
616                        self.produce_color_triangles(local_space.size(), mapping.scale(), color);
617                        self.pop_transform();
618                        Ok(())
619                    } else {
620                        Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
621                    }
622                }
623                ImageBoxMaterial::Image(image) => {
624                    if let Some(item) = layout.items.get(&unit.id) {
625                        let local_space = mapping.virtual_to_real_rect(item.local_space, local);
626                        let rect = Rect {
627                            left: 0.0,
628                            right: local_space.width(),
629                            top: 0.0,
630                            bottom: local_space.height(),
631                        };
632                        let (id, uvs, size) = match self
633                            .provider
634                            .image_id_and_uv_and_size_by_atlas_id(&image.id)
635                        {
636                            Some(result) => result,
637                            None => return Err(Error::ImageNotFound(image.id.to_owned())),
638                        };
639                        let rect = if let Some(aspect) = unit.content_keep_aspect_ratio {
640                            let ox = rect.left;
641                            let oy = rect.top;
642                            let iw = rect.width();
643                            let ih = rect.height();
644                            let ra = size.x / size.y;
645                            let ia = iw / ih;
646                            let scale = if (ra >= ia) != aspect.outside {
647                                iw / size.x
648                            } else {
649                                ih / size.y
650                            };
651                            let w = size.x * scale;
652                            let h = size.y * scale;
653                            let ow = lerp(0.0, iw - w, aspect.horizontal_alignment);
654                            let oh = lerp(0.0, ih - h, aspect.vertical_alignment);
655                            Rect {
656                                left: ox + ow,
657                                right: ox + ow + w,
658                                top: oy + oh,
659                                bottom: oy + oh + h,
660                            }
661                        } else {
662                            rect
663                        };
664                        self.push_transform(&unit.transform, local_space);
665                        self.produce_image_triangles(id, uvs, size, rect, mapping.scale(), image);
666                        self.pop_transform();
667                        Ok(())
668                    } else {
669                        Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
670                    }
671                }
672                ImageBoxMaterial::Procedural(procedural) => {
673                    if let Some(item) = layout.items.get(&unit.id) {
674                        let local_space = mapping.virtual_to_real_rect(item.local_space, local);
675                        self.push_transform(&unit.transform, local_space);
676                        if let Some(batch) = self.converter.convert(TesselateBatch::Procedural {
677                            id: procedural.id.to_owned(),
678                            images: procedural.images.to_owned(),
679                            parameters: procedural.parameters.to_owned(),
680                        }) {
681                            let image_mapping =
682                                CoordsMapping::new_scaling(local_space, procedural.vertex_mapping);
683                            self.stream.batch_optimized(batch);
684                            match &procedural.mesh {
685                                ImageBoxProceduralMesh::Owned(mesh) => {
686                                    self.stream.extend(
687                                        mesh.vertices.iter().map(|vertex| {
688                                            Self::make_vertex(
689                                                image_mapping
690                                                    .virtual_to_real_vec2(vertex.position, false),
691                                                vertex.tex_coord,
692                                                vertex.page,
693                                                vertex.color,
694                                            )
695                                        }),
696                                        mesh.triangles.iter().map(|triangle| Triangle {
697                                            a: triangle[0],
698                                            b: triangle[1],
699                                            c: triangle[2],
700                                        }),
701                                    );
702                                }
703                                ImageBoxProceduralMesh::Shared(mesh) => {
704                                    self.stream.extend(
705                                        mesh.vertices.iter().map(|vertex| {
706                                            Self::make_vertex(
707                                                image_mapping
708                                                    .virtual_to_real_vec2(vertex.position, false),
709                                                vertex.tex_coord,
710                                                vertex.page,
711                                                vertex.color,
712                                            )
713                                        }),
714                                        mesh.triangles.iter().map(|triangle| Triangle {
715                                            a: triangle[0],
716                                            b: triangle[1],
717                                            c: triangle[2],
718                                        }),
719                                    );
720                                }
721                                ImageBoxProceduralMesh::Generator(generator) => {
722                                    let mesh = (generator)(local_space, &procedural.parameters);
723                                    self.stream.extend(
724                                        mesh.vertices.into_iter().map(|vertex| {
725                                            Self::make_vertex(
726                                                image_mapping
727                                                    .virtual_to_real_vec2(vertex.position, false),
728                                                vertex.tex_coord,
729                                                vertex.page,
730                                                vertex.color,
731                                            )
732                                        }),
733                                        mesh.triangles.into_iter().map(|triangle| Triangle {
734                                            a: triangle[0],
735                                            b: triangle[1],
736                                            c: triangle[2],
737                                        }),
738                                    );
739                                }
740                            }
741                        }
742                        self.pop_transform();
743                        Ok(())
744                    } else {
745                        Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
746                    }
747                }
748            },
749            WidgetUnit::TextBox(unit) => {
750                let font_index = match self.provider.font_index_by_id(&unit.font.name) {
751                    Some(index) => index,
752                    None => return Err(Error::FontNotFound(unit.font.name.to_owned())),
753                };
754                if let Some(item) = layout.items.get(&unit.id) {
755                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
756                    let size = local_space.size();
757                    self.push_transform(&unit.transform, local_space);
758                    let matrix = self.top_transform();
759                    if let Some(batch) = self.converter.convert(TesselateBatch::Text) {
760                        self.stream.batch_optimized(batch);
761                        self.stream.transformed(
762                            |stream| {
763                                let text = TextStyle::with_user_data(
764                                    &unit.text,
765                                    unit.font.size * mapping.scalar_scale(false),
766                                    font_index,
767                                    unit.color,
768                                );
769                                let mut layout = TextLayout::new(CoordinateSystem::PositiveYDown);
770                                layout.reset(&LayoutSettings {
771                                    max_width: Some(size.x),
772                                    max_height: Some(size.y),
773                                    horizontal_align: match unit.horizontal_align {
774                                        TextBoxHorizontalAlign::Left => HorizontalAlign::Left,
775                                        TextBoxHorizontalAlign::Center => HorizontalAlign::Center,
776                                        TextBoxHorizontalAlign::Right => HorizontalAlign::Right,
777                                    },
778                                    vertical_align: match unit.vertical_align {
779                                        TextBoxVerticalAlign::Top => VerticalAlign::Top,
780                                        TextBoxVerticalAlign::Middle => VerticalAlign::Middle,
781                                        TextBoxVerticalAlign::Bottom => VerticalAlign::Bottom,
782                                    },
783                                    ..Default::default()
784                                });
785                                layout.append(self.provider.fonts(), &text);
786                                self.text_renderer.include(self.provider.fonts(), &layout);
787                                self.text_renderer.render_to_stream(stream);
788                            },
789                            |vertex| {
790                                vertex.transform(matrix);
791                            },
792                        );
793                    }
794                    self.pop_transform();
795                    Ok(())
796                } else {
797                    Err(Error::WidgetHasNoLayout(unit.id.to_owned()))
798                }
799            }
800        }
801    }
802
803    fn debug_render_node(
804        &mut self,
805        unit: &WidgetUnit,
806        mapping: &CoordsMapping,
807        layout: &Layout,
808        local: bool,
809        debug: TessselateRendererDebug,
810    ) {
811        match unit {
812            WidgetUnit::AreaBox(unit) => {
813                if let Some(item) = layout.items.get(&unit.id) {
814                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
815                    self.push_transform_simple(local_space);
816                    self.debug_render_node(&unit.slot, mapping, layout, true, debug);
817                    if debug.render_non_visual_nodes {
818                        self.produce_debug_wireframe(local_space.size());
819                    }
820                    self.pop_transform();
821                }
822            }
823            WidgetUnit::ContentBox(unit) => {
824                if let Some(item) = layout.items.get(&unit.id) {
825                    let mut items = unit
826                        .items
827                        .iter()
828                        .map(|item| (item.layout.depth, item))
829                        .collect::<Vec<_>>();
830                    items.sort_unstable_by(|(a, _), (b, _)| a.partial_cmp(b).unwrap());
831                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
832                    self.push_transform(&unit.transform, local_space);
833                    for (_, item) in items {
834                        self.debug_render_node(&item.slot, mapping, layout, true, debug);
835                    }
836                    if debug.render_non_visual_nodes {
837                        self.produce_debug_wireframe(local_space.size());
838                    }
839                    self.pop_transform();
840                }
841            }
842            WidgetUnit::FlexBox(unit) => {
843                if let Some(item) = layout.items.get(&unit.id) {
844                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
845                    self.push_transform(&unit.transform, local_space);
846                    for item in &unit.items {
847                        self.debug_render_node(&item.slot, mapping, layout, true, debug);
848                    }
849                    if debug.render_non_visual_nodes {
850                        self.produce_debug_wireframe(local_space.size());
851                    }
852                    self.pop_transform();
853                }
854            }
855            WidgetUnit::GridBox(unit) => {
856                if let Some(item) = layout.items.get(&unit.id) {
857                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
858                    self.push_transform(&unit.transform, local_space);
859                    for item in &unit.items {
860                        self.debug_render_node(&item.slot, mapping, layout, true, debug);
861                    }
862                    if debug.render_non_visual_nodes {
863                        self.produce_debug_wireframe(local_space.size());
864                    }
865                    self.pop_transform();
866                }
867            }
868            WidgetUnit::SizeBox(unit) => {
869                if let Some(item) = layout.items.get(&unit.id) {
870                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
871                    self.push_transform(&unit.transform, local_space);
872                    self.debug_render_node(&unit.slot, mapping, layout, true, debug);
873                    if debug.render_non_visual_nodes {
874                        self.produce_debug_wireframe(local_space.size());
875                    }
876                    self.pop_transform();
877                }
878            }
879            WidgetUnit::ImageBox(unit) => {
880                if let Some(item) = layout.items.get(&unit.id) {
881                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
882                    self.push_transform(&unit.transform, local_space);
883                    self.produce_debug_wireframe(local_space.size());
884                    self.pop_transform();
885                }
886            }
887            WidgetUnit::TextBox(unit) => {
888                if let Some(item) = layout.items.get(&unit.id) {
889                    let local_space = mapping.virtual_to_real_rect(item.local_space, local);
890                    self.push_transform(&unit.transform, local_space);
891                    self.produce_debug_wireframe(local_space.size());
892                    self.pop_transform();
893                }
894            }
895            _ => {}
896        }
897    }
898}
899
900impl<V, B, P, C> Renderer<(), Error> for TesselateRenderer<'_, V, B, P, C>
901where
902    V: TesselateVertex + TextVertex<Color> + Default,
903    B: PartialEq,
904    C: TesselateBatchConverter<B>,
905    P: TesselateResourceProvider,
906{
907    fn render(
908        &mut self,
909        tree: &WidgetUnit,
910        mapping: &CoordsMapping,
911        layout: &Layout,
912    ) -> Result<(), Error> {
913        self.transform_stack.clear();
914        self.render_node(tree, mapping, layout, false)?;
915        self.stream.batch_end();
916        if let Some(debug) = self.debug {
917            self.transform_stack.clear();
918            self.debug_render_node(tree, mapping, layout, false, debug);
919            self.stream.batch_end();
920        }
921        Ok(())
922    }
923}
924
925fn raui_to_vec2(v: Vec2) -> vek::Vec2<Scalar> {
926    vek::Vec2::new(v.x, v.y)
927}
928
929fn vec2_to_raui(v: vek::Vec2<Scalar>) -> Vec2 {
930    Vec2 { x: v.x, y: v.y }
931}