epaint/
tessellator.rs

1//! Converts graphics primitives into textured triangles.
2//!
3//! This module converts lines, circles, text and more represented by [`Shape`]
4//! into textured triangles represented by [`Mesh`].
5
6#![expect(clippy::identity_op)]
7
8use emath::{GuiRounding as _, NumExt as _, Pos2, Rect, Rot2, Vec2, pos2, remap, vec2};
9
10use crate::{
11    CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,
12    EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,
13    StrokeKind, TextShape, TextureId, Vertex, color::ColorMode, emath, stroke::PathStroke,
14    texture_atlas::PreparedDisc,
15};
16
17// ----------------------------------------------------------------------------
18
19#[expect(clippy::approx_constant)]
20mod precomputed_vertices {
21    // fn main() {
22    //     let n = 64;
23    //     println!("pub const CIRCLE_{}: [Vec2; {}] = [", n, n+1);
24    //     for i in 0..=n {
25    //         let a = std::f64::consts::TAU * i as f64 / n as f64;
26    //         println!("    vec2({:.06}, {:.06}),", a.cos(), a.sin());
27    //     }
28    //     println!("];")
29    // }
30
31    use emath::{Vec2, vec2};
32
33    pub const CIRCLE_8: [Vec2; 9] = [
34        vec2(1.000000, 0.000000),
35        vec2(0.707107, 0.707107),
36        vec2(0.000000, 1.000000),
37        vec2(-0.707107, 0.707107),
38        vec2(-1.000000, 0.000000),
39        vec2(-0.707107, -0.707107),
40        vec2(0.000000, -1.000000),
41        vec2(0.707107, -0.707107),
42        vec2(1.000000, 0.000000),
43    ];
44
45    pub const CIRCLE_16: [Vec2; 17] = [
46        vec2(1.000000, 0.000000),
47        vec2(0.923880, 0.382683),
48        vec2(0.707107, 0.707107),
49        vec2(0.382683, 0.923880),
50        vec2(0.000000, 1.000000),
51        vec2(-0.382684, 0.923880),
52        vec2(-0.707107, 0.707107),
53        vec2(-0.923880, 0.382683),
54        vec2(-1.000000, 0.000000),
55        vec2(-0.923880, -0.382683),
56        vec2(-0.707107, -0.707107),
57        vec2(-0.382684, -0.923880),
58        vec2(0.000000, -1.000000),
59        vec2(0.382684, -0.923879),
60        vec2(0.707107, -0.707107),
61        vec2(0.923880, -0.382683),
62        vec2(1.000000, 0.000000),
63    ];
64
65    pub const CIRCLE_32: [Vec2; 33] = [
66        vec2(1.000000, 0.000000),
67        vec2(0.980785, 0.195090),
68        vec2(0.923880, 0.382683),
69        vec2(0.831470, 0.555570),
70        vec2(0.707107, 0.707107),
71        vec2(0.555570, 0.831470),
72        vec2(0.382683, 0.923880),
73        vec2(0.195090, 0.980785),
74        vec2(0.000000, 1.000000),
75        vec2(-0.195090, 0.980785),
76        vec2(-0.382683, 0.923880),
77        vec2(-0.555570, 0.831470),
78        vec2(-0.707107, 0.707107),
79        vec2(-0.831470, 0.555570),
80        vec2(-0.923880, 0.382683),
81        vec2(-0.980785, 0.195090),
82        vec2(-1.000000, 0.000000),
83        vec2(-0.980785, -0.195090),
84        vec2(-0.923880, -0.382683),
85        vec2(-0.831470, -0.555570),
86        vec2(-0.707107, -0.707107),
87        vec2(-0.555570, -0.831470),
88        vec2(-0.382683, -0.923880),
89        vec2(-0.195090, -0.980785),
90        vec2(-0.000000, -1.000000),
91        vec2(0.195090, -0.980785),
92        vec2(0.382683, -0.923880),
93        vec2(0.555570, -0.831470),
94        vec2(0.707107, -0.707107),
95        vec2(0.831470, -0.555570),
96        vec2(0.923880, -0.382683),
97        vec2(0.980785, -0.195090),
98        vec2(1.000000, -0.000000),
99    ];
100
101    pub const CIRCLE_64: [Vec2; 65] = [
102        vec2(1.000000, 0.000000),
103        vec2(0.995185, 0.098017),
104        vec2(0.980785, 0.195090),
105        vec2(0.956940, 0.290285),
106        vec2(0.923880, 0.382683),
107        vec2(0.881921, 0.471397),
108        vec2(0.831470, 0.555570),
109        vec2(0.773010, 0.634393),
110        vec2(0.707107, 0.707107),
111        vec2(0.634393, 0.773010),
112        vec2(0.555570, 0.831470),
113        vec2(0.471397, 0.881921),
114        vec2(0.382683, 0.923880),
115        vec2(0.290285, 0.956940),
116        vec2(0.195090, 0.980785),
117        vec2(0.098017, 0.995185),
118        vec2(0.000000, 1.000000),
119        vec2(-0.098017, 0.995185),
120        vec2(-0.195090, 0.980785),
121        vec2(-0.290285, 0.956940),
122        vec2(-0.382683, 0.923880),
123        vec2(-0.471397, 0.881921),
124        vec2(-0.555570, 0.831470),
125        vec2(-0.634393, 0.773010),
126        vec2(-0.707107, 0.707107),
127        vec2(-0.773010, 0.634393),
128        vec2(-0.831470, 0.555570),
129        vec2(-0.881921, 0.471397),
130        vec2(-0.923880, 0.382683),
131        vec2(-0.956940, 0.290285),
132        vec2(-0.980785, 0.195090),
133        vec2(-0.995185, 0.098017),
134        vec2(-1.000000, 0.000000),
135        vec2(-0.995185, -0.098017),
136        vec2(-0.980785, -0.195090),
137        vec2(-0.956940, -0.290285),
138        vec2(-0.923880, -0.382683),
139        vec2(-0.881921, -0.471397),
140        vec2(-0.831470, -0.555570),
141        vec2(-0.773010, -0.634393),
142        vec2(-0.707107, -0.707107),
143        vec2(-0.634393, -0.773010),
144        vec2(-0.555570, -0.831470),
145        vec2(-0.471397, -0.881921),
146        vec2(-0.382683, -0.923880),
147        vec2(-0.290285, -0.956940),
148        vec2(-0.195090, -0.980785),
149        vec2(-0.098017, -0.995185),
150        vec2(-0.000000, -1.000000),
151        vec2(0.098017, -0.995185),
152        vec2(0.195090, -0.980785),
153        vec2(0.290285, -0.956940),
154        vec2(0.382683, -0.923880),
155        vec2(0.471397, -0.881921),
156        vec2(0.555570, -0.831470),
157        vec2(0.634393, -0.773010),
158        vec2(0.707107, -0.707107),
159        vec2(0.773010, -0.634393),
160        vec2(0.831470, -0.555570),
161        vec2(0.881921, -0.471397),
162        vec2(0.923880, -0.382683),
163        vec2(0.956940, -0.290285),
164        vec2(0.980785, -0.195090),
165        vec2(0.995185, -0.098017),
166        vec2(1.000000, -0.000000),
167    ];
168
169    pub const CIRCLE_128: [Vec2; 129] = [
170        vec2(1.000000, 0.000000),
171        vec2(0.998795, 0.049068),
172        vec2(0.995185, 0.098017),
173        vec2(0.989177, 0.146730),
174        vec2(0.980785, 0.195090),
175        vec2(0.970031, 0.242980),
176        vec2(0.956940, 0.290285),
177        vec2(0.941544, 0.336890),
178        vec2(0.923880, 0.382683),
179        vec2(0.903989, 0.427555),
180        vec2(0.881921, 0.471397),
181        vec2(0.857729, 0.514103),
182        vec2(0.831470, 0.555570),
183        vec2(0.803208, 0.595699),
184        vec2(0.773010, 0.634393),
185        vec2(0.740951, 0.671559),
186        vec2(0.707107, 0.707107),
187        vec2(0.671559, 0.740951),
188        vec2(0.634393, 0.773010),
189        vec2(0.595699, 0.803208),
190        vec2(0.555570, 0.831470),
191        vec2(0.514103, 0.857729),
192        vec2(0.471397, 0.881921),
193        vec2(0.427555, 0.903989),
194        vec2(0.382683, 0.923880),
195        vec2(0.336890, 0.941544),
196        vec2(0.290285, 0.956940),
197        vec2(0.242980, 0.970031),
198        vec2(0.195090, 0.980785),
199        vec2(0.146730, 0.989177),
200        vec2(0.098017, 0.995185),
201        vec2(0.049068, 0.998795),
202        vec2(0.000000, 1.000000),
203        vec2(-0.049068, 0.998795),
204        vec2(-0.098017, 0.995185),
205        vec2(-0.146730, 0.989177),
206        vec2(-0.195090, 0.980785),
207        vec2(-0.242980, 0.970031),
208        vec2(-0.290285, 0.956940),
209        vec2(-0.336890, 0.941544),
210        vec2(-0.382683, 0.923880),
211        vec2(-0.427555, 0.903989),
212        vec2(-0.471397, 0.881921),
213        vec2(-0.514103, 0.857729),
214        vec2(-0.555570, 0.831470),
215        vec2(-0.595699, 0.803208),
216        vec2(-0.634393, 0.773010),
217        vec2(-0.671559, 0.740951),
218        vec2(-0.707107, 0.707107),
219        vec2(-0.740951, 0.671559),
220        vec2(-0.773010, 0.634393),
221        vec2(-0.803208, 0.595699),
222        vec2(-0.831470, 0.555570),
223        vec2(-0.857729, 0.514103),
224        vec2(-0.881921, 0.471397),
225        vec2(-0.903989, 0.427555),
226        vec2(-0.923880, 0.382683),
227        vec2(-0.941544, 0.336890),
228        vec2(-0.956940, 0.290285),
229        vec2(-0.970031, 0.242980),
230        vec2(-0.980785, 0.195090),
231        vec2(-0.989177, 0.146730),
232        vec2(-0.995185, 0.098017),
233        vec2(-0.998795, 0.049068),
234        vec2(-1.000000, 0.000000),
235        vec2(-0.998795, -0.049068),
236        vec2(-0.995185, -0.098017),
237        vec2(-0.989177, -0.146730),
238        vec2(-0.980785, -0.195090),
239        vec2(-0.970031, -0.242980),
240        vec2(-0.956940, -0.290285),
241        vec2(-0.941544, -0.336890),
242        vec2(-0.923880, -0.382683),
243        vec2(-0.903989, -0.427555),
244        vec2(-0.881921, -0.471397),
245        vec2(-0.857729, -0.514103),
246        vec2(-0.831470, -0.555570),
247        vec2(-0.803208, -0.595699),
248        vec2(-0.773010, -0.634393),
249        vec2(-0.740951, -0.671559),
250        vec2(-0.707107, -0.707107),
251        vec2(-0.671559, -0.740951),
252        vec2(-0.634393, -0.773010),
253        vec2(-0.595699, -0.803208),
254        vec2(-0.555570, -0.831470),
255        vec2(-0.514103, -0.857729),
256        vec2(-0.471397, -0.881921),
257        vec2(-0.427555, -0.903989),
258        vec2(-0.382683, -0.923880),
259        vec2(-0.336890, -0.941544),
260        vec2(-0.290285, -0.956940),
261        vec2(-0.242980, -0.970031),
262        vec2(-0.195090, -0.980785),
263        vec2(-0.146730, -0.989177),
264        vec2(-0.098017, -0.995185),
265        vec2(-0.049068, -0.998795),
266        vec2(-0.000000, -1.000000),
267        vec2(0.049068, -0.998795),
268        vec2(0.098017, -0.995185),
269        vec2(0.146730, -0.989177),
270        vec2(0.195090, -0.980785),
271        vec2(0.242980, -0.970031),
272        vec2(0.290285, -0.956940),
273        vec2(0.336890, -0.941544),
274        vec2(0.382683, -0.923880),
275        vec2(0.427555, -0.903989),
276        vec2(0.471397, -0.881921),
277        vec2(0.514103, -0.857729),
278        vec2(0.555570, -0.831470),
279        vec2(0.595699, -0.803208),
280        vec2(0.634393, -0.773010),
281        vec2(0.671559, -0.740951),
282        vec2(0.707107, -0.707107),
283        vec2(0.740951, -0.671559),
284        vec2(0.773010, -0.634393),
285        vec2(0.803208, -0.595699),
286        vec2(0.831470, -0.555570),
287        vec2(0.857729, -0.514103),
288        vec2(0.881921, -0.471397),
289        vec2(0.903989, -0.427555),
290        vec2(0.923880, -0.382683),
291        vec2(0.941544, -0.336890),
292        vec2(0.956940, -0.290285),
293        vec2(0.970031, -0.242980),
294        vec2(0.980785, -0.195090),
295        vec2(0.989177, -0.146730),
296        vec2(0.995185, -0.098017),
297        vec2(0.998795, -0.049068),
298        vec2(1.000000, -0.000000),
299    ];
300}
301
302// ----------------------------------------------------------------------------
303
304#[derive(Clone, Copy, Debug, Default, PartialEq)]
305struct PathPoint {
306    pos: Pos2,
307
308    /// For filled paths the normal is used for anti-aliasing (both strokes and filled areas).
309    ///
310    /// For strokes the normal is also used for giving thickness to the path
311    /// (i.e. in what direction to expand).
312    ///
313    /// The normal could be estimated by differences between successive points,
314    /// but that would be less accurate (and in some cases slower).
315    ///
316    /// Normals are normally unit-length.
317    normal: Vec2,
318}
319
320/// A connected line (without thickness or gaps) which can be tessellated
321/// to either to a stroke (with thickness) or a filled convex area.
322/// Used as a scratch-pad during tessellation.
323#[derive(Clone, Debug, Default)]
324pub struct Path(Vec<PathPoint>);
325
326impl Path {
327    #[inline(always)]
328    pub fn clear(&mut self) {
329        self.0.clear();
330    }
331
332    #[inline(always)]
333    pub fn reserve(&mut self, additional: usize) {
334        self.0.reserve(additional);
335    }
336
337    #[inline(always)]
338    pub fn add_point(&mut self, pos: Pos2, normal: Vec2) {
339        self.0.push(PathPoint { pos, normal });
340    }
341
342    pub fn add_circle(&mut self, center: Pos2, radius: f32) {
343        use precomputed_vertices::{CIRCLE_8, CIRCLE_16, CIRCLE_32, CIRCLE_64, CIRCLE_128};
344
345        // These cutoffs are based on a high-dpi display. TODO(emilk): use pixels_per_point here?
346        // same cutoffs as in add_circle_quadrant
347
348        if radius <= 2.0 {
349            self.0.extend(CIRCLE_8.iter().map(|&n| PathPoint {
350                pos: center + radius * n,
351                normal: n,
352            }));
353        } else if radius <= 5.0 {
354            self.0.extend(CIRCLE_16.iter().map(|&n| PathPoint {
355                pos: center + radius * n,
356                normal: n,
357            }));
358        } else if radius < 18.0 {
359            self.0.extend(CIRCLE_32.iter().map(|&n| PathPoint {
360                pos: center + radius * n,
361                normal: n,
362            }));
363        } else if radius < 50.0 {
364            self.0.extend(CIRCLE_64.iter().map(|&n| PathPoint {
365                pos: center + radius * n,
366                normal: n,
367            }));
368        } else {
369            self.0.extend(CIRCLE_128.iter().map(|&n| PathPoint {
370                pos: center + radius * n,
371                normal: n,
372            }));
373        }
374    }
375
376    pub fn add_line_segment(&mut self, points: [Pos2; 2]) {
377        self.reserve(2);
378        let normal = (points[1] - points[0]).normalized().rot90();
379        self.add_point(points[0], normal);
380        self.add_point(points[1], normal);
381    }
382
383    pub fn add_open_points(&mut self, points: &[Pos2]) {
384        let n = points.len();
385        assert!(n >= 2, "A path needs at least two points, but got {n}");
386
387        if n == 2 {
388            // Common case optimization:
389            self.add_line_segment([points[0], points[1]]);
390        } else {
391            self.reserve(n);
392            self.add_point(points[0], (points[1] - points[0]).normalized().rot90());
393            let mut n0 = (points[1] - points[0]).normalized().rot90();
394            for i in 1..n - 1 {
395                let mut n1 = (points[i + 1] - points[i]).normalized().rot90();
396
397                // Handle duplicated points (but not triplicated…):
398                if n0 == Vec2::ZERO {
399                    n0 = n1;
400                } else if n1 == Vec2::ZERO {
401                    n1 = n0;
402                }
403
404                let normal = (n0 + n1) / 2.0;
405                let length_sq = normal.length_sq();
406                let right_angle_length_sq = 0.5;
407                let sharper_than_a_right_angle = length_sq < right_angle_length_sq;
408                if sharper_than_a_right_angle {
409                    // cut off the sharp corner
410                    let center_normal = normal.normalized();
411                    let n0c = (n0 + center_normal) / 2.0;
412                    let n1c = (n1 + center_normal) / 2.0;
413                    self.add_point(points[i], n0c / n0c.length_sq());
414                    self.add_point(points[i], n1c / n1c.length_sq());
415                } else {
416                    // miter join
417                    self.add_point(points[i], normal / length_sq);
418                }
419
420                n0 = n1;
421            }
422            self.add_point(
423                points[n - 1],
424                (points[n - 1] - points[n - 2]).normalized().rot90(),
425            );
426        }
427    }
428
429    pub fn add_line_loop(&mut self, points: &[Pos2]) {
430        let n = points.len();
431        assert!(n >= 2, "A path needs at least two points, but got {n}");
432        self.reserve(n);
433
434        let mut n0 = (points[0] - points[n - 1]).normalized().rot90();
435
436        for i in 0..n {
437            let next_i = if i + 1 == n { 0 } else { i + 1 };
438            let mut n1 = (points[next_i] - points[i]).normalized().rot90();
439
440            // Handle duplicated points (but not triplicated…):
441            if n0 == Vec2::ZERO {
442                n0 = n1;
443            } else if n1 == Vec2::ZERO {
444                n1 = n0;
445            }
446
447            let normal = (n0 + n1) / 2.0;
448            let length_sq = normal.length_sq();
449
450            // We can't just cut off corners for filled shapes like this,
451            // because the feather will both expand and contract the corner along the provided normals
452            // to make sure it doesn't grow, and the shrinking will make the inner points cross each other.
453            //
454            // A better approach is to shrink the vertices in by half the feather-width here
455            // and then only expand during feathering.
456            //
457            // See https://github.com/emilk/egui/issues/1226
458            const CUT_OFF_SHARP_CORNERS: bool = false;
459
460            let right_angle_length_sq = 0.5;
461            let sharper_than_a_right_angle = length_sq < right_angle_length_sq;
462            if CUT_OFF_SHARP_CORNERS && sharper_than_a_right_angle {
463                // cut off the sharp corner
464                let center_normal = normal.normalized();
465                let n0c = (n0 + center_normal) / 2.0;
466                let n1c = (n1 + center_normal) / 2.0;
467                self.add_point(points[i], n0c / n0c.length_sq());
468                self.add_point(points[i], n1c / n1c.length_sq());
469            } else {
470                // miter join
471                self.add_point(points[i], normal / length_sq);
472            }
473
474            n0 = n1;
475        }
476    }
477
478    /// The path is taken to be closed (i.e. returning to the start again).
479    ///
480    /// Calling this may reverse the vertices in the path if they are wrong winding order.
481    /// The preferred winding order is clockwise.
482    pub fn fill_and_stroke(
483        &mut self,
484        feathering: f32,
485        fill: Color32,
486        stroke: &PathStroke,
487        out: &mut Mesh,
488    ) {
489        stroke_and_fill_path(feathering, &mut self.0, PathType::Closed, stroke, fill, out);
490    }
491
492    /// Open-ended.
493    pub fn stroke_open(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) {
494        stroke_path(feathering, &mut self.0, PathType::Open, stroke, out);
495    }
496
497    /// A closed path (returning to the first point).
498    pub fn stroke_closed(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) {
499        stroke_path(feathering, &mut self.0, PathType::Closed, stroke, out);
500    }
501
502    pub fn stroke(
503        &mut self,
504        feathering: f32,
505        path_type: PathType,
506        stroke: &PathStroke,
507        out: &mut Mesh,
508    ) {
509        stroke_path(feathering, &mut self.0, path_type, stroke, out);
510    }
511
512    /// The path is taken to be closed (i.e. returning to the start again).
513    ///
514    /// Calling this may reverse the vertices in the path if they are wrong winding order.
515    /// The preferred winding order is clockwise.
516    pub fn fill(&mut self, feathering: f32, color: Color32, out: &mut Mesh) {
517        fill_closed_path(feathering, &mut self.0, color, out);
518    }
519
520    /// Like [`Self::fill`] but with texturing.
521    ///
522    /// The `uv_from_pos` is called for each vertex position.
523    pub fn fill_with_uv(
524        &mut self,
525        feathering: f32,
526        color: Color32,
527        texture_id: TextureId,
528        uv_from_pos: impl Fn(Pos2) -> Pos2,
529        out: &mut Mesh,
530    ) {
531        fill_closed_path_with_uv(feathering, &mut self.0, color, texture_id, uv_from_pos, out);
532    }
533}
534
535pub mod path {
536    //! Helpers for constructing paths
537    use crate::CornerRadiusF32;
538    use emath::{Pos2, Rect, pos2};
539
540    /// overwrites existing points
541    pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, cr: CornerRadiusF32) {
542        path.clear();
543
544        let min = rect.min;
545        let max = rect.max;
546
547        let cr = clamp_corner_radius(cr, rect);
548
549        if cr == CornerRadiusF32::ZERO {
550            path.reserve(4);
551            path.push(pos2(min.x, min.y)); // left top
552            path.push(pos2(max.x, min.y)); // right top
553            path.push(pos2(max.x, max.y)); // right bottom
554            path.push(pos2(min.x, max.y)); // left bottom
555        } else {
556            // We need to avoid duplicated vertices, because that leads to visual artifacts later.
557            // Duplicated vertices can happen when one side is all rounding, with no straight edge between.
558            let eps = f32::EPSILON * rect.size().max_elem();
559
560            add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); // south east
561
562            if rect.width() <= cr.se + cr.sw + eps {
563                path.pop(); // avoid duplicated vertex
564            }
565
566            add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); // south west
567
568            if rect.height() <= cr.sw + cr.nw + eps {
569                path.pop(); // avoid duplicated vertex
570            }
571
572            add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); // north west
573
574            if rect.width() <= cr.nw + cr.ne + eps {
575                path.pop(); // avoid duplicated vertex
576            }
577
578            add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); // north east
579
580            if rect.height() <= cr.ne + cr.se + eps {
581                path.pop(); // avoid duplicated vertex
582            }
583        }
584    }
585
586    /// Add one quadrant of a circle
587    ///
588    /// * quadrant 0: right bottom
589    /// * quadrant 1: left bottom
590    /// * quadrant 2: left top
591    /// * quadrant 3: right top
592    //
593    // Derivation:
594    //
595    // * angle 0 * TAU / 4 = right
596    //   - quadrant 0: right bottom
597    // * angle 1 * TAU / 4 = bottom
598    //   - quadrant 1: left bottom
599    // * angle 2 * TAU / 4 = left
600    //   - quadrant 2: left top
601    // * angle 3 * TAU / 4 = top
602    //   - quadrant 3: right top
603    // * angle 4 * TAU / 4 = right
604    pub fn add_circle_quadrant(path: &mut Vec<Pos2>, center: Pos2, radius: f32, quadrant: f32) {
605        use super::precomputed_vertices::{CIRCLE_8, CIRCLE_16, CIRCLE_32, CIRCLE_64, CIRCLE_128};
606
607        // These cutoffs are based on a high-dpi display. TODO(emilk): use pixels_per_point here?
608        // same cutoffs as in add_circle
609
610        if radius <= 0.0 {
611            path.push(center);
612        } else if radius <= 2.0 {
613            let offset = quadrant as usize * 2;
614            let quadrant_vertices = &CIRCLE_8[offset..=offset + 2];
615            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
616        } else if radius <= 5.0 {
617            let offset = quadrant as usize * 4;
618            let quadrant_vertices = &CIRCLE_16[offset..=offset + 4];
619            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
620        } else if radius < 18.0 {
621            let offset = quadrant as usize * 8;
622            let quadrant_vertices = &CIRCLE_32[offset..=offset + 8];
623            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
624        } else if radius < 50.0 {
625            let offset = quadrant as usize * 16;
626            let quadrant_vertices = &CIRCLE_64[offset..=offset + 16];
627            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
628        } else {
629            let offset = quadrant as usize * 32;
630            let quadrant_vertices = &CIRCLE_128[offset..=offset + 32];
631            path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
632        }
633    }
634
635    // Ensures the radius of each corner is within a valid range
636    fn clamp_corner_radius(cr: CornerRadiusF32, rect: Rect) -> CornerRadiusF32 {
637        let half_width = rect.width() * 0.5;
638        let half_height = rect.height() * 0.5;
639        let max_cr = half_width.min(half_height);
640        cr.at_most(max_cr).at_least(0.0)
641    }
642}
643
644// ----------------------------------------------------------------------------
645
646#[derive(Clone, Copy, PartialEq, Eq)]
647pub enum PathType {
648    Open,
649    Closed,
650}
651
652/// Tessellation quality options
653#[derive(Clone, Copy, Debug, PartialEq)]
654#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
655#[cfg_attr(feature = "serde", serde(default))]
656pub struct TessellationOptions {
657    /// Use "feathering" to smooth out the edges of shapes as a form of anti-aliasing.
658    ///
659    /// Feathering works by making each edge into a thin gradient into transparency.
660    /// The size of this edge is controlled by [`Self::feathering_size_in_pixels`].
661    ///
662    /// This makes shapes appear smoother, but requires more triangles and is therefore slower.
663    ///
664    /// This setting does not affect text.
665    ///
666    /// Default: `true`.
667    pub feathering: bool,
668
669    /// The size of the feathering, in physical pixels.
670    ///
671    /// The default, and suggested, value for this is `1.0`.
672    /// If you use a larger value, edges will appear blurry.
673    pub feathering_size_in_pixels: f32,
674
675    /// If `true` (default) cull certain primitives before tessellating them.
676    /// This likely makes
677    pub coarse_tessellation_culling: bool,
678
679    /// If `true`, small filled circled will be optimized by using pre-rasterized circled
680    /// from the font atlas.
681    pub prerasterized_discs: bool,
682
683    /// If `true` (default) align text to the physical pixel grid.
684    /// This makes the text sharper on most platforms.
685    pub round_text_to_pixels: bool,
686
687    /// If `true` (default), align right-angled line segments to the physical pixel grid.
688    ///
689    /// This makes the line segments appear crisp on any display.
690    pub round_line_segments_to_pixels: bool,
691
692    /// If `true` (default), align rectangles to the physical pixel grid.
693    ///
694    /// This makes the rectangle strokes more crisp,
695    /// and makes filled rectangles tile perfectly (without feathering).
696    ///
697    /// You can override this with [`crate::RectShape::round_to_pixels`].
698    pub round_rects_to_pixels: bool,
699
700    /// Output the clip rectangles to be painted.
701    pub debug_paint_clip_rects: bool,
702
703    /// Output the text-containing rectangles.
704    pub debug_paint_text_rects: bool,
705
706    /// If true, no clipping will be done.
707    pub debug_ignore_clip_rects: bool,
708
709    /// The maximum distance between the original curve and the flattened curve.
710    pub bezier_tolerance: f32,
711
712    /// The default value will be 1.0e-5, it will be used during float compare.
713    pub epsilon: f32,
714
715    /// If `rayon` feature is activated, should we parallelize tessellation?
716    pub parallel_tessellation: bool,
717
718    /// If `true`, invalid meshes will be silently ignored.
719    /// If `false`, invalid meshes will cause a panic.
720    ///
721    /// The default is `false` to save performance.
722    pub validate_meshes: bool,
723}
724
725impl Default for TessellationOptions {
726    fn default() -> Self {
727        Self {
728            feathering: true,
729            feathering_size_in_pixels: 1.0,
730            coarse_tessellation_culling: true,
731            prerasterized_discs: true,
732            round_text_to_pixels: true,
733            round_line_segments_to_pixels: true,
734            round_rects_to_pixels: true,
735            debug_paint_text_rects: false,
736            debug_paint_clip_rects: false,
737            debug_ignore_clip_rects: false,
738            bezier_tolerance: 0.1,
739            epsilon: 1.0e-5,
740            parallel_tessellation: true,
741            validate_meshes: false,
742        }
743    }
744}
745
746fn cw_signed_area(path: &[PathPoint]) -> f64 {
747    if let Some(last) = path.last() {
748        let mut previous = last.pos;
749        let mut area = 0.0;
750        for p in path {
751            area += (previous.x * p.pos.y - p.pos.x * previous.y) as f64;
752            previous = p.pos;
753        }
754        area
755    } else {
756        0.0
757    }
758}
759
760/// Tessellate the given convex area into a polygon.
761///
762/// Calling this may reverse the vertices in the path if they are wrong winding order.
763///
764/// The preferred winding order is clockwise.
765fn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32, out: &mut Mesh) {
766    if fill_color == Color32::TRANSPARENT {
767        return;
768    }
769
770    let n = path.len() as u32;
771    if n < 3 {
772        return;
773    }
774
775    if 0.0 < feathering {
776        if cw_signed_area(path) < 0.0 {
777            // Wrong winding order - fix:
778            path.reverse();
779            for point in &mut *path {
780                point.normal = -point.normal;
781            }
782        }
783
784        out.reserve_triangles(3 * n as usize);
785        out.reserve_vertices(2 * n as usize);
786        let idx_inner = out.vertices.len() as u32;
787        let idx_outer = idx_inner + 1;
788
789        // The fill:
790        for i in 2..n {
791            out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
792        }
793
794        // The feathering:
795        let mut i0 = n - 1;
796        for i1 in 0..n {
797            let p1 = &path[i1 as usize];
798            let dm = 0.5 * feathering * p1.normal;
799
800            let pos_inner = p1.pos - dm;
801            let pos_outer = p1.pos + dm;
802
803            out.colored_vertex(pos_inner, fill_color);
804            out.colored_vertex(pos_outer, Color32::TRANSPARENT);
805            out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
806            out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
807            i0 = i1;
808        }
809    } else {
810        out.reserve_triangles(n as usize);
811        let idx = out.vertices.len() as u32;
812        out.vertices
813            .extend(path.iter().map(|p| Vertex::untextured(p.pos, fill_color)));
814        for i in 2..n {
815            out.add_triangle(idx, idx + i - 1, idx + i);
816        }
817    }
818}
819
820/// Like [`fill_closed_path`] but with texturing.
821///
822/// The `uv_from_pos` is called for each vertex position.
823fn fill_closed_path_with_uv(
824    feathering: f32,
825    path: &mut [PathPoint],
826    color: Color32,
827    texture_id: TextureId,
828    uv_from_pos: impl Fn(Pos2) -> Pos2,
829    out: &mut Mesh,
830) {
831    if color == Color32::TRANSPARENT {
832        return;
833    }
834
835    if out.is_empty() {
836        out.texture_id = texture_id;
837    } else {
838        assert_eq!(
839            out.texture_id, texture_id,
840            "Mixing different `texture_id` in the same "
841        );
842    }
843
844    let n = path.len() as u32;
845    if 0.0 < feathering {
846        if cw_signed_area(path) < 0.0 {
847            // Wrong winding order - fix:
848            path.reverse();
849            for point in &mut *path {
850                point.normal = -point.normal;
851            }
852        }
853
854        out.reserve_triangles(3 * n as usize);
855        out.reserve_vertices(2 * n as usize);
856        let color_outer = Color32::TRANSPARENT;
857        let idx_inner = out.vertices.len() as u32;
858        let idx_outer = idx_inner + 1;
859
860        // The fill:
861        for i in 2..n {
862            out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
863        }
864
865        // The feathering:
866        let mut i0 = n - 1;
867        for i1 in 0..n {
868            let p1 = &path[i1 as usize];
869            let dm = 0.5 * feathering * p1.normal;
870
871            let pos = p1.pos - dm;
872            out.vertices.push(Vertex {
873                pos,
874                uv: uv_from_pos(pos),
875                color,
876            });
877
878            let pos = p1.pos + dm;
879            out.vertices.push(Vertex {
880                pos,
881                uv: uv_from_pos(pos),
882                color: color_outer,
883            });
884
885            out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
886            out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
887            i0 = i1;
888        }
889    } else {
890        out.reserve_triangles(n as usize);
891        let idx = out.vertices.len() as u32;
892        out.vertices.extend(path.iter().map(|p| Vertex {
893            pos: p.pos,
894            uv: uv_from_pos(p.pos),
895            color,
896        }));
897        for i in 2..n {
898            out.add_triangle(idx, idx + i - 1, idx + i);
899        }
900    }
901}
902
903/// Tessellate the given path as a stroke with thickness.
904fn stroke_path(
905    feathering: f32,
906    path: &mut [PathPoint],
907    path_type: PathType,
908    stroke: &PathStroke,
909    out: &mut Mesh,
910) {
911    let fill = Color32::TRANSPARENT;
912    stroke_and_fill_path(feathering, path, path_type, stroke, fill, out);
913}
914
915/// Tessellate the given path as a stroke with thickness, with optional fill color.
916///
917/// Calling this may reverse the vertices in the path if they are wrong winding order.
918///
919/// The preferred winding order is clockwise.
920fn stroke_and_fill_path(
921    feathering: f32,
922    path: &mut [PathPoint],
923    path_type: PathType,
924    stroke: &PathStroke,
925    color_fill: Color32,
926    out: &mut Mesh,
927) {
928    let n = path.len() as u32;
929
930    if n < 2 {
931        return;
932    }
933
934    if stroke.width == 0.0 {
935        // Skip the stroke, just fill.
936        return fill_closed_path(feathering, path, color_fill, out);
937    }
938
939    if color_fill != Color32::TRANSPARENT && cw_signed_area(path) < 0.0 {
940        // Wrong winding order - fix:
941        path.reverse();
942        for point in &mut *path {
943            point.normal = -point.normal;
944        }
945    }
946
947    if stroke.color == ColorMode::TRANSPARENT {
948        // Skip the stroke, just fill. But subtract the width from the path:
949        match stroke.kind {
950            StrokeKind::Inside => {
951                for point in &mut *path {
952                    point.pos -= stroke.width * point.normal;
953                }
954            }
955            StrokeKind::Middle => {
956                for point in &mut *path {
957                    point.pos -= 0.5 * stroke.width * point.normal;
958                }
959            }
960            StrokeKind::Outside => {}
961        }
962
963        // Skip the stroke, just fill.
964        return fill_closed_path(feathering, path, color_fill, out);
965    }
966
967    let idx = out.vertices.len() as u32;
968
969    // Move the points so that the stroke is on middle of the path.
970    match stroke.kind {
971        StrokeKind::Inside => {
972            for point in &mut *path {
973                point.pos -= 0.5 * stroke.width * point.normal;
974            }
975        }
976        StrokeKind::Middle => {
977            // correct
978        }
979        StrokeKind::Outside => {
980            for point in &mut *path {
981                point.pos += 0.5 * stroke.width * point.normal;
982            }
983        }
984    }
985
986    // Expand the bounding box to include the thickness of the path
987    let uv_bbox = if matches!(stroke.color, ColorMode::UV(_)) {
988        Rect::from_points(&path.iter().map(|p| p.pos).collect::<Vec<Pos2>>())
989            .expand((stroke.width / 2.0) + feathering)
990    } else {
991        Rect::NAN
992    };
993    let get_color = |col: &ColorMode, pos: Pos2| match col {
994        ColorMode::Solid(col) => *col,
995        ColorMode::UV(fun) => fun(uv_bbox, pos),
996    };
997
998    if 0.0 < feathering {
999        let color_outer = Color32::TRANSPARENT;
1000        let color_middle = &stroke.color;
1001
1002        // We add a bit of an epsilon here, because when we round to pixels,
1003        // we can get rounding errors (unless pixels_per_point is an integer).
1004        // And it's better to err on the side of the nicer rendering with line caps
1005        // (the thin-line optimization has no line caps).
1006        let thin_line = stroke.width <= 0.9 * feathering;
1007        if thin_line {
1008            // If the stroke is painted smaller than the pixel width (=feathering width),
1009            // then we risk severe aliasing.
1010            // Instead, we paint the stroke as a triangular ridge, two feather-widths wide,
1011            // and lessen the opacity of the middle part instead of making it thinner.
1012            if color_fill != Color32::TRANSPARENT && stroke.width < feathering {
1013                // If this is filled shape, then we need to also compensate so that the
1014                // filled area remains the same as it would have been without the
1015                // artificially wide line.
1016                for point in &mut *path {
1017                    point.pos += 0.5 * (feathering - stroke.width) * point.normal;
1018                }
1019            }
1020
1021            // TODO(emilk): add line caps (if this is an open line).
1022
1023            let opacity = stroke.width / feathering;
1024
1025            /*
1026            We paint the line using three edges: outer, middle, fill.
1027
1028            .       o   m   i      outer, middle, fill
1029            .       |---|          feathering (pixel width)
1030            */
1031
1032            out.reserve_triangles(4 * n as usize);
1033            out.reserve_vertices(3 * n as usize);
1034
1035            let mut i0 = n - 1;
1036            for i1 in 0..n {
1037                let connect_with_previous = path_type == PathType::Closed || i1 > 0;
1038                let p1 = path[i1 as usize];
1039                let p = p1.pos;
1040                let n = p1.normal;
1041                out.colored_vertex(p + n * feathering, color_outer);
1042                out.colored_vertex(p, mul_color(get_color(color_middle, p), opacity));
1043                out.colored_vertex(p - n * feathering, color_fill);
1044
1045                if connect_with_previous {
1046                    out.add_triangle(idx + 3 * i0 + 0, idx + 3 * i0 + 1, idx + 3 * i1 + 0);
1047                    out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i1 + 0, idx + 3 * i1 + 1);
1048
1049                    out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i0 + 2, idx + 3 * i1 + 1);
1050                    out.add_triangle(idx + 3 * i0 + 2, idx + 3 * i1 + 1, idx + 3 * i1 + 2);
1051                }
1052
1053                i0 = i1;
1054            }
1055
1056            if color_fill != Color32::TRANSPARENT {
1057                out.reserve_triangles(n as usize - 2);
1058                let idx_fill = idx + 2;
1059                for i in 2..n {
1060                    out.add_triangle(idx_fill + 3 * (i - 1), idx_fill, idx_fill + 3 * i);
1061                }
1062            }
1063        } else {
1064            // thick anti-aliased line
1065
1066            /*
1067            We paint the line using four edges: outer, middle, middle, fill
1068
1069            .       o   m     p    m   f   outer, middle, point, middle, fill
1070            .       |---|                  feathering (pixel width)
1071            .         |--------------|     width
1072            .       |---------|            outer_rad
1073            .           |-----|            inner_rad
1074            */
1075
1076            let inner_rad = 0.5 * (stroke.width - feathering);
1077            let outer_rad = 0.5 * (stroke.width + feathering);
1078
1079            match path_type {
1080                PathType::Closed => {
1081                    out.reserve_triangles(6 * n as usize);
1082                    out.reserve_vertices(4 * n as usize);
1083
1084                    let mut i0 = n - 1;
1085                    for i1 in 0..n {
1086                        let p1 = path[i1 as usize];
1087                        let p = p1.pos;
1088                        let n = p1.normal;
1089                        out.colored_vertex(p + n * outer_rad, color_outer);
1090                        out.colored_vertex(
1091                            p + n * inner_rad,
1092                            get_color(color_middle, p + n * inner_rad),
1093                        );
1094                        out.colored_vertex(
1095                            p - n * inner_rad,
1096                            get_color(color_middle, p - n * inner_rad),
1097                        );
1098                        out.colored_vertex(p - n * outer_rad, color_fill);
1099
1100                        out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
1101                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
1102
1103                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
1104                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1105
1106                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
1107                        out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1108
1109                        i0 = i1;
1110                    }
1111
1112                    if color_fill != Color32::TRANSPARENT {
1113                        out.reserve_triangles(n as usize - 2);
1114                        let idx_fill = idx + 3;
1115                        for i in 2..n {
1116                            out.add_triangle(idx_fill + 4 * (i - 1), idx_fill, idx_fill + 4 * i);
1117                        }
1118                    }
1119                }
1120                PathType::Open => {
1121                    // Anti-alias the ends by extruding the outer edge and adding
1122                    // two more triangles to each end:
1123
1124                    //   | aa |       | aa |
1125                    //    _________________   ___
1126                    //   | \    added    / |  feathering
1127                    //   |   \ ___p___ /   |  ___
1128                    //   |    |       |    |
1129                    //   |    |  opa  |    |
1130                    //   |    |  que  |    |
1131                    //   |    |       |    |
1132
1133                    // (in the future it would be great with an option to add a circular end instead)
1134
1135                    // TODO(emilk): we should probably shrink before adding the line caps,
1136                    // so that we don't add to the area of the line.
1137                    // TODO(emilk): make line caps optional.
1138
1139                    out.reserve_triangles(6 * n as usize + 4);
1140                    out.reserve_vertices(4 * n as usize);
1141
1142                    {
1143                        let end = path[0];
1144                        let p = end.pos;
1145                        let n = end.normal;
1146                        let back_extrude = n.rot90() * feathering;
1147                        out.colored_vertex(p + n * outer_rad + back_extrude, color_outer);
1148                        out.colored_vertex(
1149                            p + n * inner_rad,
1150                            get_color(color_middle, p + n * inner_rad),
1151                        );
1152                        out.colored_vertex(
1153                            p - n * inner_rad,
1154                            get_color(color_middle, p - n * inner_rad),
1155                        );
1156                        out.colored_vertex(p - n * outer_rad + back_extrude, color_outer);
1157
1158                        out.add_triangle(idx + 0, idx + 1, idx + 2);
1159                        out.add_triangle(idx + 0, idx + 2, idx + 3);
1160                    }
1161
1162                    let mut i0 = 0;
1163                    for i1 in 1..n - 1 {
1164                        let point = path[i1 as usize];
1165                        let p = point.pos;
1166                        let n = point.normal;
1167                        out.colored_vertex(p + n * outer_rad, color_outer);
1168                        out.colored_vertex(
1169                            p + n * inner_rad,
1170                            get_color(color_middle, p + n * inner_rad),
1171                        );
1172                        out.colored_vertex(
1173                            p - n * inner_rad,
1174                            get_color(color_middle, p - n * inner_rad),
1175                        );
1176                        out.colored_vertex(p - n * outer_rad, color_outer);
1177
1178                        out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
1179                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
1180
1181                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
1182                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1183
1184                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
1185                        out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1186
1187                        i0 = i1;
1188                    }
1189
1190                    {
1191                        let i1 = n - 1;
1192                        let end = path[i1 as usize];
1193                        let p = end.pos;
1194                        let n = end.normal;
1195                        let back_extrude = -n.rot90() * feathering;
1196                        out.colored_vertex(p + n * outer_rad + back_extrude, color_outer);
1197                        out.colored_vertex(
1198                            p + n * inner_rad,
1199                            get_color(color_middle, p + n * inner_rad),
1200                        );
1201                        out.colored_vertex(
1202                            p - n * inner_rad,
1203                            get_color(color_middle, p - n * inner_rad),
1204                        );
1205                        out.colored_vertex(p - n * outer_rad + back_extrude, color_outer);
1206
1207                        out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
1208                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
1209
1210                        out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
1211                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1212
1213                        out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
1214                        out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1215
1216                        // The extension:
1217                        out.add_triangle(idx + 4 * i1 + 0, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1218                        out.add_triangle(idx + 4 * i1 + 0, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1219                    }
1220                }
1221            }
1222        }
1223    } else {
1224        // not anti-aliased:
1225        out.reserve_triangles(2 * n as usize);
1226        out.reserve_vertices(2 * n as usize);
1227
1228        let last_index = if path_type == PathType::Closed {
1229            n
1230        } else {
1231            n - 1
1232        };
1233        for i in 0..last_index {
1234            out.add_triangle(
1235                idx + (2 * i + 0) % (2 * n),
1236                idx + (2 * i + 1) % (2 * n),
1237                idx + (2 * i + 2) % (2 * n),
1238            );
1239            out.add_triangle(
1240                idx + (2 * i + 2) % (2 * n),
1241                idx + (2 * i + 1) % (2 * n),
1242                idx + (2 * i + 3) % (2 * n),
1243            );
1244        }
1245
1246        let thin_line = stroke.width <= feathering;
1247        if thin_line {
1248            // Fade out thin lines rather than making them thinner
1249            let opacity = stroke.width / feathering;
1250            let radius = feathering / 2.0;
1251            for p in path.iter_mut() {
1252                out.colored_vertex(
1253                    p.pos + radius * p.normal,
1254                    mul_color(get_color(&stroke.color, p.pos + radius * p.normal), opacity),
1255                );
1256                out.colored_vertex(
1257                    p.pos - radius * p.normal,
1258                    mul_color(get_color(&stroke.color, p.pos - radius * p.normal), opacity),
1259                );
1260            }
1261        } else {
1262            let radius = stroke.width / 2.0;
1263            for p in path.iter_mut() {
1264                out.colored_vertex(
1265                    p.pos + radius * p.normal,
1266                    get_color(&stroke.color, p.pos + radius * p.normal),
1267                );
1268                out.colored_vertex(
1269                    p.pos - radius * p.normal,
1270                    get_color(&stroke.color, p.pos - radius * p.normal),
1271                );
1272            }
1273        }
1274
1275        if color_fill != Color32::TRANSPARENT {
1276            // We Need to create new vertices, because the ones we used for the stroke
1277            // has the wrong color.
1278
1279            // Shrink to ignore the stroke…
1280            for point in &mut *path {
1281                point.pos -= 0.5 * stroke.width * point.normal;
1282            }
1283            // …then fill:
1284            fill_closed_path(feathering, path, color_fill, out);
1285        }
1286    }
1287}
1288
1289fn mul_color(color: Color32, factor: f32) -> Color32 {
1290    // The fast gamma-space multiply also happens to be perceptually better.
1291    // Win-win!
1292    color.gamma_multiply(factor)
1293}
1294
1295// ----------------------------------------------------------------------------
1296
1297/// Converts [`Shape`]s into triangles ([`Mesh`]).
1298///
1299/// For performance reasons it is smart to reuse the same [`Tessellator`].
1300#[derive(Clone)]
1301pub struct Tessellator {
1302    pixels_per_point: f32,
1303    options: TessellationOptions,
1304    font_tex_size: [usize; 2],
1305
1306    /// See [`crate::TextureAtlas::prepared_discs`].
1307    prepared_discs: Vec<PreparedDisc>,
1308
1309    /// size of feathering in points. normally the size of a physical pixel. 0.0 if disabled
1310    feathering: f32,
1311
1312    /// Only used for culling
1313    clip_rect: Rect,
1314
1315    scratchpad_points: Vec<Pos2>,
1316    scratchpad_path: Path,
1317}
1318
1319impl Tessellator {
1320    /// Create a new [`Tessellator`].
1321    ///
1322    /// * `pixels_per_point`: number of physical pixels to each logical point
1323    /// * `options`: tessellation quality
1324    /// * `shapes`: what to tessellate
1325    /// * `font_tex_size`: size of the font texture. Required to normalize glyph uv rectangles when tessellating text.
1326    /// * `prepared_discs`: What [`crate::TextureAtlas::prepared_discs`] returns. Can safely be set to an empty vec.
1327    pub fn new(
1328        pixels_per_point: f32,
1329        options: TessellationOptions,
1330        font_tex_size: [usize; 2],
1331        prepared_discs: Vec<PreparedDisc>,
1332    ) -> Self {
1333        let feathering = if options.feathering {
1334            let pixel_size = 1.0 / pixels_per_point;
1335            options.feathering_size_in_pixels * pixel_size
1336        } else {
1337            0.0
1338        };
1339        Self {
1340            pixels_per_point,
1341            options,
1342            font_tex_size,
1343            prepared_discs,
1344            feathering,
1345            clip_rect: Rect::EVERYTHING,
1346            scratchpad_points: Default::default(),
1347            scratchpad_path: Default::default(),
1348        }
1349    }
1350
1351    /// Set the [`Rect`] to use for culling.
1352    pub fn set_clip_rect(&mut self, clip_rect: Rect) {
1353        self.clip_rect = clip_rect;
1354    }
1355
1356    /// Tessellate a clipped shape into a list of primitives.
1357    pub fn tessellate_clipped_shape(
1358        &mut self,
1359        clipped_shape: ClippedShape,
1360        out_primitives: &mut Vec<ClippedPrimitive>,
1361    ) {
1362        let ClippedShape { clip_rect, shape } = clipped_shape;
1363
1364        if !clip_rect.is_positive() {
1365            return; // skip empty clip rectangles
1366        }
1367
1368        if let Shape::Vec(shapes) = shape {
1369            for shape in shapes {
1370                self.tessellate_clipped_shape(ClippedShape { clip_rect, shape }, out_primitives);
1371            }
1372            return;
1373        }
1374
1375        if let Shape::Callback(callback) = shape {
1376            out_primitives.push(ClippedPrimitive {
1377                clip_rect,
1378                primitive: Primitive::Callback(callback),
1379            });
1380            return;
1381        }
1382
1383        let start_new_mesh = match out_primitives.last() {
1384            None => true,
1385            Some(output_clipped_primitive) => {
1386                output_clipped_primitive.clip_rect != clip_rect
1387                    || match &output_clipped_primitive.primitive {
1388                        Primitive::Mesh(output_mesh) => {
1389                            output_mesh.texture_id != shape.texture_id()
1390                        }
1391                        Primitive::Callback(_) => true,
1392                    }
1393            }
1394        };
1395
1396        if start_new_mesh {
1397            out_primitives.push(ClippedPrimitive {
1398                clip_rect,
1399                primitive: Primitive::Mesh(Mesh::default()),
1400            });
1401        }
1402
1403        #[expect(clippy::unwrap_used)] // it's never empty
1404        let out = out_primitives.last_mut().unwrap();
1405
1406        if let Primitive::Mesh(out_mesh) = &mut out.primitive {
1407            self.clip_rect = clip_rect;
1408            self.tessellate_shape(shape, out_mesh);
1409        } else {
1410            unreachable!();
1411        }
1412    }
1413
1414    /// Tessellate a single [`Shape`] into a [`Mesh`].
1415    ///
1416    /// This call can panic the given shape is of [`Shape::Vec`] or [`Shape::Callback`].
1417    /// For that, use [`Self::tessellate_clipped_shape`] instead.
1418    /// * `shape`: the shape to tessellate.
1419    /// * `out`: triangles are appended to this.
1420    pub fn tessellate_shape(&mut self, shape: Shape, out: &mut Mesh) {
1421        match shape {
1422            Shape::Noop => {}
1423            Shape::Vec(vec) => {
1424                for shape in vec {
1425                    self.tessellate_shape(shape, out);
1426                }
1427            }
1428            Shape::Circle(circle) => {
1429                self.tessellate_circle(circle, out);
1430            }
1431            Shape::Ellipse(ellipse) => {
1432                self.tessellate_ellipse(ellipse, out);
1433            }
1434            Shape::Mesh(mesh) => {
1435                profiling::scope!("mesh");
1436
1437                if self.options.validate_meshes && !mesh.is_valid() {
1438                    debug_assert!(false, "Invalid Mesh in Shape::Mesh");
1439                    return;
1440                }
1441                // note: `append` still checks if the mesh is valid if extra asserts are enabled.
1442
1443                if self.options.coarse_tessellation_culling
1444                    && !self.clip_rect.intersects(mesh.calc_bounds())
1445                {
1446                    return;
1447                }
1448
1449                out.append_ref(&mesh);
1450            }
1451            Shape::LineSegment { points, stroke } => {
1452                self.tessellate_line_segment(points, stroke, out);
1453            }
1454            Shape::Path(path_shape) => {
1455                self.tessellate_path(&path_shape, out);
1456            }
1457            Shape::Rect(rect_shape) => {
1458                self.tessellate_rect(&rect_shape, out);
1459            }
1460            Shape::Text(text_shape) => {
1461                if self.options.debug_paint_text_rects {
1462                    let rect = text_shape.galley.rect.translate(text_shape.pos.to_vec2());
1463                    self.tessellate_rect(
1464                        &RectShape::stroke(rect, 2.0, (0.5, Color32::GREEN), StrokeKind::Outside),
1465                        out,
1466                    );
1467                }
1468                self.tessellate_text(&text_shape, out);
1469            }
1470            Shape::QuadraticBezier(quadratic_shape) => {
1471                self.tessellate_quadratic_bezier(&quadratic_shape, out);
1472            }
1473            Shape::CubicBezier(cubic_shape) => self.tessellate_cubic_bezier(&cubic_shape, out),
1474            Shape::Callback(_) => {
1475                panic!("Shape::Callback passed to Tessellator");
1476            }
1477        }
1478    }
1479
1480    /// Tessellate a single [`CircleShape`] into a [`Mesh`].
1481    ///
1482    /// * `shape`: the circle to tessellate.
1483    /// * `out`: triangles are appended to this.
1484    pub fn tessellate_circle(&mut self, shape: CircleShape, out: &mut Mesh) {
1485        let CircleShape {
1486            center,
1487            radius,
1488            mut fill,
1489            stroke,
1490        } = shape;
1491
1492        if radius <= 0.0 {
1493            return;
1494        }
1495
1496        if self.options.coarse_tessellation_culling
1497            && !self
1498                .clip_rect
1499                .expand(radius + stroke.width)
1500                .contains(center)
1501        {
1502            return;
1503        }
1504
1505        if self.options.prerasterized_discs && fill != Color32::TRANSPARENT {
1506            let radius_px = radius * self.pixels_per_point;
1507            // strike the right balance between some circles becoming too blurry, and some too sharp.
1508            let cutoff_radius = radius_px * 2.0_f32.powf(0.25);
1509
1510            // Find the right disc radius for a crisp edge:
1511            // TODO(emilk): perhaps we can do something faster than this linear search.
1512            for disc in &self.prepared_discs {
1513                if cutoff_radius <= disc.r {
1514                    let side = radius_px * disc.w / (self.pixels_per_point * disc.r);
1515                    let rect = Rect::from_center_size(center, Vec2::splat(side));
1516                    out.add_rect_with_uv(rect, disc.uv, fill);
1517
1518                    if stroke.is_empty() {
1519                        return; // we are done
1520                    } else {
1521                        // we still need to do the stroke
1522                        fill = Color32::TRANSPARENT; // don't fill again below
1523                        break;
1524                    }
1525                }
1526            }
1527        }
1528
1529        let path_stroke = PathStroke::from(stroke).outside();
1530        self.scratchpad_path.clear();
1531        self.scratchpad_path.add_circle(center, radius);
1532        self.scratchpad_path
1533            .fill_and_stroke(self.feathering, fill, &path_stroke, out);
1534    }
1535
1536    /// Tessellate a single [`EllipseShape`] into a [`Mesh`].
1537    ///
1538    /// * `shape`: the ellipse to tessellate.
1539    /// * `out`: triangles are appended to this.
1540    pub fn tessellate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) {
1541        let EllipseShape {
1542            center,
1543            radius,
1544            fill,
1545            stroke,
1546            angle,
1547        } = shape;
1548
1549        if radius.x <= 0.0 || radius.y <= 0.0 {
1550            return;
1551        }
1552
1553        if self.options.coarse_tessellation_culling
1554            && !self
1555                .clip_rect
1556                .expand2(radius + Vec2::splat(stroke.width))
1557                .contains(center)
1558        {
1559            return;
1560        }
1561
1562        // Get the max pixel radius
1563        let max_radius = (radius.max_elem() * self.pixels_per_point) as u32;
1564
1565        // Ensure there is at least 8 points in each quarter of the ellipse
1566        let num_points = u32::max(8, max_radius / 16);
1567
1568        // Create an ease ratio based the ellipses a and b
1569        let ratio = ((radius.y / radius.x) / 2.0).clamp(0.0, 1.0);
1570
1571        // Generate points between the 0 to pi/2
1572        let quarter: Vec<Vec2> = (1..num_points)
1573            .map(|i| {
1574                let percent = i as f32 / num_points as f32;
1575
1576                // Ease the percent value, concentrating points around tight bends
1577                let eased = 2.0 * (percent - percent.powf(2.0)) * ratio + percent.powf(2.0);
1578
1579                // Scale the ease to the quarter
1580                let t = eased * std::f32::consts::FRAC_PI_2;
1581                Vec2::new(radius.x * f32::cos(t), radius.y * f32::sin(t))
1582            })
1583            .collect();
1584
1585        // Build the ellipse from the 4 known vertices filling arcs between
1586        // them by mirroring the points between 0 and pi/2
1587        let mut points = Vec::new();
1588        points.push(center + Vec2::new(radius.x, 0.0));
1589        points.extend(quarter.iter().map(|p| center + *p));
1590        points.push(center + Vec2::new(0.0, radius.y));
1591        points.extend(quarter.iter().rev().map(|p| center + Vec2::new(-p.x, p.y)));
1592        points.push(center + Vec2::new(-radius.x, 0.0));
1593        points.extend(quarter.iter().map(|p| center - *p));
1594        points.push(center + Vec2::new(0.0, -radius.y));
1595        points.extend(quarter.iter().rev().map(|p| center + Vec2::new(p.x, -p.y)));
1596
1597        // Apply rotation if angle is non-zero
1598        if angle != 0.0 {
1599            let rot = emath::Rot2::from_angle(angle);
1600            for point in &mut points {
1601                *point = center + rot * (*point - center);
1602            }
1603        }
1604
1605        let path_stroke = PathStroke::from(stroke).outside();
1606        self.scratchpad_path.clear();
1607        self.scratchpad_path.add_line_loop(&points);
1608        self.scratchpad_path
1609            .fill_and_stroke(self.feathering, fill, &path_stroke, out);
1610    }
1611
1612    /// Tessellate a single [`Mesh`] into a [`Mesh`].
1613    ///
1614    /// * `mesh`: the mesh to tessellate.
1615    /// * `out`: triangles are appended to this.
1616    pub fn tessellate_mesh(&self, mesh: &Mesh, out: &mut Mesh) {
1617        if !mesh.is_valid() {
1618            debug_assert!(false, "Invalid Mesh in Shape::Mesh");
1619            return;
1620        }
1621
1622        if self.options.coarse_tessellation_culling
1623            && !self.clip_rect.intersects(mesh.calc_bounds())
1624        {
1625            return;
1626        }
1627
1628        out.append_ref(mesh);
1629    }
1630
1631    /// Tessellate a line segment between the two points with the given stroke into a [`Mesh`].
1632    ///
1633    /// * `shape`: the mesh to tessellate.
1634    /// * `out`: triangles are appended to this.
1635    pub fn tessellate_line_segment(
1636        &mut self,
1637        mut points: [Pos2; 2],
1638        stroke: impl Into<Stroke>,
1639        out: &mut Mesh,
1640    ) {
1641        let stroke = stroke.into();
1642        if stroke.is_empty() {
1643            return;
1644        }
1645
1646        if self.options.coarse_tessellation_culling
1647            && !self
1648                .clip_rect
1649                .intersects(Rect::from_two_pos(points[0], points[1]).expand(stroke.width))
1650        {
1651            return;
1652        }
1653
1654        if self.options.round_line_segments_to_pixels {
1655            let feathering = self.feathering;
1656            let pixels_per_point = self.pixels_per_point;
1657
1658            let quarter_pixel = 0.25 * feathering; // Used to avoid fence post problem.
1659
1660            let [a, b] = &mut points;
1661            if a.x == b.x {
1662                // Vertical line
1663                let mut x = a.x;
1664                stroke.round_center_to_pixel(self.pixels_per_point, &mut x);
1665                a.x = x;
1666                b.x = x;
1667
1668                // Often the ends of the line are exactly on a pixel boundary,
1669                // but we extend line segments with a cap that is a pixel wide…
1670                // Solution: first shrink the line segment (on each end),
1671                // then round to pixel center!
1672                // We shrink by half-a-pixel n total (a quarter on each end),
1673                // so that on average we avoid the fence-post-problem after rounding.
1674                if a.y < b.y {
1675                    a.y = (a.y + quarter_pixel).round_to_pixel_center(pixels_per_point);
1676                    b.y = (b.y - quarter_pixel).round_to_pixel_center(pixels_per_point);
1677                } else {
1678                    a.y = (a.y - quarter_pixel).round_to_pixel_center(pixels_per_point);
1679                    b.y = (b.y + quarter_pixel).round_to_pixel_center(pixels_per_point);
1680                }
1681            }
1682            if a.y == b.y {
1683                // Horizontal line
1684                let mut y = a.y;
1685                stroke.round_center_to_pixel(self.pixels_per_point, &mut y);
1686                a.y = y;
1687                b.y = y;
1688
1689                // See earlier comment for vertical lines
1690                if a.x < b.x {
1691                    a.x = (a.x + quarter_pixel).round_to_pixel_center(pixels_per_point);
1692                    b.x = (b.x - quarter_pixel).round_to_pixel_center(pixels_per_point);
1693                } else {
1694                    a.x = (a.x - quarter_pixel).round_to_pixel_center(pixels_per_point);
1695                    b.x = (b.x + quarter_pixel).round_to_pixel_center(pixels_per_point);
1696                }
1697            }
1698        }
1699
1700        self.scratchpad_path.clear();
1701        self.scratchpad_path.add_line_segment(points);
1702        self.scratchpad_path
1703            .stroke_open(self.feathering, &stroke.into(), out);
1704    }
1705
1706    #[deprecated = "Use `tessellate_line_segment` instead"]
1707    pub fn tessellate_line(
1708        &mut self,
1709        points: [Pos2; 2],
1710        stroke: impl Into<Stroke>,
1711        out: &mut Mesh,
1712    ) {
1713        self.tessellate_line_segment(points, stroke, out);
1714    }
1715
1716    /// Tessellate a single [`PathShape`] into a [`Mesh`].
1717    ///
1718    /// * `path_shape`: the path to tessellate.
1719    /// * `out`: triangles are appended to this.
1720    pub fn tessellate_path(&mut self, path_shape: &PathShape, out: &mut Mesh) {
1721        if path_shape.points.len() < 2 {
1722            return;
1723        }
1724
1725        if self.options.coarse_tessellation_culling
1726            && !path_shape.visual_bounding_rect().intersects(self.clip_rect)
1727        {
1728            return;
1729        }
1730
1731        profiling::function_scope!();
1732
1733        let PathShape {
1734            points,
1735            closed,
1736            fill,
1737            stroke,
1738        } = path_shape;
1739
1740        self.scratchpad_path.clear();
1741
1742        if *closed {
1743            self.scratchpad_path.add_line_loop(points);
1744
1745            self.scratchpad_path
1746                .fill_and_stroke(self.feathering, *fill, stroke, out);
1747        } else {
1748            debug_assert_eq!(
1749                *fill,
1750                Color32::TRANSPARENT,
1751                "You asked to fill a path that is not closed. That makes no sense."
1752            );
1753
1754            self.scratchpad_path.add_open_points(points);
1755
1756            self.scratchpad_path
1757                .stroke(self.feathering, PathType::Open, stroke, out);
1758        }
1759    }
1760
1761    /// Tessellate a single [`Rect`] into a [`Mesh`].
1762    ///
1763    /// * `rect`: the rectangle to tessellate.
1764    /// * `out`: triangles are appended to this.
1765    pub fn tessellate_rect(&mut self, rect_shape: &RectShape, out: &mut Mesh) {
1766        if self.options.coarse_tessellation_culling
1767            && !rect_shape.visual_bounding_rect().intersects(self.clip_rect)
1768        {
1769            return;
1770        }
1771
1772        let brush = rect_shape.brush.as_ref();
1773        let RectShape {
1774            mut rect,
1775            corner_radius,
1776            mut fill,
1777            mut stroke,
1778            mut stroke_kind,
1779            round_to_pixels,
1780            mut blur_width,
1781            brush: _, // brush is extracted on its own, because it is not Copy
1782            angle,
1783        } = *rect_shape;
1784
1785        let mut corner_radius = CornerRadiusF32::from(corner_radius);
1786        let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels);
1787
1788        if stroke.width == 0.0 {
1789            stroke.color = Color32::TRANSPARENT;
1790        }
1791
1792        // It is common to (sometimes accidentally) create an infinitely sized rectangle.
1793        // Make sure we can handle that:
1794        rect.min = rect.min.at_least(pos2(-1e7, -1e7));
1795        rect.max = rect.max.at_most(pos2(1e7, 1e7));
1796
1797        if !stroke.is_empty() {
1798            // Check if the stroke covers the whole rectangle
1799            let rect_with_stroke = match stroke_kind {
1800                StrokeKind::Inside => rect,
1801                StrokeKind::Middle => rect.expand(stroke.width / 2.0),
1802                StrokeKind::Outside => rect.expand(stroke.width),
1803            };
1804
1805            if rect_with_stroke.size().min_elem() <= 2.0 * stroke.width + 0.5 * self.feathering {
1806                // The stroke covers the fill.
1807                // Change this to be a fill-only shape, using the stroke color as the new fill color.
1808                rect = rect_with_stroke;
1809
1810                // We blend so that if the stroke is semi-transparent,
1811                // the fill still shines through.
1812                fill = stroke.color;
1813
1814                stroke = Stroke::NONE;
1815            }
1816        }
1817
1818        if stroke.is_empty() && out.texture_id == TextureId::default() {
1819            // Approximate thin rectangles with line segments.
1820            // This is important so that thin rectangles look good.
1821            if rect.width() <= 2.0 * self.feathering {
1822                return self.tessellate_line_segment(
1823                    [rect.center_top(), rect.center_bottom()],
1824                    (rect.width(), fill),
1825                    out,
1826                );
1827            }
1828            if rect.height() <= 2.0 * self.feathering {
1829                return self.tessellate_line_segment(
1830                    [rect.left_center(), rect.right_center()],
1831                    (rect.height(), fill),
1832                    out,
1833                );
1834            }
1835        }
1836
1837        // Important: round to pixels BEFORE modifying/applying stroke_kind
1838        if round_to_pixels {
1839            // The rounding is aware of the stroke kind.
1840            // It is designed to be clever in trying to divine the intentions of the user.
1841            match stroke_kind {
1842                StrokeKind::Inside => {
1843                    // The stroke is inside the rect, so the rect defines the _outside_ of the stroke.
1844                    // We round the outside of the stroke on a pixel boundary.
1845                    // This will make the outside of the stroke crisp.
1846                    //
1847                    // Will make each stroke asymmetric if not an even multiple of physical pixels,
1848                    // but the left stroke will always be the mirror image of the right stroke,
1849                    // and the top stroke will always be the mirror image of the bottom stroke.
1850                    //
1851                    // This is so that a user can tile rectangles with `StrokeKind::Inside`,
1852                    // and get no pixel overlap between them.
1853                    rect = rect.round_to_pixels(self.pixels_per_point);
1854                }
1855                StrokeKind::Middle => {
1856                    // On this path we optimize for crisp and symmetric strokes.
1857                    stroke.round_rect_to_pixel(self.pixels_per_point, &mut rect);
1858                }
1859                StrokeKind::Outside => {
1860                    // Put the inside of the stroke on a pixel boundary.
1861                    // Makes the inside of the stroke and the filled rect crisp,
1862                    // but the outside of the stroke may become feathered (blurry).
1863                    //
1864                    // Will make each stroke asymmetric if not an even multiple of physical pixels,
1865                    // but the left stroke will always be the mirror image of the right stroke,
1866                    // and the top stroke will always be the mirror image of the bottom stroke.
1867                    rect = rect.round_to_pixels(self.pixels_per_point);
1868                }
1869            }
1870        }
1871
1872        let old_feathering = self.feathering;
1873
1874        if self.feathering < blur_width {
1875            // We accomplish the blur by using a larger-than-normal feathering.
1876            // Feathering is usually used to make the edges of a shape softer for anti-aliasing.
1877
1878            // The tessellator can't handle blurring/feathering larger than the smallest side of the rect.
1879            let eps = 0.1; // avoid numerical problems
1880            blur_width = blur_width
1881                .at_most(rect.size().min_elem() - eps - 2.0 * stroke.width)
1882                .at_least(0.0);
1883
1884            corner_radius += 0.5 * blur_width;
1885
1886            self.feathering = self.feathering.max(blur_width);
1887        }
1888
1889        {
1890            // Modify `rect` so that it represents the OUTER border
1891            // We do this because `path::rounded_rectangle` uses the
1892            // corner radius to pick the fidelity/resolution of the corner.
1893
1894            let original_cr = corner_radius;
1895
1896            match stroke_kind {
1897                StrokeKind::Inside => {}
1898                StrokeKind::Middle => {
1899                    rect = rect.expand(stroke.width / 2.0);
1900                    corner_radius += stroke.width / 2.0;
1901                }
1902                StrokeKind::Outside => {
1903                    rect = rect.expand(stroke.width);
1904                    corner_radius += stroke.width;
1905                }
1906            }
1907
1908            stroke_kind = StrokeKind::Inside;
1909
1910            // A small corner_radius is incompatible with a wide stroke,
1911            // because the small bend will be extruded inwards and cross itself.
1912            // There are two ways to solve this (wile maintaining constant stroke width):
1913            // either we increase the corner_radius, or we set it to zero.
1914            // We choose the former: if the user asks for _any_ corner_radius, they should get it.
1915
1916            let min_inside_cr = 0.1; // Large enough to avoid numerical issues
1917            let min_outside_cr = stroke.width + min_inside_cr;
1918
1919            let extra_cr_tweak = 0.4; // Otherwise is doesn't _feels_  enough.
1920
1921            if original_cr.nw == 0.0 {
1922                corner_radius.nw = 0.0;
1923            } else {
1924                corner_radius.nw += extra_cr_tweak;
1925                corner_radius.nw = corner_radius.nw.at_least(min_outside_cr);
1926            }
1927            if original_cr.ne == 0.0 {
1928                corner_radius.ne = 0.0;
1929            } else {
1930                corner_radius.ne += extra_cr_tweak;
1931                corner_radius.ne = corner_radius.ne.at_least(min_outside_cr);
1932            }
1933            if original_cr.sw == 0.0 {
1934                corner_radius.sw = 0.0;
1935            } else {
1936                corner_radius.sw += extra_cr_tweak;
1937                corner_radius.sw = corner_radius.sw.at_least(min_outside_cr);
1938            }
1939            if original_cr.se == 0.0 {
1940                corner_radius.se = 0.0;
1941            } else {
1942                corner_radius.se += extra_cr_tweak;
1943                corner_radius.se = corner_radius.se.at_least(min_outside_cr);
1944            }
1945        }
1946
1947        let path = &mut self.scratchpad_path;
1948        path.clear();
1949        path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius);
1950
1951        // Apply rotation if angle is non-zero
1952        if angle != 0.0 {
1953            let rot = emath::Rot2::from_angle(angle);
1954            let center = rect.center();
1955            for point in &mut self.scratchpad_points {
1956                *point = center + rot * (*point - center);
1957            }
1958        }
1959
1960        path.add_line_loop(&self.scratchpad_points);
1961
1962        let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind);
1963
1964        if let Some(brush) = brush {
1965            // Textured fill
1966
1967            let fill_rect = match stroke_kind {
1968                StrokeKind::Inside => rect.shrink(stroke.width),
1969                StrokeKind::Middle => rect.shrink(stroke.width / 2.0),
1970                StrokeKind::Outside => rect,
1971            };
1972
1973            if fill_rect.is_positive() {
1974                let crate::Brush {
1975                    fill_texture_id,
1976                    uv,
1977                } = **brush;
1978                let uv_from_pos = |p: Pos2| {
1979                    pos2(
1980                        remap(p.x, rect.x_range(), uv.x_range()),
1981                        remap(p.y, rect.y_range(), uv.y_range()),
1982                    )
1983                };
1984                path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out);
1985            }
1986
1987            if !stroke.is_empty() {
1988                path.stroke_closed(self.feathering, &path_stroke, out);
1989            }
1990        } else {
1991            // Stroke and maybe fill
1992            path.fill_and_stroke(self.feathering, fill, &path_stroke, out);
1993        }
1994
1995        self.feathering = old_feathering; // restore
1996    }
1997
1998    /// Tessellate a single [`TextShape`] into a [`Mesh`].
1999    /// * `text_shape`: the text to tessellate.
2000    /// * `out`: triangles are appended to this.
2001    pub fn tessellate_text(&mut self, text_shape: &TextShape, out: &mut Mesh) {
2002        let TextShape {
2003            pos: galley_pos,
2004            galley,
2005            underline,
2006            override_text_color,
2007            fallback_color,
2008            opacity_factor,
2009            angle,
2010        } = text_shape;
2011
2012        if galley.is_empty() {
2013            return;
2014        }
2015
2016        if *opacity_factor <= 0.0 {
2017            return;
2018        }
2019
2020        if galley.pixels_per_point != self.pixels_per_point {
2021            log::warn!(
2022                "epaint: WARNING: pixels_per_point (dpi scale) have changed between text layout and tessellation. \
2023                       You must recreate your text shapes if pixels_per_point changes."
2024            );
2025        }
2026
2027        out.vertices.reserve(galley.num_vertices);
2028        out.indices.reserve(galley.num_indices);
2029
2030        // The contents of the galley are already snapped to pixel coordinates,
2031        // but we need to make sure the galley ends up on the start of a physical pixel:
2032        let galley_pos = if self.options.round_text_to_pixels {
2033            galley_pos.round_to_pixels(self.pixels_per_point)
2034        } else {
2035            *galley_pos
2036        };
2037
2038        let uv_normalizer = vec2(
2039            1.0 / self.font_tex_size[0] as f32,
2040            1.0 / self.font_tex_size[1] as f32,
2041        );
2042
2043        let rotator = Rot2::from_angle(*angle);
2044
2045        for row in &galley.rows {
2046            if row.visuals.mesh.is_empty() {
2047                continue;
2048            }
2049
2050            let final_row_pos = galley_pos + rotator * row.pos.to_vec2();
2051
2052            let mut row_rect = row.visuals.mesh_bounds;
2053            if *angle != 0.0 {
2054                row_rect = row_rect.rotate_bb(rotator);
2055            }
2056            row_rect = row_rect.translate(final_row_pos.to_vec2());
2057
2058            if self.options.coarse_tessellation_culling && !self.clip_rect.intersects(row_rect) {
2059                // culling individual lines of text is important, since a single `Shape::Text`
2060                // can span hundreds of lines.
2061                continue;
2062            }
2063
2064            let index_offset = out.vertices.len() as u32;
2065
2066            out.indices.extend(
2067                row.visuals
2068                    .mesh
2069                    .indices
2070                    .iter()
2071                    .map(|index| index + index_offset),
2072            );
2073
2074            out.vertices.extend(
2075                row.visuals
2076                    .mesh
2077                    .vertices
2078                    .iter()
2079                    .enumerate()
2080                    .map(|(i, vertex)| {
2081                        let Vertex { pos, uv, mut color } = *vertex;
2082
2083                        if let Some(override_text_color) = override_text_color {
2084                            // Only override the glyph color (not background color, strike-through color, etc)
2085                            if row.visuals.glyph_vertex_range.contains(&i) {
2086                                color = *override_text_color;
2087                            }
2088                        } else if color == Color32::PLACEHOLDER {
2089                            color = *fallback_color;
2090                        }
2091
2092                        if *opacity_factor < 1.0 {
2093                            color = color.gamma_multiply(*opacity_factor);
2094                        }
2095
2096                        debug_assert!(color != Color32::PLACEHOLDER, "A placeholder color made it to the tessellator. You forgot to set a fallback color.");
2097
2098                        let offset = if *angle == 0.0 {
2099                            pos.to_vec2()
2100                        } else {
2101                            rotator * pos.to_vec2()
2102                        };
2103
2104                        Vertex {
2105                            pos: final_row_pos + offset,
2106                            uv: (uv.to_vec2() * uv_normalizer).to_pos2(),
2107                            color,
2108                        }
2109                    }),
2110            );
2111
2112            if *underline != Stroke::NONE {
2113                self.tessellate_line_segment(
2114                    [row_rect.left_bottom(), row_rect.right_bottom()],
2115                    *underline,
2116                    out,
2117                );
2118            }
2119        }
2120    }
2121
2122    /// Tessellate a single [`QuadraticBezierShape`] into a [`Mesh`].
2123    ///
2124    /// * `quadratic_shape`: the shape to tessellate.
2125    /// * `out`: triangles are appended to this.
2126    pub fn tessellate_quadratic_bezier(
2127        &mut self,
2128        quadratic_shape: &QuadraticBezierShape,
2129        out: &mut Mesh,
2130    ) {
2131        let options = &self.options;
2132        let clip_rect = self.clip_rect;
2133
2134        if options.coarse_tessellation_culling
2135            && !quadratic_shape.visual_bounding_rect().intersects(clip_rect)
2136        {
2137            return;
2138        }
2139
2140        let points = quadratic_shape.flatten(Some(options.bezier_tolerance));
2141
2142        self.tessellate_bezier_complete(
2143            &points,
2144            quadratic_shape.fill,
2145            quadratic_shape.closed,
2146            &quadratic_shape.stroke,
2147            out,
2148        );
2149    }
2150
2151    /// Tessellate a single [`CubicBezierShape`] into a [`Mesh`].
2152    ///
2153    /// * `cubic_shape`: the shape to tessellate.
2154    /// * `out`: triangles are appended to this.
2155    pub fn tessellate_cubic_bezier(&mut self, cubic_shape: &CubicBezierShape, out: &mut Mesh) {
2156        let options = &self.options;
2157        let clip_rect = self.clip_rect;
2158        if options.coarse_tessellation_culling
2159            && !cubic_shape.visual_bounding_rect().intersects(clip_rect)
2160        {
2161            return;
2162        }
2163
2164        let points_vec =
2165            cubic_shape.flatten_closed(Some(options.bezier_tolerance), Some(options.epsilon));
2166
2167        for points in points_vec {
2168            self.tessellate_bezier_complete(
2169                &points,
2170                cubic_shape.fill,
2171                cubic_shape.closed,
2172                &cubic_shape.stroke,
2173                out,
2174            );
2175        }
2176    }
2177
2178    fn tessellate_bezier_complete(
2179        &mut self,
2180        points: &[Pos2],
2181        fill: Color32,
2182        closed: bool,
2183        stroke: &PathStroke,
2184        out: &mut Mesh,
2185    ) {
2186        if points.len() < 2 {
2187            return;
2188        }
2189
2190        self.scratchpad_path.clear();
2191        if closed {
2192            self.scratchpad_path.add_line_loop(points);
2193
2194            self.scratchpad_path
2195                .fill_and_stroke(self.feathering, fill, stroke, out);
2196        } else {
2197            debug_assert_eq!(
2198                fill,
2199                Color32::TRANSPARENT,
2200                "You asked to fill a bezier path that is not closed. That makes no sense."
2201            );
2202
2203            self.scratchpad_path.add_open_points(points);
2204
2205            self.scratchpad_path
2206                .stroke(self.feathering, PathType::Open, stroke, out);
2207        }
2208    }
2209}
2210
2211impl Tessellator {
2212    /// Turns [`Shape`]:s into sets of triangles.
2213    ///
2214    /// The given shapes will tessellated in the same order as they are given.
2215    /// They will be batched together by clip rectangle.
2216    ///
2217    /// * `pixels_per_point`: number of physical pixels to each logical point
2218    /// * `options`: tessellation quality
2219    /// * `shapes`: what to tessellate
2220    /// * `font_tex_size`: size of the font texture. Required to normalize glyph uv rectangles when tessellating text.
2221    /// * `prepared_discs`: What [`crate::TextureAtlas::prepared_discs`] returns. Can safely be set to an empty vec.
2222    ///
2223    /// The implementation uses a [`Tessellator`].
2224    ///
2225    /// ## Returns
2226    /// A list of clip rectangles with matching [`Mesh`].
2227    #[allow(clippy::allow_attributes, unused_mut)]
2228    pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {
2229        profiling::function_scope!();
2230
2231        #[cfg(feature = "rayon")]
2232        if self.options.parallel_tessellation {
2233            self.parallel_tessellation_of_large_shapes(&mut shapes);
2234        }
2235
2236        let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default();
2237
2238        {
2239            profiling::scope!("tessellate");
2240            for clipped_shape in shapes {
2241                self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives);
2242            }
2243        }
2244
2245        if self.options.debug_paint_clip_rects {
2246            clipped_primitives = self.add_clip_rects(clipped_primitives);
2247        }
2248
2249        if self.options.debug_ignore_clip_rects {
2250            for clipped_primitive in &mut clipped_primitives {
2251                clipped_primitive.clip_rect = Rect::EVERYTHING;
2252            }
2253        }
2254
2255        clipped_primitives.retain(|p| {
2256            p.clip_rect.is_positive()
2257                && match &p.primitive {
2258                    Primitive::Mesh(mesh) => !mesh.is_empty(),
2259                    Primitive::Callback(_) => true,
2260                }
2261        });
2262
2263        for clipped_primitive in &clipped_primitives {
2264            if let Primitive::Mesh(mesh) = &clipped_primitive.primitive {
2265                debug_assert!(mesh.is_valid(), "Tessellator generated invalid Mesh");
2266            }
2267        }
2268
2269        clipped_primitives
2270    }
2271
2272    /// Find large shapes and throw them on the rayon thread pool,
2273    /// then replace the original shape with their tessellated meshes.
2274    #[cfg(feature = "rayon")]
2275    fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) {
2276        profiling::function_scope!();
2277
2278        use rayon::prelude::*;
2279
2280        // We only parallelize large/slow stuff, because each tessellation job
2281        // will allocate a new Mesh, and so it creates a lot of extra memory fragmentation
2282        // and allocations that is only worth it for large shapes.
2283        fn should_parallelize(shape: &Shape) -> bool {
2284            match shape {
2285                Shape::Vec(shapes) => 4 < shapes.len() || shapes.iter().any(should_parallelize),
2286
2287                Shape::Path(path_shape) => 32 < path_shape.points.len(),
2288
2289                Shape::QuadraticBezier(_) | Shape::CubicBezier(_) | Shape::Ellipse(_) => true,
2290
2291                Shape::Noop
2292                | Shape::Text(_)
2293                | Shape::Circle(_)
2294                | Shape::Mesh(_)
2295                | Shape::LineSegment { .. }
2296                | Shape::Rect(_)
2297                | Shape::Callback(_) => false,
2298            }
2299        }
2300
2301        let tessellated: Vec<(usize, Mesh)> = shapes
2302            .par_iter()
2303            .enumerate()
2304            .filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape))
2305            .map(|(index, clipped_shape)| {
2306                profiling::scope!("tessellate_big_shape");
2307                // TODO(emilk): reuse tessellator in a thread local
2308                let mut tessellator = (*self).clone();
2309                let mut mesh = Mesh::default();
2310                tessellator.tessellate_shape(clipped_shape.shape.clone(), &mut mesh);
2311                (index, mesh)
2312            })
2313            .collect();
2314
2315        profiling::scope!("distribute results", tessellated.len().to_string());
2316        for (index, mesh) in tessellated {
2317            shapes[index].shape = Shape::Mesh(mesh.into());
2318        }
2319    }
2320
2321    fn add_clip_rects(
2322        &mut self,
2323        clipped_primitives: Vec<ClippedPrimitive>,
2324    ) -> Vec<ClippedPrimitive> {
2325        self.clip_rect = Rect::EVERYTHING;
2326        let stroke = Stroke::new(2.0, Color32::from_rgb(150, 255, 150));
2327
2328        clipped_primitives
2329            .into_iter()
2330            .flat_map(|clipped_primitive| {
2331                let mut clip_rect_mesh = Mesh::default();
2332                self.tessellate_shape(
2333                    Shape::rect_stroke(
2334                        clipped_primitive.clip_rect,
2335                        0.0,
2336                        stroke,
2337                        StrokeKind::Outside,
2338                    ),
2339                    &mut clip_rect_mesh,
2340                );
2341
2342                [
2343                    clipped_primitive,
2344                    ClippedPrimitive {
2345                        clip_rect: Rect::EVERYTHING, // whatever
2346                        primitive: Primitive::Mesh(clip_rect_mesh),
2347                    },
2348                ]
2349            })
2350            .collect()
2351    }
2352}
2353
2354#[test]
2355fn test_tessellator() {
2356    use crate::*;
2357
2358    let mut shapes = Vec::with_capacity(2);
2359
2360    let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
2361    let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
2362
2363    let mut mesh = Mesh::with_texture(TextureId::Managed(1));
2364    mesh.add_rect_with_uv(rect, uv, Color32::WHITE);
2365    shapes.push(Shape::mesh(mesh));
2366
2367    let mut mesh = Mesh::with_texture(TextureId::Managed(2));
2368    mesh.add_rect_with_uv(rect, uv, Color32::WHITE);
2369    shapes.push(Shape::mesh(mesh));
2370
2371    let shape = Shape::Vec(shapes);
2372    let clipped_shapes = vec![ClippedShape {
2373        clip_rect: rect,
2374        shape,
2375    }];
2376
2377    let font_tex_size = [1024, 1024]; // unused
2378    let prepared_discs = vec![]; // unused
2379
2380    let primitives = Tessellator::new(1.0, Default::default(), font_tex_size, prepared_discs)
2381        .tessellate_shapes(clipped_shapes);
2382
2383    assert_eq!(primitives.len(), 2);
2384}
2385
2386#[test]
2387fn path_bounding_box() {
2388    use crate::*;
2389
2390    for i in 1..=100 {
2391        let width = i as f32;
2392
2393        let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(10.0, 10.0));
2394        let expected_rect = rect.expand((width / 2.0) + 1.5);
2395
2396        let mut mesh = Mesh::default();
2397
2398        let mut path = Path::default();
2399        path.add_open_points(&[
2400            pos2(0.0, 0.0),
2401            pos2(2.0, 0.0),
2402            pos2(5.0, 5.0),
2403            pos2(0.0, 5.0),
2404            pos2(0.0, 7.0),
2405            pos2(10.0, 10.0),
2406        ]);
2407
2408        path.stroke(
2409            1.5,
2410            PathType::Closed,
2411            &PathStroke::new_uv(width, move |r, p| {
2412                assert_eq!(r, expected_rect);
2413                // see https://github.com/emilk/egui/pull/4353#discussion_r1573879940 for why .contains() isn't used here.
2414                // TL;DR rounding errors.
2415                assert!(
2416                    r.distance_to_pos(p) <= 0.55,
2417                    "passed rect {r:?} didn't contain point {p:?} (distance: {})",
2418                    r.distance_to_pos(p)
2419                );
2420                assert!(
2421                    expected_rect.distance_to_pos(p) <= 0.55,
2422                    "expected rect {expected_rect:?} didn't contain point {p:?}"
2423                );
2424                Color32::WHITE
2425            }),
2426            &mut mesh,
2427        );
2428    }
2429}