iced_wgpu/
geometry.rs

1//! Build and draw geometry.
2use crate::core::text::LineHeight;
3use crate::core::{
4    self, Pixels, Point, Radians, Rectangle, Size, Svg, Transformation, Vector,
5};
6use crate::graphics::cache::{self, Cached};
7use crate::graphics::color;
8use crate::graphics::geometry::fill::{self, Fill};
9use crate::graphics::geometry::{
10    self, LineCap, LineDash, LineJoin, Path, Stroke, Style,
11};
12use crate::graphics::gradient::{self, Gradient};
13use crate::graphics::mesh::{self, Mesh};
14use crate::graphics::{Image, Text};
15use crate::text;
16use crate::triangle;
17
18use lyon::geom::euclid;
19use lyon::tessellation;
20
21use std::borrow::Cow;
22use std::sync::Arc;
23
24#[derive(Debug)]
25pub enum Geometry {
26    Live {
27        meshes: Vec<Mesh>,
28        images: Vec<Image>,
29        text: Vec<Text>,
30    },
31    Cached(Cache),
32}
33
34#[derive(Debug, Clone, Default)]
35pub struct Cache {
36    pub meshes: Option<triangle::Cache>,
37    pub images: Option<Arc<[Image]>>,
38    pub text: Option<text::Cache>,
39}
40
41impl Cached for Geometry {
42    type Cache = Cache;
43
44    fn load(cache: &Self::Cache) -> Self {
45        Geometry::Cached(cache.clone())
46    }
47
48    fn cache(
49        self,
50        group: cache::Group,
51        previous: Option<Self::Cache>,
52    ) -> Self::Cache {
53        match self {
54            Self::Live {
55                meshes,
56                images,
57                text,
58            } => {
59                let images = if images.is_empty() {
60                    None
61                } else {
62                    Some(Arc::from(images))
63                };
64
65                if let Some(mut previous) = previous {
66                    if let Some(cache) = &mut previous.meshes {
67                        cache.update(meshes);
68                    } else {
69                        previous.meshes = triangle::Cache::new(meshes);
70                    }
71
72                    if let Some(cache) = &mut previous.text {
73                        cache.update(text);
74                    } else {
75                        previous.text = text::Cache::new(group, text);
76                    }
77
78                    previous.images = images;
79
80                    previous
81                } else {
82                    Cache {
83                        meshes: triangle::Cache::new(meshes),
84                        images,
85                        text: text::Cache::new(group, text),
86                    }
87                }
88            }
89            Self::Cached(cache) => cache,
90        }
91    }
92}
93
94/// A frame for drawing some geometry.
95pub struct Frame {
96    clip_bounds: Rectangle,
97    buffers: BufferStack,
98    meshes: Vec<Mesh>,
99    images: Vec<Image>,
100    text: Vec<Text>,
101    transforms: Transforms,
102    fill_tessellator: tessellation::FillTessellator,
103    stroke_tessellator: tessellation::StrokeTessellator,
104}
105
106impl Frame {
107    /// Creates a new [`Frame`] with the given clip bounds.
108    pub fn new(bounds: Rectangle) -> Frame {
109        Frame {
110            clip_bounds: bounds,
111            buffers: BufferStack::new(),
112            meshes: Vec::new(),
113            images: Vec::new(),
114            text: Vec::new(),
115            transforms: Transforms {
116                previous: Vec::new(),
117                current: Transform(lyon::math::Transform::identity()),
118            },
119            fill_tessellator: tessellation::FillTessellator::new(),
120            stroke_tessellator: tessellation::StrokeTessellator::new(),
121        }
122    }
123}
124
125impl geometry::frame::Backend for Frame {
126    type Geometry = Geometry;
127
128    #[inline]
129    fn width(&self) -> f32 {
130        self.clip_bounds.width
131    }
132
133    #[inline]
134    fn height(&self) -> f32 {
135        self.clip_bounds.height
136    }
137
138    #[inline]
139    fn size(&self) -> Size {
140        self.clip_bounds.size()
141    }
142
143    #[inline]
144    fn center(&self) -> Point {
145        Point::new(self.clip_bounds.width / 2.0, self.clip_bounds.height / 2.0)
146    }
147
148    fn fill(&mut self, path: &Path, fill: impl Into<Fill>) {
149        let Fill { style, rule } = fill.into();
150
151        let mut buffer = self
152            .buffers
153            .get_fill(&self.transforms.current.transform_style(style));
154
155        let options = tessellation::FillOptions::default()
156            .with_fill_rule(into_fill_rule(rule));
157
158        if self.transforms.current.is_identity() {
159            self.fill_tessellator.tessellate_path(
160                path.raw(),
161                &options,
162                buffer.as_mut(),
163            )
164        } else {
165            let path = path.transform(&self.transforms.current.0);
166
167            self.fill_tessellator.tessellate_path(
168                path.raw(),
169                &options,
170                buffer.as_mut(),
171            )
172        }
173        .expect("Tessellate path.");
174    }
175
176    fn fill_rectangle(
177        &mut self,
178        top_left: Point,
179        size: Size,
180        fill: impl Into<Fill>,
181    ) {
182        let Fill { style, rule } = fill.into();
183
184        let mut buffer = self
185            .buffers
186            .get_fill(&self.transforms.current.transform_style(style));
187
188        let top_left = self
189            .transforms
190            .current
191            .0
192            .transform_point(lyon::math::Point::new(top_left.x, top_left.y));
193
194        let size =
195            self.transforms.current.0.transform_vector(
196                lyon::math::Vector::new(size.width, size.height),
197            );
198
199        let options = tessellation::FillOptions::default()
200            .with_fill_rule(into_fill_rule(rule));
201
202        self.fill_tessellator
203            .tessellate_rectangle(
204                &lyon::math::Box2D::new(top_left, top_left + size),
205                &options,
206                buffer.as_mut(),
207            )
208            .expect("Fill rectangle");
209    }
210
211    fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>) {
212        let stroke = stroke.into();
213
214        let mut buffer = self
215            .buffers
216            .get_stroke(&self.transforms.current.transform_style(stroke.style));
217
218        let mut options = tessellation::StrokeOptions::default();
219        options.line_width = stroke.width;
220        options.start_cap = into_line_cap(stroke.line_cap);
221        options.end_cap = into_line_cap(stroke.line_cap);
222        options.line_join = into_line_join(stroke.line_join);
223
224        let path = if stroke.line_dash.segments.is_empty() {
225            Cow::Borrowed(path)
226        } else {
227            Cow::Owned(dashed(path, stroke.line_dash))
228        };
229
230        if self.transforms.current.is_identity() {
231            self.stroke_tessellator.tessellate_path(
232                path.raw(),
233                &options,
234                buffer.as_mut(),
235            )
236        } else {
237            let path = path.transform(&self.transforms.current.0);
238
239            self.stroke_tessellator.tessellate_path(
240                path.raw(),
241                &options,
242                buffer.as_mut(),
243            )
244        }
245        .expect("Stroke path");
246    }
247
248    fn stroke_rectangle<'a>(
249        &mut self,
250        top_left: Point,
251        size: Size,
252        stroke: impl Into<Stroke<'a>>,
253    ) {
254        let stroke = stroke.into();
255
256        let mut buffer = self
257            .buffers
258            .get_stroke(&self.transforms.current.transform_style(stroke.style));
259
260        let top_left = self
261            .transforms
262            .current
263            .0
264            .transform_point(lyon::math::Point::new(top_left.x, top_left.y));
265
266        let size =
267            self.transforms.current.0.transform_vector(
268                lyon::math::Vector::new(size.width, size.height),
269            );
270
271        let mut options = tessellation::StrokeOptions::default();
272        options.line_width = stroke.width;
273        options.start_cap = into_line_cap(stroke.line_cap);
274        options.end_cap = into_line_cap(stroke.line_cap);
275        options.line_join = into_line_join(stroke.line_join);
276
277        self.stroke_tessellator
278            .tessellate_rectangle(
279                &lyon::math::Box2D::new(top_left, top_left + size),
280                &options,
281                buffer.as_mut(),
282            )
283            .expect("Stroke rectangle");
284    }
285
286    fn stroke_text<'a>(
287        &mut self,
288        text: impl Into<geometry::Text>,
289        stroke: impl Into<Stroke<'a>>,
290    ) {
291        let text = text.into();
292        let stroke = stroke.into();
293
294        text.draw_with(|glyph, _color| self.stroke(&glyph, stroke));
295    }
296
297    fn fill_text(&mut self, text: impl Into<geometry::Text>) {
298        let text = text.into();
299
300        let (scale_x, scale_y) = self.transforms.current.scale();
301
302        if self.transforms.current.is_scale_translation()
303            && scale_x == scale_y
304            && scale_x > 0.0
305            && scale_y > 0.0
306        {
307            let (bounds, size, line_height) =
308                if self.transforms.current.is_identity() {
309                    (
310                        Rectangle::new(
311                            text.position,
312                            Size::new(text.max_width, f32::INFINITY),
313                        ),
314                        text.size,
315                        text.line_height,
316                    )
317                } else {
318                    let position =
319                        self.transforms.current.transform_point(text.position);
320
321                    let size = Pixels(text.size.0 * scale_y);
322
323                    let line_height = match text.line_height {
324                        LineHeight::Absolute(size) => {
325                            LineHeight::Absolute(Pixels(size.0 * scale_y))
326                        }
327                        LineHeight::Relative(factor) => {
328                            LineHeight::Relative(factor)
329                        }
330                    };
331
332                    (
333                        Rectangle::new(
334                            position,
335                            Size::new(text.max_width, f32::INFINITY),
336                        ),
337                        size,
338                        line_height,
339                    )
340                };
341
342            self.text.push(Text::Cached {
343                content: text.content,
344                bounds,
345                color: text.color,
346                size,
347                line_height: line_height.to_absolute(size),
348                font: text.font,
349                align_x: text.align_x,
350                align_y: text.align_y,
351                shaping: text.shaping,
352                clip_bounds: self.clip_bounds,
353            });
354        } else {
355            text.draw_with(|path, color| self.fill(&path, color));
356        }
357    }
358
359    #[inline]
360    fn translate(&mut self, translation: Vector) {
361        self.transforms.current.0 =
362            self.transforms
363                .current
364                .0
365                .pre_translate(lyon::math::Vector::new(
366                    translation.x,
367                    translation.y,
368                ));
369    }
370
371    #[inline]
372    fn rotate(&mut self, angle: impl Into<Radians>) {
373        self.transforms.current.0 = self
374            .transforms
375            .current
376            .0
377            .pre_rotate(lyon::math::Angle::radians(angle.into().0));
378    }
379
380    #[inline]
381    fn scale(&mut self, scale: impl Into<f32>) {
382        let scale = scale.into();
383
384        self.scale_nonuniform(Vector { x: scale, y: scale });
385    }
386
387    #[inline]
388    fn scale_nonuniform(&mut self, scale: impl Into<Vector>) {
389        let scale = scale.into();
390
391        self.transforms.current.0 =
392            self.transforms.current.0.pre_scale(scale.x, scale.y);
393    }
394
395    fn push_transform(&mut self) {
396        self.transforms.previous.push(self.transforms.current);
397    }
398
399    fn pop_transform(&mut self) {
400        self.transforms.current = self.transforms.previous.pop().unwrap();
401    }
402
403    fn draft(&mut self, clip_bounds: Rectangle) -> Frame {
404        Frame::new(clip_bounds)
405    }
406
407    fn paste(&mut self, frame: Frame) {
408        self.meshes.extend(frame.meshes);
409        self.meshes
410            .extend(frame.buffers.into_meshes(frame.clip_bounds));
411
412        self.images.extend(frame.images);
413        self.text.extend(frame.text);
414    }
415
416    fn into_geometry(mut self) -> Self::Geometry {
417        self.meshes
418            .extend(self.buffers.into_meshes(self.clip_bounds));
419
420        Geometry::Live {
421            meshes: self.meshes,
422            images: self.images,
423            text: self.text,
424        }
425    }
426
427    fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) {
428        let mut image = image.into();
429
430        let (bounds, external_rotation) =
431            self.transforms.current.transform_rectangle(bounds);
432
433        image.rotation += external_rotation;
434
435        self.images.push(Image::Raster(image, bounds));
436    }
437
438    fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) {
439        let mut svg = svg.into();
440
441        let (bounds, external_rotation) =
442            self.transforms.current.transform_rectangle(bounds);
443
444        svg.rotation += external_rotation;
445
446        self.images.push(Image::Vector(svg, bounds));
447    }
448}
449
450enum Buffer {
451    Solid(tessellation::VertexBuffers<mesh::SolidVertex2D, u32>),
452    Gradient(tessellation::VertexBuffers<mesh::GradientVertex2D, u32>),
453}
454
455struct BufferStack {
456    stack: Vec<Buffer>,
457}
458
459impl BufferStack {
460    fn new() -> Self {
461        Self { stack: Vec::new() }
462    }
463
464    fn get_mut(&mut self, style: &Style) -> &mut Buffer {
465        match style {
466            Style::Solid(_) => match self.stack.last() {
467                Some(Buffer::Solid(_)) => {}
468                _ => {
469                    self.stack.push(Buffer::Solid(
470                        tessellation::VertexBuffers::new(),
471                    ));
472                }
473            },
474            Style::Gradient(_) => match self.stack.last() {
475                Some(Buffer::Gradient(_)) => {}
476                _ => {
477                    self.stack.push(Buffer::Gradient(
478                        tessellation::VertexBuffers::new(),
479                    ));
480                }
481            },
482        }
483
484        self.stack.last_mut().unwrap()
485    }
486
487    fn get_fill<'a>(
488        &'a mut self,
489        style: &Style,
490    ) -> Box<dyn tessellation::FillGeometryBuilder + 'a> {
491        match (style, self.get_mut(style)) {
492            (Style::Solid(color), Buffer::Solid(buffer)) => {
493                Box::new(tessellation::BuffersBuilder::new(
494                    buffer,
495                    TriangleVertex2DBuilder(color::pack(*color)),
496                ))
497            }
498            (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
499                Box::new(tessellation::BuffersBuilder::new(
500                    buffer,
501                    GradientVertex2DBuilder {
502                        gradient: gradient.pack(),
503                    },
504                ))
505            }
506            _ => unreachable!(),
507        }
508    }
509
510    fn get_stroke<'a>(
511        &'a mut self,
512        style: &Style,
513    ) -> Box<dyn tessellation::StrokeGeometryBuilder + 'a> {
514        match (style, self.get_mut(style)) {
515            (Style::Solid(color), Buffer::Solid(buffer)) => {
516                Box::new(tessellation::BuffersBuilder::new(
517                    buffer,
518                    TriangleVertex2DBuilder(color::pack(*color)),
519                ))
520            }
521            (Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
522                Box::new(tessellation::BuffersBuilder::new(
523                    buffer,
524                    GradientVertex2DBuilder {
525                        gradient: gradient.pack(),
526                    },
527                ))
528            }
529            _ => unreachable!(),
530        }
531    }
532
533    fn into_meshes(self, clip_bounds: Rectangle) -> impl Iterator<Item = Mesh> {
534        self.stack
535            .into_iter()
536            .filter_map(move |buffer| match buffer {
537                Buffer::Solid(buffer) if !buffer.indices.is_empty() => {
538                    Some(Mesh::Solid {
539                        buffers: mesh::Indexed {
540                            vertices: buffer.vertices,
541                            indices: buffer.indices,
542                        },
543                        clip_bounds,
544                        transformation: Transformation::IDENTITY,
545                    })
546                }
547                Buffer::Gradient(buffer) if !buffer.indices.is_empty() => {
548                    Some(Mesh::Gradient {
549                        buffers: mesh::Indexed {
550                            vertices: buffer.vertices,
551                            indices: buffer.indices,
552                        },
553                        clip_bounds,
554                        transformation: Transformation::IDENTITY,
555                    })
556                }
557                _ => None,
558            })
559    }
560}
561
562#[derive(Debug)]
563struct Transforms {
564    previous: Vec<Transform>,
565    current: Transform,
566}
567
568#[derive(Debug, Clone, Copy)]
569struct Transform(lyon::math::Transform);
570
571impl Transform {
572    fn is_identity(&self) -> bool {
573        self.0 == lyon::math::Transform::identity()
574    }
575
576    fn is_scale_translation(&self) -> bool {
577        self.0.m12.abs() < 2.0 * f32::EPSILON
578            && self.0.m21.abs() < 2.0 * f32::EPSILON
579    }
580
581    fn scale(&self) -> (f32, f32) {
582        (self.0.m11, self.0.m22)
583    }
584
585    fn transform_point(&self, point: Point) -> Point {
586        let transformed = self
587            .0
588            .transform_point(euclid::Point2D::new(point.x, point.y));
589
590        Point {
591            x: transformed.x,
592            y: transformed.y,
593        }
594    }
595
596    fn transform_style(&self, style: Style) -> Style {
597        match style {
598            Style::Solid(color) => Style::Solid(color),
599            Style::Gradient(gradient) => {
600                Style::Gradient(self.transform_gradient(gradient))
601            }
602        }
603    }
604
605    fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
606        match &mut gradient {
607            Gradient::Linear(linear) => {
608                linear.start = self.transform_point(linear.start);
609                linear.end = self.transform_point(linear.end);
610            }
611        }
612
613        gradient
614    }
615
616    fn transform_rectangle(
617        &self,
618        rectangle: Rectangle,
619    ) -> (Rectangle, Radians) {
620        let top_left = self.transform_point(rectangle.position());
621        let top_right = self.transform_point(
622            rectangle.position() + Vector::new(rectangle.width, 0.0),
623        );
624        let bottom_left = self.transform_point(
625            rectangle.position() + Vector::new(0.0, rectangle.height),
626        );
627
628        Rectangle::with_vertices(top_left, top_right, bottom_left)
629    }
630}
631struct GradientVertex2DBuilder {
632    gradient: gradient::Packed,
633}
634
635impl tessellation::FillVertexConstructor<mesh::GradientVertex2D>
636    for GradientVertex2DBuilder
637{
638    fn new_vertex(
639        &mut self,
640        vertex: tessellation::FillVertex<'_>,
641    ) -> mesh::GradientVertex2D {
642        let position = vertex.position();
643
644        mesh::GradientVertex2D {
645            position: [position.x, position.y],
646            gradient: self.gradient,
647        }
648    }
649}
650
651impl tessellation::StrokeVertexConstructor<mesh::GradientVertex2D>
652    for GradientVertex2DBuilder
653{
654    fn new_vertex(
655        &mut self,
656        vertex: tessellation::StrokeVertex<'_, '_>,
657    ) -> mesh::GradientVertex2D {
658        let position = vertex.position();
659
660        mesh::GradientVertex2D {
661            position: [position.x, position.y],
662            gradient: self.gradient,
663        }
664    }
665}
666
667struct TriangleVertex2DBuilder(color::Packed);
668
669impl tessellation::FillVertexConstructor<mesh::SolidVertex2D>
670    for TriangleVertex2DBuilder
671{
672    fn new_vertex(
673        &mut self,
674        vertex: tessellation::FillVertex<'_>,
675    ) -> mesh::SolidVertex2D {
676        let position = vertex.position();
677
678        mesh::SolidVertex2D {
679            position: [position.x, position.y],
680            color: self.0,
681        }
682    }
683}
684
685impl tessellation::StrokeVertexConstructor<mesh::SolidVertex2D>
686    for TriangleVertex2DBuilder
687{
688    fn new_vertex(
689        &mut self,
690        vertex: tessellation::StrokeVertex<'_, '_>,
691    ) -> mesh::SolidVertex2D {
692        let position = vertex.position();
693
694        mesh::SolidVertex2D {
695            position: [position.x, position.y],
696            color: self.0,
697        }
698    }
699}
700
701fn into_line_join(line_join: LineJoin) -> lyon::tessellation::LineJoin {
702    match line_join {
703        LineJoin::Miter => lyon::tessellation::LineJoin::Miter,
704        LineJoin::Round => lyon::tessellation::LineJoin::Round,
705        LineJoin::Bevel => lyon::tessellation::LineJoin::Bevel,
706    }
707}
708
709fn into_line_cap(line_cap: LineCap) -> lyon::tessellation::LineCap {
710    match line_cap {
711        LineCap::Butt => lyon::tessellation::LineCap::Butt,
712        LineCap::Square => lyon::tessellation::LineCap::Square,
713        LineCap::Round => lyon::tessellation::LineCap::Round,
714    }
715}
716
717fn into_fill_rule(rule: fill::Rule) -> lyon::tessellation::FillRule {
718    match rule {
719        fill::Rule::NonZero => lyon::tessellation::FillRule::NonZero,
720        fill::Rule::EvenOdd => lyon::tessellation::FillRule::EvenOdd,
721    }
722}
723
724pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path {
725    use lyon::algorithms::walk::{
726        RepeatedPattern, WalkerEvent, walk_along_path,
727    };
728    use lyon::path::iterator::PathIterator;
729
730    Path::new(|builder| {
731        let segments_odd = (line_dash.segments.len() % 2 == 1)
732            .then(|| [line_dash.segments, line_dash.segments].concat());
733
734        let mut draw_line = false;
735
736        walk_along_path(
737            path.raw().iter().flattened(
738                lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
739            ),
740            0.0,
741            lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
742            &mut RepeatedPattern {
743                callback: |event: WalkerEvent<'_>| {
744                    let point = Point {
745                        x: event.position.x,
746                        y: event.position.y,
747                    };
748
749                    if draw_line {
750                        builder.line_to(point);
751                    } else {
752                        builder.move_to(point);
753                    }
754
755                    draw_line = !draw_line;
756
757                    true
758                },
759                index: line_dash.offset,
760                intervals: segments_odd
761                    .as_deref()
762                    .unwrap_or(line_dash.segments),
763            },
764        );
765    })
766}