epaint/
shape.rs

1//! The different shapes that can be painted.
2
3use std::{any::Any, sync::Arc};
4
5use crate::{
6    stroke::PathStroke,
7    text::{FontId, Fonts, Galley},
8    Color32, Mesh, Stroke, TextureId,
9};
10use emath::{pos2, Align2, Pos2, Rangef, Rect, TSTransform, Vec2};
11
12pub use crate::{CubicBezierShape, QuadraticBezierShape};
13
14/// A paint primitive such as a circle or a piece of text.
15/// Coordinates are all screen space points (not physical pixels).
16///
17/// You should generally recreate your [`Shape`]s each frame,
18/// but storing them should also be fine with one exception:
19/// [`Shape::Text`] depends on the current `pixels_per_point` (dpi scale)
20/// and so must be recreated every time `pixels_per_point` changes.
21#[must_use = "Add a Shape to a Painter"]
22#[derive(Clone, Debug, PartialEq)]
23pub enum Shape {
24    /// Paint nothing. This can be useful as a placeholder.
25    Noop,
26
27    /// Recursively nest more shapes - sometimes a convenience to be able to do.
28    /// For performance reasons it is better to avoid it.
29    Vec(Vec<Shape>),
30
31    /// Circle with optional outline and fill.
32    Circle(CircleShape),
33
34    /// Ellipse with optional outline and fill.
35    Ellipse(EllipseShape),
36
37    /// A line between two points.
38    LineSegment {
39        points: [Pos2; 2],
40        stroke: PathStroke,
41    },
42
43    /// A series of lines between points.
44    /// The path can have a stroke and/or fill (if closed).
45    Path(PathShape),
46
47    /// Rectangle with optional outline and fill.
48    Rect(RectShape),
49
50    /// Text.
51    ///
52    /// This needs to be recreated if `pixels_per_point` (dpi scale) changes.
53    Text(TextShape),
54
55    /// A general triangle mesh.
56    ///
57    /// Can be used to display images.
58    Mesh(Mesh),
59
60    /// A quadratic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
61    QuadraticBezier(QuadraticBezierShape),
62
63    /// A cubic [Bézier Curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve).
64    CubicBezier(CubicBezierShape),
65
66    /// Backend-specific painting.
67    Callback(PaintCallback),
68}
69
70#[test]
71fn shape_impl_send_sync() {
72    fn assert_send_sync<T: Send + Sync>() {}
73    assert_send_sync::<Shape>();
74}
75
76impl From<Vec<Self>> for Shape {
77    #[inline(always)]
78    fn from(shapes: Vec<Self>) -> Self {
79        Self::Vec(shapes)
80    }
81}
82
83impl From<Mesh> for Shape {
84    #[inline(always)]
85    fn from(mesh: Mesh) -> Self {
86        Self::Mesh(mesh)
87    }
88}
89
90/// ## Constructors
91impl Shape {
92    /// A line between two points.
93    /// More efficient than calling [`Self::line`].
94    #[inline]
95    pub fn line_segment(points: [Pos2; 2], stroke: impl Into<PathStroke>) -> Self {
96        Self::LineSegment {
97            points,
98            stroke: stroke.into(),
99        }
100    }
101
102    /// A horizontal line.
103    pub fn hline(x: impl Into<Rangef>, y: f32, stroke: impl Into<PathStroke>) -> Self {
104        let x = x.into();
105        Self::LineSegment {
106            points: [pos2(x.min, y), pos2(x.max, y)],
107            stroke: stroke.into(),
108        }
109    }
110
111    /// A vertical line.
112    pub fn vline(x: f32, y: impl Into<Rangef>, stroke: impl Into<PathStroke>) -> Self {
113        let y = y.into();
114        Self::LineSegment {
115            points: [pos2(x, y.min), pos2(x, y.max)],
116            stroke: stroke.into(),
117        }
118    }
119
120    /// A line through many points.
121    ///
122    /// Use [`Self::line_segment`] instead if your line only connects two points.
123    #[inline]
124    pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
125        Self::Path(PathShape::line(points, stroke))
126    }
127
128    /// A line that closes back to the start point again.
129    #[inline]
130    pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
131        Self::Path(PathShape::closed_line(points, stroke))
132    }
133
134    /// Turn a line into equally spaced dots.
135    pub fn dotted_line(
136        path: &[Pos2],
137        color: impl Into<Color32>,
138        spacing: f32,
139        radius: f32,
140    ) -> Vec<Self> {
141        let mut shapes = Vec::new();
142        points_from_line(path, spacing, radius, color.into(), &mut shapes);
143        shapes
144    }
145
146    /// Turn a line into dashes.
147    pub fn dashed_line(
148        path: &[Pos2],
149        stroke: impl Into<Stroke>,
150        dash_length: f32,
151        gap_length: f32,
152    ) -> Vec<Self> {
153        let mut shapes = Vec::new();
154        dashes_from_line(
155            path,
156            stroke.into(),
157            &[dash_length],
158            &[gap_length],
159            &mut shapes,
160            0.,
161        );
162        shapes
163    }
164
165    /// Turn a line into dashes with different dash/gap lengths and a start offset.
166    pub fn dashed_line_with_offset(
167        path: &[Pos2],
168        stroke: impl Into<Stroke>,
169        dash_lengths: &[f32],
170        gap_lengths: &[f32],
171        dash_offset: f32,
172    ) -> Vec<Self> {
173        let mut shapes = Vec::new();
174        dashes_from_line(
175            path,
176            stroke.into(),
177            dash_lengths,
178            gap_lengths,
179            &mut shapes,
180            dash_offset,
181        );
182        shapes
183    }
184
185    /// Turn a line into dashes. If you need to create many dashed lines use this instead of
186    /// [`Self::dashed_line`].
187    pub fn dashed_line_many(
188        points: &[Pos2],
189        stroke: impl Into<Stroke>,
190        dash_length: f32,
191        gap_length: f32,
192        shapes: &mut Vec<Self>,
193    ) {
194        dashes_from_line(
195            points,
196            stroke.into(),
197            &[dash_length],
198            &[gap_length],
199            shapes,
200            0.,
201        );
202    }
203
204    /// Turn a line into dashes with different dash/gap lengths and a start offset. If you need to
205    /// create many dashed lines use this instead of [`Self::dashed_line_with_offset`].
206    pub fn dashed_line_many_with_offset(
207        points: &[Pos2],
208        stroke: impl Into<Stroke>,
209        dash_lengths: &[f32],
210        gap_lengths: &[f32],
211        dash_offset: f32,
212        shapes: &mut Vec<Self>,
213    ) {
214        dashes_from_line(
215            points,
216            stroke.into(),
217            dash_lengths,
218            gap_lengths,
219            shapes,
220            dash_offset,
221        );
222    }
223
224    /// A convex polygon with a fill and optional stroke.
225    ///
226    /// The most performant winding order is clockwise.
227    #[inline]
228    pub fn convex_polygon(
229        points: Vec<Pos2>,
230        fill: impl Into<Color32>,
231        stroke: impl Into<PathStroke>,
232    ) -> Self {
233        Self::Path(PathShape::convex_polygon(points, fill, stroke))
234    }
235
236    #[inline]
237    pub fn circle_filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
238        Self::Circle(CircleShape::filled(center, radius, fill_color))
239    }
240
241    #[inline]
242    pub fn circle_stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
243        Self::Circle(CircleShape::stroke(center, radius, stroke))
244    }
245
246    #[inline]
247    pub fn ellipse_filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {
248        Self::Ellipse(EllipseShape::filled(center, radius, fill_color))
249    }
250
251    #[inline]
252    pub fn ellipse_stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {
253        Self::Ellipse(EllipseShape::stroke(center, radius, stroke))
254    }
255
256    #[inline]
257    pub fn rect_filled(
258        rect: Rect,
259        rounding: impl Into<Rounding>,
260        fill_color: impl Into<Color32>,
261    ) -> Self {
262        Self::Rect(RectShape::filled(rect, rounding, fill_color))
263    }
264
265    #[inline]
266    pub fn rect_stroke(
267        rect: Rect,
268        rounding: impl Into<Rounding>,
269        stroke: impl Into<Stroke>,
270    ) -> Self {
271        Self::Rect(RectShape::stroke(rect, rounding, stroke))
272    }
273
274    #[allow(clippy::needless_pass_by_value)]
275    pub fn text(
276        fonts: &Fonts,
277        pos: Pos2,
278        anchor: Align2,
279        text: impl ToString,
280        font_id: FontId,
281        color: Color32,
282    ) -> Self {
283        let galley = fonts.layout_no_wrap(text.to_string(), font_id, color);
284        let rect = anchor.anchor_size(pos, galley.size());
285        Self::galley(rect.min, galley, color)
286    }
287
288    /// Any uncolored parts of the [`Galley`] (using [`Color32::PLACEHOLDER`]) will be replaced with the given color.
289    ///
290    /// Any non-placeholder color in the galley takes precedence over this fallback color.
291    #[inline]
292    pub fn galley(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {
293        TextShape::new(pos, galley, fallback_color).into()
294    }
295
296    /// All text color in the [`Galley`] will be replaced with the given color.
297    #[inline]
298    pub fn galley_with_override_text_color(
299        pos: Pos2,
300        galley: Arc<Galley>,
301        text_color: Color32,
302    ) -> Self {
303        TextShape::new(pos, galley, text_color)
304            .with_override_text_color(text_color)
305            .into()
306    }
307
308    #[inline]
309    #[deprecated = "Use `Shape::galley` or `Shape::galley_with_override_text_color` instead"]
310    pub fn galley_with_color(pos: Pos2, galley: Arc<Galley>, text_color: Color32) -> Self {
311        Self::galley_with_override_text_color(pos, galley, text_color)
312    }
313
314    #[inline]
315    pub fn mesh(mesh: Mesh) -> Self {
316        debug_assert!(mesh.is_valid());
317        Self::Mesh(mesh)
318    }
319
320    /// An image at the given position.
321    ///
322    /// `uv` should normally be `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`
323    /// unless you want to crop or flip the image.
324    ///
325    /// `tint` is a color multiplier. Use [`Color32::WHITE`] if you don't want to tint the image.
326    pub fn image(texture_id: TextureId, rect: Rect, uv: Rect, tint: Color32) -> Self {
327        let mut mesh = Mesh::with_texture(texture_id);
328        mesh.add_rect_with_uv(rect, uv, tint);
329        Self::mesh(mesh)
330    }
331
332    /// The visual bounding rectangle (includes stroke widths)
333    pub fn visual_bounding_rect(&self) -> Rect {
334        match self {
335            Self::Noop => Rect::NOTHING,
336            Self::Vec(shapes) => {
337                let mut rect = Rect::NOTHING;
338                for shape in shapes {
339                    rect = rect.union(shape.visual_bounding_rect());
340                }
341                rect
342            }
343            Self::Circle(circle_shape) => circle_shape.visual_bounding_rect(),
344            Self::Ellipse(ellipse_shape) => ellipse_shape.visual_bounding_rect(),
345            Self::LineSegment { points, stroke } => {
346                if stroke.is_empty() {
347                    Rect::NOTHING
348                } else {
349                    Rect::from_two_pos(points[0], points[1]).expand(stroke.width / 2.0)
350                }
351            }
352            Self::Path(path_shape) => path_shape.visual_bounding_rect(),
353            Self::Rect(rect_shape) => rect_shape.visual_bounding_rect(),
354            Self::Text(text_shape) => text_shape.visual_bounding_rect(),
355            Self::Mesh(mesh) => mesh.calc_bounds(),
356            Self::QuadraticBezier(bezier) => bezier.visual_bounding_rect(),
357            Self::CubicBezier(bezier) => bezier.visual_bounding_rect(),
358            Self::Callback(custom) => custom.rect,
359        }
360    }
361}
362
363/// ## Inspection and transforms
364impl Shape {
365    #[inline(always)]
366    pub fn texture_id(&self) -> super::TextureId {
367        if let Self::Mesh(mesh) = self {
368            mesh.texture_id
369        } else if let Self::Rect(rect_shape) = self {
370            rect_shape.fill_texture_id
371        } else {
372            super::TextureId::default()
373        }
374    }
375
376    /// Scale the shape by `factor`, in-place.
377    ///
378    /// A wrapper around [`Self::transform`].
379    #[inline(always)]
380    pub fn scale(&mut self, factor: f32) {
381        self.transform(TSTransform::from_scaling(factor));
382    }
383
384    /// Move the shape by `delta`, in-place.
385    ///
386    /// A wrapper around [`Self::transform`].
387    #[inline(always)]
388    pub fn translate(&mut self, delta: Vec2) {
389        self.transform(TSTransform::from_translation(delta));
390    }
391
392    /// Move the shape by this many points, in-place.
393    ///
394    /// If using a [`PaintCallback`], note that only the rect is scaled as opposed
395    /// to other shapes where the stroke is also scaled.
396    pub fn transform(&mut self, transform: TSTransform) {
397        match self {
398            Self::Noop => {}
399            Self::Vec(shapes) => {
400                for shape in shapes {
401                    shape.transform(transform);
402                }
403            }
404            Self::Circle(circle_shape) => {
405                circle_shape.center = transform * circle_shape.center;
406                circle_shape.radius *= transform.scaling;
407                circle_shape.stroke.width *= transform.scaling;
408            }
409            Self::Ellipse(ellipse_shape) => {
410                ellipse_shape.center = transform * ellipse_shape.center;
411                ellipse_shape.radius *= transform.scaling;
412                ellipse_shape.stroke.width *= transform.scaling;
413            }
414            Self::LineSegment { points, stroke } => {
415                for p in points {
416                    *p = transform * *p;
417                }
418                stroke.width *= transform.scaling;
419            }
420            Self::Path(path_shape) => {
421                for p in &mut path_shape.points {
422                    *p = transform * *p;
423                }
424                path_shape.stroke.width *= transform.scaling;
425            }
426            Self::Rect(rect_shape) => {
427                rect_shape.rect = transform * rect_shape.rect;
428                rect_shape.stroke.width *= transform.scaling;
429                rect_shape.rounding *= transform.scaling;
430            }
431            Self::Text(text_shape) => {
432                text_shape.pos = transform * text_shape.pos;
433
434                // Scale text:
435                let galley = Arc::make_mut(&mut text_shape.galley);
436                for row in &mut galley.rows {
437                    row.visuals.mesh_bounds = transform.scaling * row.visuals.mesh_bounds;
438                    for v in &mut row.visuals.mesh.vertices {
439                        v.pos = Pos2::new(transform.scaling * v.pos.x, transform.scaling * v.pos.y);
440                    }
441                }
442
443                galley.mesh_bounds = transform.scaling * galley.mesh_bounds;
444                galley.rect = transform.scaling * galley.rect;
445            }
446            Self::Mesh(mesh) => {
447                mesh.transform(transform);
448            }
449            Self::QuadraticBezier(bezier_shape) => {
450                bezier_shape.points[0] = transform * bezier_shape.points[0];
451                bezier_shape.points[1] = transform * bezier_shape.points[1];
452                bezier_shape.points[2] = transform * bezier_shape.points[2];
453                bezier_shape.stroke.width *= transform.scaling;
454            }
455            Self::CubicBezier(cubic_curve) => {
456                for p in &mut cubic_curve.points {
457                    *p = transform * *p;
458                }
459                cubic_curve.stroke.width *= transform.scaling;
460            }
461            Self::Callback(shape) => {
462                shape.rect = transform * shape.rect;
463            }
464        }
465    }
466}
467
468// ----------------------------------------------------------------------------
469
470/// How to paint a circle.
471#[derive(Copy, Clone, Debug, PartialEq)]
472#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
473pub struct CircleShape {
474    pub center: Pos2,
475    pub radius: f32,
476    pub fill: Color32,
477    pub stroke: Stroke,
478}
479
480impl CircleShape {
481    #[inline]
482    pub fn filled(center: Pos2, radius: f32, fill_color: impl Into<Color32>) -> Self {
483        Self {
484            center,
485            radius,
486            fill: fill_color.into(),
487            stroke: Default::default(),
488        }
489    }
490
491    #[inline]
492    pub fn stroke(center: Pos2, radius: f32, stroke: impl Into<Stroke>) -> Self {
493        Self {
494            center,
495            radius,
496            fill: Default::default(),
497            stroke: stroke.into(),
498        }
499    }
500
501    /// The visual bounding rectangle (includes stroke width)
502    pub fn visual_bounding_rect(&self) -> Rect {
503        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
504            Rect::NOTHING
505        } else {
506            Rect::from_center_size(
507                self.center,
508                Vec2::splat(self.radius * 2.0 + self.stroke.width),
509            )
510        }
511    }
512}
513
514impl From<CircleShape> for Shape {
515    #[inline(always)]
516    fn from(shape: CircleShape) -> Self {
517        Self::Circle(shape)
518    }
519}
520
521// ----------------------------------------------------------------------------
522
523/// How to paint an ellipse.
524#[derive(Copy, Clone, Debug, PartialEq)]
525#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
526pub struct EllipseShape {
527    pub center: Pos2,
528
529    /// Radius is the vector (a, b) where the width of the Ellipse is 2a and the height is 2b
530    pub radius: Vec2,
531    pub fill: Color32,
532    pub stroke: Stroke,
533}
534
535impl EllipseShape {
536    #[inline]
537    pub fn filled(center: Pos2, radius: Vec2, fill_color: impl Into<Color32>) -> Self {
538        Self {
539            center,
540            radius,
541            fill: fill_color.into(),
542            stroke: Default::default(),
543        }
544    }
545
546    #[inline]
547    pub fn stroke(center: Pos2, radius: Vec2, stroke: impl Into<Stroke>) -> Self {
548        Self {
549            center,
550            radius,
551            fill: Default::default(),
552            stroke: stroke.into(),
553        }
554    }
555
556    /// The visual bounding rectangle (includes stroke width)
557    pub fn visual_bounding_rect(&self) -> Rect {
558        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
559            Rect::NOTHING
560        } else {
561            Rect::from_center_size(
562                self.center,
563                self.radius * 2.0 + Vec2::splat(self.stroke.width),
564            )
565        }
566    }
567}
568
569impl From<EllipseShape> for Shape {
570    #[inline(always)]
571    fn from(shape: EllipseShape) -> Self {
572        Self::Ellipse(shape)
573    }
574}
575
576// ----------------------------------------------------------------------------
577
578/// A path which can be stroked and/or filled (if closed).
579#[derive(Clone, Debug, PartialEq)]
580#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
581pub struct PathShape {
582    /// Filled paths should prefer clockwise order.
583    pub points: Vec<Pos2>,
584
585    /// If true, connect the first and last of the points together.
586    /// This is required if `fill != TRANSPARENT`.
587    pub closed: bool,
588
589    /// Fill is only supported for convex polygons.
590    pub fill: Color32,
591
592    /// Color and thickness of the line.
593    pub stroke: PathStroke,
594    // TODO(emilk): Add texture support either by supplying uv for each point,
595    // or by some transform from points to uv (e.g. a callback or a linear transform matrix).
596}
597
598impl PathShape {
599    /// A line through many points.
600    ///
601    /// Use [`Shape::line_segment`] instead if your line only connects two points.
602    #[inline]
603    pub fn line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
604        Self {
605            points,
606            closed: false,
607            fill: Default::default(),
608            stroke: stroke.into(),
609        }
610    }
611
612    /// A line that closes back to the start point again.
613    #[inline]
614    pub fn closed_line(points: Vec<Pos2>, stroke: impl Into<PathStroke>) -> Self {
615        Self {
616            points,
617            closed: true,
618            fill: Default::default(),
619            stroke: stroke.into(),
620        }
621    }
622
623    /// A convex polygon with a fill and optional stroke.
624    ///
625    /// The most performant winding order is clockwise.
626    #[inline]
627    pub fn convex_polygon(
628        points: Vec<Pos2>,
629        fill: impl Into<Color32>,
630        stroke: impl Into<PathStroke>,
631    ) -> Self {
632        Self {
633            points,
634            closed: true,
635            fill: fill.into(),
636            stroke: stroke.into(),
637        }
638    }
639
640    /// The visual bounding rectangle (includes stroke width)
641    #[inline]
642    pub fn visual_bounding_rect(&self) -> Rect {
643        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
644            Rect::NOTHING
645        } else {
646            Rect::from_points(&self.points).expand(self.stroke.width / 2.0)
647        }
648    }
649}
650
651impl From<PathShape> for Shape {
652    #[inline(always)]
653    fn from(shape: PathShape) -> Self {
654        Self::Path(shape)
655    }
656}
657
658// ----------------------------------------------------------------------------
659
660/// How to paint a rectangle.
661#[derive(Copy, Clone, Debug, PartialEq)]
662#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
663pub struct RectShape {
664    pub rect: Rect,
665
666    /// How rounded the corners are. Use `Rounding::ZERO` for no rounding.
667    pub rounding: Rounding,
668
669    /// How to fill the rectangle.
670    pub fill: Color32,
671
672    /// The thickness and color of the outline.
673    pub stroke: Stroke,
674
675    /// If larger than zero, the edges of the rectangle
676    /// (for both fill and stroke) will be blurred.
677    ///
678    /// This can be used to produce shadows and glow effects.
679    ///
680    /// The blur is currently implemented using a simple linear blur in sRGBA gamma space.
681    pub blur_width: f32,
682
683    /// If the rect should be filled with a texture, which one?
684    ///
685    /// The texture is multiplied with [`Self::fill`].
686    pub fill_texture_id: TextureId,
687
688    /// What UV coordinates to use for the texture?
689    ///
690    /// To display a texture, set [`Self::fill_texture_id`],
691    /// and set this to `Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0))`.
692    ///
693    /// Use [`Rect::ZERO`] to turn off texturing.
694    pub uv: Rect,
695}
696
697impl RectShape {
698    #[inline]
699    pub fn new(
700        rect: Rect,
701        rounding: impl Into<Rounding>,
702        fill_color: impl Into<Color32>,
703        stroke: impl Into<Stroke>,
704    ) -> Self {
705        Self {
706            rect,
707            rounding: rounding.into(),
708            fill: fill_color.into(),
709            stroke: stroke.into(),
710            blur_width: 0.0,
711            fill_texture_id: Default::default(),
712            uv: Rect::ZERO,
713        }
714    }
715
716    #[inline]
717    pub fn filled(
718        rect: Rect,
719        rounding: impl Into<Rounding>,
720        fill_color: impl Into<Color32>,
721    ) -> Self {
722        Self {
723            rect,
724            rounding: rounding.into(),
725            fill: fill_color.into(),
726            stroke: Default::default(),
727            blur_width: 0.0,
728            fill_texture_id: Default::default(),
729            uv: Rect::ZERO,
730        }
731    }
732
733    #[inline]
734    pub fn stroke(rect: Rect, rounding: impl Into<Rounding>, stroke: impl Into<Stroke>) -> Self {
735        Self {
736            rect,
737            rounding: rounding.into(),
738            fill: Default::default(),
739            stroke: stroke.into(),
740            blur_width: 0.0,
741            fill_texture_id: Default::default(),
742            uv: Rect::ZERO,
743        }
744    }
745
746    /// If larger than zero, the edges of the rectangle
747    /// (for both fill and stroke) will be blurred.
748    ///
749    /// This can be used to produce shadows and glow effects.
750    ///
751    /// The blur is currently implemented using a simple linear blur in `sRGBA` gamma space.
752    #[inline]
753    pub fn with_blur_width(mut self, blur_width: f32) -> Self {
754        self.blur_width = blur_width;
755        self
756    }
757
758    /// The visual bounding rectangle (includes stroke width)
759    #[inline]
760    pub fn visual_bounding_rect(&self) -> Rect {
761        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
762            Rect::NOTHING
763        } else {
764            self.rect
765                .expand((self.stroke.width + self.blur_width) / 2.0)
766        }
767    }
768}
769
770impl From<RectShape> for Shape {
771    #[inline(always)]
772    fn from(shape: RectShape) -> Self {
773        Self::Rect(shape)
774    }
775}
776
777#[derive(Copy, Clone, Debug, PartialEq)]
778#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
779/// How rounded the corners of things should be
780pub struct Rounding {
781    /// Radius of the rounding of the North-West (left top) corner.
782    pub nw: f32,
783
784    /// Radius of the rounding of the North-East (right top) corner.
785    pub ne: f32,
786
787    /// Radius of the rounding of the South-West (left bottom) corner.
788    pub sw: f32,
789
790    /// Radius of the rounding of the South-East (right bottom) corner.
791    pub se: f32,
792}
793
794impl Default for Rounding {
795    #[inline]
796    fn default() -> Self {
797        Self::ZERO
798    }
799}
800
801impl From<f32> for Rounding {
802    #[inline]
803    fn from(radius: f32) -> Self {
804        Self {
805            nw: radius,
806            ne: radius,
807            sw: radius,
808            se: radius,
809        }
810    }
811}
812
813impl Rounding {
814    /// No rounding on any corner.
815    pub const ZERO: Self = Self {
816        nw: 0.0,
817        ne: 0.0,
818        sw: 0.0,
819        se: 0.0,
820    };
821
822    #[inline]
823    pub const fn same(radius: f32) -> Self {
824        Self {
825            nw: radius,
826            ne: radius,
827            sw: radius,
828            se: radius,
829        }
830    }
831
832    /// Do all corners have the same rounding?
833    #[inline]
834    pub fn is_same(&self) -> bool {
835        self.nw == self.ne && self.nw == self.sw && self.nw == self.se
836    }
837
838    /// Make sure each corner has a rounding of at least this.
839    #[inline]
840    pub fn at_least(&self, min: f32) -> Self {
841        Self {
842            nw: self.nw.max(min),
843            ne: self.ne.max(min),
844            sw: self.sw.max(min),
845            se: self.se.max(min),
846        }
847    }
848
849    /// Make sure each corner has a rounding of at most this.
850    #[inline]
851    pub fn at_most(&self, max: f32) -> Self {
852        Self {
853            nw: self.nw.min(max),
854            ne: self.ne.min(max),
855            sw: self.sw.min(max),
856            se: self.se.min(max),
857        }
858    }
859}
860
861impl std::ops::Add for Rounding {
862    type Output = Self;
863    #[inline]
864    fn add(self, rhs: Self) -> Self {
865        Self {
866            nw: self.nw + rhs.nw,
867            ne: self.ne + rhs.ne,
868            sw: self.sw + rhs.sw,
869            se: self.se + rhs.se,
870        }
871    }
872}
873
874impl std::ops::AddAssign for Rounding {
875    #[inline]
876    fn add_assign(&mut self, rhs: Self) {
877        *self = Self {
878            nw: self.nw + rhs.nw,
879            ne: self.ne + rhs.ne,
880            sw: self.sw + rhs.sw,
881            se: self.se + rhs.se,
882        };
883    }
884}
885
886impl std::ops::AddAssign<f32> for Rounding {
887    #[inline]
888    fn add_assign(&mut self, rhs: f32) {
889        *self = Self {
890            nw: self.nw + rhs,
891            ne: self.ne + rhs,
892            sw: self.sw + rhs,
893            se: self.se + rhs,
894        };
895    }
896}
897
898impl std::ops::Sub for Rounding {
899    type Output = Self;
900    #[inline]
901    fn sub(self, rhs: Self) -> Self {
902        Self {
903            nw: self.nw - rhs.nw,
904            ne: self.ne - rhs.ne,
905            sw: self.sw - rhs.sw,
906            se: self.se - rhs.se,
907        }
908    }
909}
910
911impl std::ops::SubAssign for Rounding {
912    #[inline]
913    fn sub_assign(&mut self, rhs: Self) {
914        *self = Self {
915            nw: self.nw - rhs.nw,
916            ne: self.ne - rhs.ne,
917            sw: self.sw - rhs.sw,
918            se: self.se - rhs.se,
919        };
920    }
921}
922
923impl std::ops::SubAssign<f32> for Rounding {
924    #[inline]
925    fn sub_assign(&mut self, rhs: f32) {
926        *self = Self {
927            nw: self.nw - rhs,
928            ne: self.ne - rhs,
929            sw: self.sw - rhs,
930            se: self.se - rhs,
931        };
932    }
933}
934
935impl std::ops::Div<f32> for Rounding {
936    type Output = Self;
937    #[inline]
938    fn div(self, rhs: f32) -> Self {
939        Self {
940            nw: self.nw / rhs,
941            ne: self.ne / rhs,
942            sw: self.sw / rhs,
943            se: self.se / rhs,
944        }
945    }
946}
947
948impl std::ops::DivAssign<f32> for Rounding {
949    #[inline]
950    fn div_assign(&mut self, rhs: f32) {
951        *self = Self {
952            nw: self.nw / rhs,
953            ne: self.ne / rhs,
954            sw: self.sw / rhs,
955            se: self.se / rhs,
956        };
957    }
958}
959
960impl std::ops::Mul<f32> for Rounding {
961    type Output = Self;
962    #[inline]
963    fn mul(self, rhs: f32) -> Self {
964        Self {
965            nw: self.nw * rhs,
966            ne: self.ne * rhs,
967            sw: self.sw * rhs,
968            se: self.se * rhs,
969        }
970    }
971}
972
973impl std::ops::MulAssign<f32> for Rounding {
974    #[inline]
975    fn mul_assign(&mut self, rhs: f32) {
976        *self = Self {
977            nw: self.nw * rhs,
978            ne: self.ne * rhs,
979            sw: self.sw * rhs,
980            se: self.se * rhs,
981        };
982    }
983}
984
985// ----------------------------------------------------------------------------
986
987/// How to paint some text on screen.
988///
989/// This needs to be recreated if `pixels_per_point` (dpi scale) changes.
990#[derive(Clone, Debug, PartialEq)]
991#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
992pub struct TextShape {
993    /// Top left corner of the first character.
994    pub pos: Pos2,
995
996    /// The laid out text, from [`Fonts::layout_job`].
997    pub galley: Arc<Galley>,
998
999    /// Add this underline to the whole text.
1000    /// You can also set an underline when creating the galley.
1001    pub underline: Stroke,
1002
1003    /// Any [`Color32::PLACEHOLDER`] in the galley will be replaced by the given color.
1004    /// Affects everything: backgrounds, glyphs, strikethrough, underline, etc.
1005    pub fallback_color: Color32,
1006
1007    /// If set, the text color in the galley will be ignored and replaced
1008    /// with the given color.
1009    ///
1010    /// This only affects the glyphs and will NOT replace background color nor strikethrough/underline color.
1011    pub override_text_color: Option<Color32>,
1012
1013    /// If set, the text will be rendered with the given opacity in gamma space
1014    /// Affects everything: backgrounds, glyphs, strikethrough, underline, etc.
1015    pub opacity_factor: f32,
1016
1017    /// Rotate text by this many radians clockwise.
1018    /// The pivot is `pos` (the upper left corner of the text).
1019    pub angle: f32,
1020}
1021
1022impl TextShape {
1023    /// The given fallback color will be used for any uncolored part of the galley (using [`Color32::PLACEHOLDER`]).
1024    ///
1025    /// Any non-placeholder color in the galley takes precedence over this fallback color.
1026    #[inline]
1027    pub fn new(pos: Pos2, galley: Arc<Galley>, fallback_color: Color32) -> Self {
1028        Self {
1029            pos,
1030            galley,
1031            underline: Stroke::NONE,
1032            fallback_color,
1033            override_text_color: None,
1034            opacity_factor: 1.0,
1035            angle: 0.0,
1036        }
1037    }
1038
1039    /// The visual bounding rectangle
1040    #[inline]
1041    pub fn visual_bounding_rect(&self) -> Rect {
1042        self.galley.mesh_bounds.translate(self.pos.to_vec2())
1043    }
1044
1045    #[inline]
1046    pub fn with_underline(mut self, underline: Stroke) -> Self {
1047        self.underline = underline;
1048        self
1049    }
1050
1051    /// Use the given color for the text, regardless of what color is already in the galley.
1052    #[inline]
1053    pub fn with_override_text_color(mut self, override_text_color: Color32) -> Self {
1054        self.override_text_color = Some(override_text_color);
1055        self
1056    }
1057
1058    /// Rotate text by this many radians clockwise.
1059    /// The pivot is `pos` (the upper left corner of the text).
1060    #[inline]
1061    pub fn with_angle(mut self, angle: f32) -> Self {
1062        self.angle = angle;
1063        self
1064    }
1065
1066    /// Render text with this opacity in gamma space
1067    #[inline]
1068    pub fn with_opacity_factor(mut self, opacity_factor: f32) -> Self {
1069        self.opacity_factor = opacity_factor;
1070        self
1071    }
1072}
1073
1074impl From<TextShape> for Shape {
1075    #[inline(always)]
1076    fn from(shape: TextShape) -> Self {
1077        Self::Text(shape)
1078    }
1079}
1080
1081// ----------------------------------------------------------------------------
1082
1083/// Creates equally spaced filled circles from a line.
1084fn points_from_line(
1085    path: &[Pos2],
1086    spacing: f32,
1087    radius: f32,
1088    color: Color32,
1089    shapes: &mut Vec<Shape>,
1090) {
1091    let mut position_on_segment = 0.0;
1092    path.windows(2).for_each(|window| {
1093        let (start, end) = (window[0], window[1]);
1094        let vector = end - start;
1095        let segment_length = vector.length();
1096        while position_on_segment < segment_length {
1097            let new_point = start + vector * (position_on_segment / segment_length);
1098            shapes.push(Shape::circle_filled(new_point, radius, color));
1099            position_on_segment += spacing;
1100        }
1101        position_on_segment -= segment_length;
1102    });
1103}
1104
1105/// Creates dashes from a line.
1106fn dashes_from_line(
1107    path: &[Pos2],
1108    stroke: Stroke,
1109    dash_lengths: &[f32],
1110    gap_lengths: &[f32],
1111    shapes: &mut Vec<Shape>,
1112    dash_offset: f32,
1113) {
1114    assert_eq!(dash_lengths.len(), gap_lengths.len());
1115    let mut position_on_segment = dash_offset;
1116    let mut drawing_dash = false;
1117    let mut step = 0;
1118    let steps = dash_lengths.len();
1119    path.windows(2).for_each(|window| {
1120        let (start, end) = (window[0], window[1]);
1121        let vector = end - start;
1122        let segment_length = vector.length();
1123
1124        let mut start_point = start;
1125        while position_on_segment < segment_length {
1126            let new_point = start + vector * (position_on_segment / segment_length);
1127            if drawing_dash {
1128                // This is the end point.
1129                shapes.push(Shape::line_segment([start_point, new_point], stroke));
1130                position_on_segment += gap_lengths[step];
1131                // Increment step counter
1132                step += 1;
1133                if step >= steps {
1134                    step = 0;
1135                }
1136            } else {
1137                // Start a new dash.
1138                start_point = new_point;
1139                position_on_segment += dash_lengths[step];
1140            }
1141            drawing_dash = !drawing_dash;
1142        }
1143
1144        // If the segment ends and the dash is not finished, add the segment's end point.
1145        if drawing_dash {
1146            shapes.push(Shape::line_segment([start_point, end], stroke));
1147        }
1148
1149        position_on_segment -= segment_length;
1150    });
1151}
1152
1153// ----------------------------------------------------------------------------
1154
1155/// Information passed along with [`PaintCallback`] ([`Shape::Callback`]).
1156pub struct PaintCallbackInfo {
1157    /// Viewport in points.
1158    ///
1159    /// This specifies where on the screen to paint, and the borders of this
1160    /// Rect is the [-1, +1] of the Normalized Device Coordinates.
1161    ///
1162    /// Note than only a portion of this may be visible due to [`Self::clip_rect`].
1163    ///
1164    /// This comes from [`PaintCallback::rect`].
1165    pub viewport: Rect,
1166
1167    /// Clip rectangle in points.
1168    pub clip_rect: Rect,
1169
1170    /// Pixels per point.
1171    pub pixels_per_point: f32,
1172
1173    /// Full size of the screen, in pixels.
1174    pub screen_size_px: [u32; 2],
1175}
1176
1177/// Size of the viewport in whole, physical pixels.
1178pub struct ViewportInPixels {
1179    /// Physical pixel offset for left side of the viewport.
1180    pub left_px: i32,
1181
1182    /// Physical pixel offset for top side of the viewport.
1183    pub top_px: i32,
1184
1185    /// Physical pixel offset for bottom side of the viewport.
1186    ///
1187    /// This is what `glViewport`, `glScissor` etc expects for the y axis.
1188    pub from_bottom_px: i32,
1189
1190    /// Viewport width in physical pixels.
1191    pub width_px: i32,
1192
1193    /// Viewport height in physical pixels.
1194    pub height_px: i32,
1195}
1196
1197impl ViewportInPixels {
1198    fn from_points(rect: &Rect, pixels_per_point: f32, screen_size_px: [u32; 2]) -> Self {
1199        // Fractional pixel values for viewports are generally valid, but may cause sampling issues
1200        // and rounding errors might cause us to get out of bounds.
1201
1202        // Round:
1203        let left_px = (pixels_per_point * rect.min.x).round() as i32; // inclusive
1204        let top_px = (pixels_per_point * rect.min.y).round() as i32; // inclusive
1205        let right_px = (pixels_per_point * rect.max.x).round() as i32; // exclusive
1206        let bottom_px = (pixels_per_point * rect.max.y).round() as i32; // exclusive
1207
1208        // Clamp to screen:
1209        let screen_width = screen_size_px[0] as i32;
1210        let screen_height = screen_size_px[1] as i32;
1211        let left_px = left_px.clamp(0, screen_width);
1212        let right_px = right_px.clamp(left_px, screen_width);
1213        let top_px = top_px.clamp(0, screen_height);
1214        let bottom_px = bottom_px.clamp(top_px, screen_height);
1215
1216        let width_px = right_px - left_px;
1217        let height_px = bottom_px - top_px;
1218
1219        Self {
1220            left_px,
1221            top_px,
1222            from_bottom_px: screen_height - height_px - top_px,
1223            width_px,
1224            height_px,
1225        }
1226    }
1227}
1228
1229#[test]
1230fn test_viewport_rounding() {
1231    for i in 0..=10_000 {
1232        // Two adjacent viewports should never overlap:
1233        let x = i as f32 / 97.0;
1234        let left = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_max_x(x);
1235        let right = Rect::from_min_max(pos2(0.0, 0.0), pos2(100.0, 100.0)).with_min_x(x);
1236
1237        for pixels_per_point in [0.618, 1.0, std::f32::consts::PI] {
1238            let left = ViewportInPixels::from_points(&left, pixels_per_point, [100, 100]);
1239            let right = ViewportInPixels::from_points(&right, pixels_per_point, [100, 100]);
1240            assert_eq!(left.left_px + left.width_px, right.left_px);
1241        }
1242    }
1243}
1244
1245impl PaintCallbackInfo {
1246    /// The viewport rectangle. This is what you would use in e.g. `glViewport`.
1247    pub fn viewport_in_pixels(&self) -> ViewportInPixels {
1248        ViewportInPixels::from_points(&self.viewport, self.pixels_per_point, self.screen_size_px)
1249    }
1250
1251    /// The "scissor" or "clip" rectangle. This is what you would use in e.g. `glScissor`.
1252    pub fn clip_rect_in_pixels(&self) -> ViewportInPixels {
1253        ViewportInPixels::from_points(&self.clip_rect, self.pixels_per_point, self.screen_size_px)
1254    }
1255}
1256
1257/// If you want to paint some 3D shapes inside an egui region, you can use this.
1258///
1259/// This is advanced usage, and is backend specific.
1260#[derive(Clone)]
1261pub struct PaintCallback {
1262    /// Where to paint.
1263    ///
1264    /// This will become [`PaintCallbackInfo::viewport`].
1265    pub rect: Rect,
1266
1267    /// Paint something custom (e.g. 3D stuff).
1268    ///
1269    /// The concrete value of `callback` depends on the rendering backend used. For instance, the
1270    /// `glow` backend requires that callback be an `egui_glow::CallbackFn` while the `wgpu`
1271    /// backend requires a `egui_wgpu::Callback`.
1272    ///
1273    /// If the type cannot be downcast to the type expected by the current backend the callback
1274    /// will not be drawn.
1275    ///
1276    /// The rendering backend is responsible for first setting the active viewport to
1277    /// [`Self::rect`].
1278    ///
1279    /// The rendering backend is also responsible for restoring any state, such as the bound shader
1280    /// program, vertex array, etc.
1281    ///
1282    /// Shape has to be clone, therefore this has to be an `Arc` instead of a `Box`.
1283    pub callback: Arc<dyn Any + Send + Sync>,
1284}
1285
1286impl std::fmt::Debug for PaintCallback {
1287    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1288        f.debug_struct("CustomShape")
1289            .field("rect", &self.rect)
1290            .finish_non_exhaustive()
1291    }
1292}
1293
1294impl std::cmp::PartialEq for PaintCallback {
1295    fn eq(&self, other: &Self) -> bool {
1296        self.rect.eq(&other.rect) && Arc::ptr_eq(&self.callback, &other.callback)
1297    }
1298}
1299
1300impl From<PaintCallback> for Shape {
1301    #[inline(always)]
1302    fn from(shape: PaintCallback) -> Self {
1303        Self::Callback(shape)
1304    }
1305}