1use 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
94pub 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 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}