Skip to main content

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