1#![expect(clippy::identity_op)]
7
8use emath::{GuiRounding as _, NumExt as _, Pos2, Rect, Rot2, Vec2, pos2, remap, vec2};
9
10use crate::{
11 CircleShape, ClippedPrimitive, ClippedShape, Color32, CornerRadiusF32, CubicBezierShape,
12 EllipseShape, Mesh, PathShape, Primitive, QuadraticBezierShape, RectShape, Shape, Stroke,
13 StrokeKind, TextShape, TextureId, Vertex, color::ColorMode, emath, stroke::PathStroke,
14 texture_atlas::PreparedDisc,
15};
16
17#[expect(clippy::approx_constant)]
20mod precomputed_vertices {
21 use emath::{Vec2, vec2};
32
33 pub const CIRCLE_8: [Vec2; 9] = [
34 vec2(1.000000, 0.000000),
35 vec2(0.707107, 0.707107),
36 vec2(0.000000, 1.000000),
37 vec2(-0.707107, 0.707107),
38 vec2(-1.000000, 0.000000),
39 vec2(-0.707107, -0.707107),
40 vec2(0.000000, -1.000000),
41 vec2(0.707107, -0.707107),
42 vec2(1.000000, 0.000000),
43 ];
44
45 pub const CIRCLE_16: [Vec2; 17] = [
46 vec2(1.000000, 0.000000),
47 vec2(0.923880, 0.382683),
48 vec2(0.707107, 0.707107),
49 vec2(0.382683, 0.923880),
50 vec2(0.000000, 1.000000),
51 vec2(-0.382684, 0.923880),
52 vec2(-0.707107, 0.707107),
53 vec2(-0.923880, 0.382683),
54 vec2(-1.000000, 0.000000),
55 vec2(-0.923880, -0.382683),
56 vec2(-0.707107, -0.707107),
57 vec2(-0.382684, -0.923880),
58 vec2(0.000000, -1.000000),
59 vec2(0.382684, -0.923879),
60 vec2(0.707107, -0.707107),
61 vec2(0.923880, -0.382683),
62 vec2(1.000000, 0.000000),
63 ];
64
65 pub const CIRCLE_32: [Vec2; 33] = [
66 vec2(1.000000, 0.000000),
67 vec2(0.980785, 0.195090),
68 vec2(0.923880, 0.382683),
69 vec2(0.831470, 0.555570),
70 vec2(0.707107, 0.707107),
71 vec2(0.555570, 0.831470),
72 vec2(0.382683, 0.923880),
73 vec2(0.195090, 0.980785),
74 vec2(0.000000, 1.000000),
75 vec2(-0.195090, 0.980785),
76 vec2(-0.382683, 0.923880),
77 vec2(-0.555570, 0.831470),
78 vec2(-0.707107, 0.707107),
79 vec2(-0.831470, 0.555570),
80 vec2(-0.923880, 0.382683),
81 vec2(-0.980785, 0.195090),
82 vec2(-1.000000, 0.000000),
83 vec2(-0.980785, -0.195090),
84 vec2(-0.923880, -0.382683),
85 vec2(-0.831470, -0.555570),
86 vec2(-0.707107, -0.707107),
87 vec2(-0.555570, -0.831470),
88 vec2(-0.382683, -0.923880),
89 vec2(-0.195090, -0.980785),
90 vec2(-0.000000, -1.000000),
91 vec2(0.195090, -0.980785),
92 vec2(0.382683, -0.923880),
93 vec2(0.555570, -0.831470),
94 vec2(0.707107, -0.707107),
95 vec2(0.831470, -0.555570),
96 vec2(0.923880, -0.382683),
97 vec2(0.980785, -0.195090),
98 vec2(1.000000, -0.000000),
99 ];
100
101 pub const CIRCLE_64: [Vec2; 65] = [
102 vec2(1.000000, 0.000000),
103 vec2(0.995185, 0.098017),
104 vec2(0.980785, 0.195090),
105 vec2(0.956940, 0.290285),
106 vec2(0.923880, 0.382683),
107 vec2(0.881921, 0.471397),
108 vec2(0.831470, 0.555570),
109 vec2(0.773010, 0.634393),
110 vec2(0.707107, 0.707107),
111 vec2(0.634393, 0.773010),
112 vec2(0.555570, 0.831470),
113 vec2(0.471397, 0.881921),
114 vec2(0.382683, 0.923880),
115 vec2(0.290285, 0.956940),
116 vec2(0.195090, 0.980785),
117 vec2(0.098017, 0.995185),
118 vec2(0.000000, 1.000000),
119 vec2(-0.098017, 0.995185),
120 vec2(-0.195090, 0.980785),
121 vec2(-0.290285, 0.956940),
122 vec2(-0.382683, 0.923880),
123 vec2(-0.471397, 0.881921),
124 vec2(-0.555570, 0.831470),
125 vec2(-0.634393, 0.773010),
126 vec2(-0.707107, 0.707107),
127 vec2(-0.773010, 0.634393),
128 vec2(-0.831470, 0.555570),
129 vec2(-0.881921, 0.471397),
130 vec2(-0.923880, 0.382683),
131 vec2(-0.956940, 0.290285),
132 vec2(-0.980785, 0.195090),
133 vec2(-0.995185, 0.098017),
134 vec2(-1.000000, 0.000000),
135 vec2(-0.995185, -0.098017),
136 vec2(-0.980785, -0.195090),
137 vec2(-0.956940, -0.290285),
138 vec2(-0.923880, -0.382683),
139 vec2(-0.881921, -0.471397),
140 vec2(-0.831470, -0.555570),
141 vec2(-0.773010, -0.634393),
142 vec2(-0.707107, -0.707107),
143 vec2(-0.634393, -0.773010),
144 vec2(-0.555570, -0.831470),
145 vec2(-0.471397, -0.881921),
146 vec2(-0.382683, -0.923880),
147 vec2(-0.290285, -0.956940),
148 vec2(-0.195090, -0.980785),
149 vec2(-0.098017, -0.995185),
150 vec2(-0.000000, -1.000000),
151 vec2(0.098017, -0.995185),
152 vec2(0.195090, -0.980785),
153 vec2(0.290285, -0.956940),
154 vec2(0.382683, -0.923880),
155 vec2(0.471397, -0.881921),
156 vec2(0.555570, -0.831470),
157 vec2(0.634393, -0.773010),
158 vec2(0.707107, -0.707107),
159 vec2(0.773010, -0.634393),
160 vec2(0.831470, -0.555570),
161 vec2(0.881921, -0.471397),
162 vec2(0.923880, -0.382683),
163 vec2(0.956940, -0.290285),
164 vec2(0.980785, -0.195090),
165 vec2(0.995185, -0.098017),
166 vec2(1.000000, -0.000000),
167 ];
168
169 pub const CIRCLE_128: [Vec2; 129] = [
170 vec2(1.000000, 0.000000),
171 vec2(0.998795, 0.049068),
172 vec2(0.995185, 0.098017),
173 vec2(0.989177, 0.146730),
174 vec2(0.980785, 0.195090),
175 vec2(0.970031, 0.242980),
176 vec2(0.956940, 0.290285),
177 vec2(0.941544, 0.336890),
178 vec2(0.923880, 0.382683),
179 vec2(0.903989, 0.427555),
180 vec2(0.881921, 0.471397),
181 vec2(0.857729, 0.514103),
182 vec2(0.831470, 0.555570),
183 vec2(0.803208, 0.595699),
184 vec2(0.773010, 0.634393),
185 vec2(0.740951, 0.671559),
186 vec2(0.707107, 0.707107),
187 vec2(0.671559, 0.740951),
188 vec2(0.634393, 0.773010),
189 vec2(0.595699, 0.803208),
190 vec2(0.555570, 0.831470),
191 vec2(0.514103, 0.857729),
192 vec2(0.471397, 0.881921),
193 vec2(0.427555, 0.903989),
194 vec2(0.382683, 0.923880),
195 vec2(0.336890, 0.941544),
196 vec2(0.290285, 0.956940),
197 vec2(0.242980, 0.970031),
198 vec2(0.195090, 0.980785),
199 vec2(0.146730, 0.989177),
200 vec2(0.098017, 0.995185),
201 vec2(0.049068, 0.998795),
202 vec2(0.000000, 1.000000),
203 vec2(-0.049068, 0.998795),
204 vec2(-0.098017, 0.995185),
205 vec2(-0.146730, 0.989177),
206 vec2(-0.195090, 0.980785),
207 vec2(-0.242980, 0.970031),
208 vec2(-0.290285, 0.956940),
209 vec2(-0.336890, 0.941544),
210 vec2(-0.382683, 0.923880),
211 vec2(-0.427555, 0.903989),
212 vec2(-0.471397, 0.881921),
213 vec2(-0.514103, 0.857729),
214 vec2(-0.555570, 0.831470),
215 vec2(-0.595699, 0.803208),
216 vec2(-0.634393, 0.773010),
217 vec2(-0.671559, 0.740951),
218 vec2(-0.707107, 0.707107),
219 vec2(-0.740951, 0.671559),
220 vec2(-0.773010, 0.634393),
221 vec2(-0.803208, 0.595699),
222 vec2(-0.831470, 0.555570),
223 vec2(-0.857729, 0.514103),
224 vec2(-0.881921, 0.471397),
225 vec2(-0.903989, 0.427555),
226 vec2(-0.923880, 0.382683),
227 vec2(-0.941544, 0.336890),
228 vec2(-0.956940, 0.290285),
229 vec2(-0.970031, 0.242980),
230 vec2(-0.980785, 0.195090),
231 vec2(-0.989177, 0.146730),
232 vec2(-0.995185, 0.098017),
233 vec2(-0.998795, 0.049068),
234 vec2(-1.000000, 0.000000),
235 vec2(-0.998795, -0.049068),
236 vec2(-0.995185, -0.098017),
237 vec2(-0.989177, -0.146730),
238 vec2(-0.980785, -0.195090),
239 vec2(-0.970031, -0.242980),
240 vec2(-0.956940, -0.290285),
241 vec2(-0.941544, -0.336890),
242 vec2(-0.923880, -0.382683),
243 vec2(-0.903989, -0.427555),
244 vec2(-0.881921, -0.471397),
245 vec2(-0.857729, -0.514103),
246 vec2(-0.831470, -0.555570),
247 vec2(-0.803208, -0.595699),
248 vec2(-0.773010, -0.634393),
249 vec2(-0.740951, -0.671559),
250 vec2(-0.707107, -0.707107),
251 vec2(-0.671559, -0.740951),
252 vec2(-0.634393, -0.773010),
253 vec2(-0.595699, -0.803208),
254 vec2(-0.555570, -0.831470),
255 vec2(-0.514103, -0.857729),
256 vec2(-0.471397, -0.881921),
257 vec2(-0.427555, -0.903989),
258 vec2(-0.382683, -0.923880),
259 vec2(-0.336890, -0.941544),
260 vec2(-0.290285, -0.956940),
261 vec2(-0.242980, -0.970031),
262 vec2(-0.195090, -0.980785),
263 vec2(-0.146730, -0.989177),
264 vec2(-0.098017, -0.995185),
265 vec2(-0.049068, -0.998795),
266 vec2(-0.000000, -1.000000),
267 vec2(0.049068, -0.998795),
268 vec2(0.098017, -0.995185),
269 vec2(0.146730, -0.989177),
270 vec2(0.195090, -0.980785),
271 vec2(0.242980, -0.970031),
272 vec2(0.290285, -0.956940),
273 vec2(0.336890, -0.941544),
274 vec2(0.382683, -0.923880),
275 vec2(0.427555, -0.903989),
276 vec2(0.471397, -0.881921),
277 vec2(0.514103, -0.857729),
278 vec2(0.555570, -0.831470),
279 vec2(0.595699, -0.803208),
280 vec2(0.634393, -0.773010),
281 vec2(0.671559, -0.740951),
282 vec2(0.707107, -0.707107),
283 vec2(0.740951, -0.671559),
284 vec2(0.773010, -0.634393),
285 vec2(0.803208, -0.595699),
286 vec2(0.831470, -0.555570),
287 vec2(0.857729, -0.514103),
288 vec2(0.881921, -0.471397),
289 vec2(0.903989, -0.427555),
290 vec2(0.923880, -0.382683),
291 vec2(0.941544, -0.336890),
292 vec2(0.956940, -0.290285),
293 vec2(0.970031, -0.242980),
294 vec2(0.980785, -0.195090),
295 vec2(0.989177, -0.146730),
296 vec2(0.995185, -0.098017),
297 vec2(0.998795, -0.049068),
298 vec2(1.000000, -0.000000),
299 ];
300}
301
302#[derive(Clone, Copy, Debug, Default, PartialEq)]
305struct PathPoint {
306 pos: Pos2,
307
308 normal: Vec2,
318}
319
320#[derive(Clone, Debug, Default)]
324pub struct Path(Vec<PathPoint>);
325
326impl Path {
327 #[inline(always)]
328 pub fn clear(&mut self) {
329 self.0.clear();
330 }
331
332 #[inline(always)]
333 pub fn reserve(&mut self, additional: usize) {
334 self.0.reserve(additional);
335 }
336
337 #[inline(always)]
338 pub fn add_point(&mut self, pos: Pos2, normal: Vec2) {
339 self.0.push(PathPoint { pos, normal });
340 }
341
342 pub fn add_circle(&mut self, center: Pos2, radius: f32) {
343 use precomputed_vertices::{CIRCLE_8, CIRCLE_16, CIRCLE_32, CIRCLE_64, CIRCLE_128};
344
345 if radius <= 2.0 {
349 self.0.extend(CIRCLE_8.iter().map(|&n| PathPoint {
350 pos: center + radius * n,
351 normal: n,
352 }));
353 } else if radius <= 5.0 {
354 self.0.extend(CIRCLE_16.iter().map(|&n| PathPoint {
355 pos: center + radius * n,
356 normal: n,
357 }));
358 } else if radius < 18.0 {
359 self.0.extend(CIRCLE_32.iter().map(|&n| PathPoint {
360 pos: center + radius * n,
361 normal: n,
362 }));
363 } else if radius < 50.0 {
364 self.0.extend(CIRCLE_64.iter().map(|&n| PathPoint {
365 pos: center + radius * n,
366 normal: n,
367 }));
368 } else {
369 self.0.extend(CIRCLE_128.iter().map(|&n| PathPoint {
370 pos: center + radius * n,
371 normal: n,
372 }));
373 }
374 }
375
376 pub fn add_line_segment(&mut self, points: [Pos2; 2]) {
377 self.reserve(2);
378 let normal = (points[1] - points[0]).normalized().rot90();
379 self.add_point(points[0], normal);
380 self.add_point(points[1], normal);
381 }
382
383 pub fn add_open_points(&mut self, points: &[Pos2]) {
384 let n = points.len();
385 assert!(n >= 2, "A path needs at least two points, but got {n}");
386
387 if n == 2 {
388 self.add_line_segment([points[0], points[1]]);
390 } else {
391 self.reserve(n);
392 self.add_point(points[0], (points[1] - points[0]).normalized().rot90());
393 let mut n0 = (points[1] - points[0]).normalized().rot90();
394 for i in 1..n - 1 {
395 let mut n1 = (points[i + 1] - points[i]).normalized().rot90();
396
397 if n0 == Vec2::ZERO {
399 n0 = n1;
400 } else if n1 == Vec2::ZERO {
401 n1 = n0;
402 }
403
404 let normal = (n0 + n1) / 2.0;
405 let length_sq = normal.length_sq();
406 let right_angle_length_sq = 0.5;
407 let sharper_than_a_right_angle = length_sq < right_angle_length_sq;
408 if sharper_than_a_right_angle {
409 let center_normal = normal.normalized();
411 let n0c = (n0 + center_normal) / 2.0;
412 let n1c = (n1 + center_normal) / 2.0;
413 self.add_point(points[i], n0c / n0c.length_sq());
414 self.add_point(points[i], n1c / n1c.length_sq());
415 } else {
416 self.add_point(points[i], normal / length_sq);
418 }
419
420 n0 = n1;
421 }
422 self.add_point(
423 points[n - 1],
424 (points[n - 1] - points[n - 2]).normalized().rot90(),
425 );
426 }
427 }
428
429 pub fn add_line_loop(&mut self, points: &[Pos2]) {
430 let n = points.len();
431 assert!(n >= 2, "A path needs at least two points, but got {n}");
432 self.reserve(n);
433
434 let mut n0 = (points[0] - points[n - 1]).normalized().rot90();
435
436 for i in 0..n {
437 let next_i = if i + 1 == n { 0 } else { i + 1 };
438 let mut n1 = (points[next_i] - points[i]).normalized().rot90();
439
440 if n0 == Vec2::ZERO {
442 n0 = n1;
443 } else if n1 == Vec2::ZERO {
444 n1 = n0;
445 }
446
447 let normal = (n0 + n1) / 2.0;
448 let length_sq = normal.length_sq();
449
450 const CUT_OFF_SHARP_CORNERS: bool = false;
459
460 let right_angle_length_sq = 0.5;
461 let sharper_than_a_right_angle = length_sq < right_angle_length_sq;
462 if CUT_OFF_SHARP_CORNERS && sharper_than_a_right_angle {
463 let center_normal = normal.normalized();
465 let n0c = (n0 + center_normal) / 2.0;
466 let n1c = (n1 + center_normal) / 2.0;
467 self.add_point(points[i], n0c / n0c.length_sq());
468 self.add_point(points[i], n1c / n1c.length_sq());
469 } else {
470 self.add_point(points[i], normal / length_sq);
472 }
473
474 n0 = n1;
475 }
476 }
477
478 pub fn fill_and_stroke(
483 &mut self,
484 feathering: f32,
485 fill: Color32,
486 stroke: &PathStroke,
487 out: &mut Mesh,
488 ) {
489 stroke_and_fill_path(feathering, &mut self.0, PathType::Closed, stroke, fill, out);
490 }
491
492 pub fn stroke_open(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) {
494 stroke_path(feathering, &mut self.0, PathType::Open, stroke, out);
495 }
496
497 pub fn stroke_closed(&mut self, feathering: f32, stroke: &PathStroke, out: &mut Mesh) {
499 stroke_path(feathering, &mut self.0, PathType::Closed, stroke, out);
500 }
501
502 pub fn stroke(
503 &mut self,
504 feathering: f32,
505 path_type: PathType,
506 stroke: &PathStroke,
507 out: &mut Mesh,
508 ) {
509 stroke_path(feathering, &mut self.0, path_type, stroke, out);
510 }
511
512 pub fn fill(&mut self, feathering: f32, color: Color32, out: &mut Mesh) {
517 fill_closed_path(feathering, &mut self.0, color, out);
518 }
519
520 pub fn fill_with_uv(
524 &mut self,
525 feathering: f32,
526 color: Color32,
527 texture_id: TextureId,
528 uv_from_pos: impl Fn(Pos2) -> Pos2,
529 out: &mut Mesh,
530 ) {
531 fill_closed_path_with_uv(feathering, &mut self.0, color, texture_id, uv_from_pos, out);
532 }
533}
534
535pub mod path {
536 use crate::CornerRadiusF32;
538 use emath::{Pos2, Rect, pos2};
539
540 pub fn rounded_rectangle(path: &mut Vec<Pos2>, rect: Rect, cr: CornerRadiusF32) {
542 path.clear();
543
544 let min = rect.min;
545 let max = rect.max;
546
547 let cr = clamp_corner_radius(cr, rect);
548
549 if cr == CornerRadiusF32::ZERO {
550 path.reserve(4);
551 path.push(pos2(min.x, min.y)); path.push(pos2(max.x, min.y)); path.push(pos2(max.x, max.y)); path.push(pos2(min.x, max.y)); } else {
556 let eps = f32::EPSILON * rect.size().max_elem();
559
560 add_circle_quadrant(path, pos2(max.x - cr.se, max.y - cr.se), cr.se, 0.0); if rect.width() <= cr.se + cr.sw + eps {
563 path.pop(); }
565
566 add_circle_quadrant(path, pos2(min.x + cr.sw, max.y - cr.sw), cr.sw, 1.0); if rect.height() <= cr.sw + cr.nw + eps {
569 path.pop(); }
571
572 add_circle_quadrant(path, pos2(min.x + cr.nw, min.y + cr.nw), cr.nw, 2.0); if rect.width() <= cr.nw + cr.ne + eps {
575 path.pop(); }
577
578 add_circle_quadrant(path, pos2(max.x - cr.ne, min.y + cr.ne), cr.ne, 3.0); if rect.height() <= cr.ne + cr.se + eps {
581 path.pop(); }
583 }
584 }
585
586 pub fn add_circle_quadrant(path: &mut Vec<Pos2>, center: Pos2, radius: f32, quadrant: f32) {
605 use super::precomputed_vertices::{CIRCLE_8, CIRCLE_16, CIRCLE_32, CIRCLE_64, CIRCLE_128};
606
607 if radius <= 0.0 {
611 path.push(center);
612 } else if radius <= 2.0 {
613 let offset = quadrant as usize * 2;
614 let quadrant_vertices = &CIRCLE_8[offset..=offset + 2];
615 path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
616 } else if radius <= 5.0 {
617 let offset = quadrant as usize * 4;
618 let quadrant_vertices = &CIRCLE_16[offset..=offset + 4];
619 path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
620 } else if radius < 18.0 {
621 let offset = quadrant as usize * 8;
622 let quadrant_vertices = &CIRCLE_32[offset..=offset + 8];
623 path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
624 } else if radius < 50.0 {
625 let offset = quadrant as usize * 16;
626 let quadrant_vertices = &CIRCLE_64[offset..=offset + 16];
627 path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
628 } else {
629 let offset = quadrant as usize * 32;
630 let quadrant_vertices = &CIRCLE_128[offset..=offset + 32];
631 path.extend(quadrant_vertices.iter().map(|&n| center + radius * n));
632 }
633 }
634
635 fn clamp_corner_radius(cr: CornerRadiusF32, rect: Rect) -> CornerRadiusF32 {
637 let half_width = rect.width() * 0.5;
638 let half_height = rect.height() * 0.5;
639 let max_cr = half_width.min(half_height);
640 cr.at_most(max_cr).at_least(0.0)
641 }
642}
643
644#[derive(Clone, Copy, PartialEq, Eq)]
647pub enum PathType {
648 Open,
649 Closed,
650}
651
652#[derive(Clone, Copy, Debug, PartialEq)]
654#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
655#[cfg_attr(feature = "serde", serde(default))]
656pub struct TessellationOptions {
657 pub feathering: bool,
668
669 pub feathering_size_in_pixels: f32,
674
675 pub coarse_tessellation_culling: bool,
678
679 pub prerasterized_discs: bool,
682
683 pub round_text_to_pixels: bool,
686
687 pub round_line_segments_to_pixels: bool,
691
692 pub round_rects_to_pixels: bool,
699
700 pub debug_paint_clip_rects: bool,
702
703 pub debug_paint_text_rects: bool,
705
706 pub debug_ignore_clip_rects: bool,
708
709 pub bezier_tolerance: f32,
711
712 pub epsilon: f32,
714
715 pub parallel_tessellation: bool,
717
718 pub validate_meshes: bool,
723}
724
725impl Default for TessellationOptions {
726 fn default() -> Self {
727 Self {
728 feathering: true,
729 feathering_size_in_pixels: 1.0,
730 coarse_tessellation_culling: true,
731 prerasterized_discs: true,
732 round_text_to_pixels: true,
733 round_line_segments_to_pixels: true,
734 round_rects_to_pixels: true,
735 debug_paint_text_rects: false,
736 debug_paint_clip_rects: false,
737 debug_ignore_clip_rects: false,
738 bezier_tolerance: 0.1,
739 epsilon: 1.0e-5,
740 parallel_tessellation: true,
741 validate_meshes: false,
742 }
743 }
744}
745
746fn cw_signed_area(path: &[PathPoint]) -> f64 {
747 if let Some(last) = path.last() {
748 let mut previous = last.pos;
749 let mut area = 0.0;
750 for p in path {
751 area += (previous.x * p.pos.y - p.pos.x * previous.y) as f64;
752 previous = p.pos;
753 }
754 area
755 } else {
756 0.0
757 }
758}
759
760fn fill_closed_path(feathering: f32, path: &mut [PathPoint], fill_color: Color32, out: &mut Mesh) {
766 if fill_color == Color32::TRANSPARENT {
767 return;
768 }
769
770 let n = path.len() as u32;
771 if n < 3 {
772 return;
773 }
774
775 if 0.0 < feathering {
776 if cw_signed_area(path) < 0.0 {
777 path.reverse();
779 for point in &mut *path {
780 point.normal = -point.normal;
781 }
782 }
783
784 out.reserve_triangles(3 * n as usize);
785 out.reserve_vertices(2 * n as usize);
786 let idx_inner = out.vertices.len() as u32;
787 let idx_outer = idx_inner + 1;
788
789 for i in 2..n {
791 out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
792 }
793
794 let mut i0 = n - 1;
796 for i1 in 0..n {
797 let p1 = &path[i1 as usize];
798 let dm = 0.5 * feathering * p1.normal;
799
800 let pos_inner = p1.pos - dm;
801 let pos_outer = p1.pos + dm;
802
803 out.colored_vertex(pos_inner, fill_color);
804 out.colored_vertex(pos_outer, Color32::TRANSPARENT);
805 out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
806 out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
807 i0 = i1;
808 }
809 } else {
810 out.reserve_triangles(n as usize);
811 let idx = out.vertices.len() as u32;
812 out.vertices
813 .extend(path.iter().map(|p| Vertex::untextured(p.pos, fill_color)));
814 for i in 2..n {
815 out.add_triangle(idx, idx + i - 1, idx + i);
816 }
817 }
818}
819
820fn fill_closed_path_with_uv(
824 feathering: f32,
825 path: &mut [PathPoint],
826 color: Color32,
827 texture_id: TextureId,
828 uv_from_pos: impl Fn(Pos2) -> Pos2,
829 out: &mut Mesh,
830) {
831 if color == Color32::TRANSPARENT {
832 return;
833 }
834
835 if out.is_empty() {
836 out.texture_id = texture_id;
837 } else {
838 assert_eq!(
839 out.texture_id, texture_id,
840 "Mixing different `texture_id` in the same "
841 );
842 }
843
844 let n = path.len() as u32;
845 if 0.0 < feathering {
846 if cw_signed_area(path) < 0.0 {
847 path.reverse();
849 for point in &mut *path {
850 point.normal = -point.normal;
851 }
852 }
853
854 out.reserve_triangles(3 * n as usize);
855 out.reserve_vertices(2 * n as usize);
856 let color_outer = Color32::TRANSPARENT;
857 let idx_inner = out.vertices.len() as u32;
858 let idx_outer = idx_inner + 1;
859
860 for i in 2..n {
862 out.add_triangle(idx_inner + 2 * (i - 1), idx_inner, idx_inner + 2 * i);
863 }
864
865 let mut i0 = n - 1;
867 for i1 in 0..n {
868 let p1 = &path[i1 as usize];
869 let dm = 0.5 * feathering * p1.normal;
870
871 let pos = p1.pos - dm;
872 out.vertices.push(Vertex {
873 pos,
874 uv: uv_from_pos(pos),
875 color,
876 });
877
878 let pos = p1.pos + dm;
879 out.vertices.push(Vertex {
880 pos,
881 uv: uv_from_pos(pos),
882 color: color_outer,
883 });
884
885 out.add_triangle(idx_inner + i1 * 2, idx_inner + i0 * 2, idx_outer + 2 * i0);
886 out.add_triangle(idx_outer + i0 * 2, idx_outer + i1 * 2, idx_inner + 2 * i1);
887 i0 = i1;
888 }
889 } else {
890 out.reserve_triangles(n as usize);
891 let idx = out.vertices.len() as u32;
892 out.vertices.extend(path.iter().map(|p| Vertex {
893 pos: p.pos,
894 uv: uv_from_pos(p.pos),
895 color,
896 }));
897 for i in 2..n {
898 out.add_triangle(idx, idx + i - 1, idx + i);
899 }
900 }
901}
902
903fn stroke_path(
905 feathering: f32,
906 path: &mut [PathPoint],
907 path_type: PathType,
908 stroke: &PathStroke,
909 out: &mut Mesh,
910) {
911 let fill = Color32::TRANSPARENT;
912 stroke_and_fill_path(feathering, path, path_type, stroke, fill, out);
913}
914
915fn stroke_and_fill_path(
921 feathering: f32,
922 path: &mut [PathPoint],
923 path_type: PathType,
924 stroke: &PathStroke,
925 color_fill: Color32,
926 out: &mut Mesh,
927) {
928 let n = path.len() as u32;
929
930 if n < 2 {
931 return;
932 }
933
934 if stroke.width == 0.0 {
935 return fill_closed_path(feathering, path, color_fill, out);
937 }
938
939 if color_fill != Color32::TRANSPARENT && cw_signed_area(path) < 0.0 {
940 path.reverse();
942 for point in &mut *path {
943 point.normal = -point.normal;
944 }
945 }
946
947 if stroke.color == ColorMode::TRANSPARENT {
948 match stroke.kind {
950 StrokeKind::Inside => {
951 for point in &mut *path {
952 point.pos -= stroke.width * point.normal;
953 }
954 }
955 StrokeKind::Middle => {
956 for point in &mut *path {
957 point.pos -= 0.5 * stroke.width * point.normal;
958 }
959 }
960 StrokeKind::Outside => {}
961 }
962
963 return fill_closed_path(feathering, path, color_fill, out);
965 }
966
967 let idx = out.vertices.len() as u32;
968
969 match stroke.kind {
971 StrokeKind::Inside => {
972 for point in &mut *path {
973 point.pos -= 0.5 * stroke.width * point.normal;
974 }
975 }
976 StrokeKind::Middle => {
977 }
979 StrokeKind::Outside => {
980 for point in &mut *path {
981 point.pos += 0.5 * stroke.width * point.normal;
982 }
983 }
984 }
985
986 let uv_bbox = if matches!(stroke.color, ColorMode::UV(_)) {
988 Rect::from_points(&path.iter().map(|p| p.pos).collect::<Vec<Pos2>>())
989 .expand((stroke.width / 2.0) + feathering)
990 } else {
991 Rect::NAN
992 };
993 let get_color = |col: &ColorMode, pos: Pos2| match col {
994 ColorMode::Solid(col) => *col,
995 ColorMode::UV(fun) => fun(uv_bbox, pos),
996 };
997
998 if 0.0 < feathering {
999 let color_outer = Color32::TRANSPARENT;
1000 let color_middle = &stroke.color;
1001
1002 let thin_line = stroke.width <= 0.9 * feathering;
1007 if thin_line {
1008 if color_fill != Color32::TRANSPARENT && stroke.width < feathering {
1013 for point in &mut *path {
1017 point.pos += 0.5 * (feathering - stroke.width) * point.normal;
1018 }
1019 }
1020
1021 let opacity = stroke.width / feathering;
1024
1025 out.reserve_triangles(4 * n as usize);
1033 out.reserve_vertices(3 * n as usize);
1034
1035 let mut i0 = n - 1;
1036 for i1 in 0..n {
1037 let connect_with_previous = path_type == PathType::Closed || i1 > 0;
1038 let p1 = path[i1 as usize];
1039 let p = p1.pos;
1040 let n = p1.normal;
1041 out.colored_vertex(p + n * feathering, color_outer);
1042 out.colored_vertex(p, mul_color(get_color(color_middle, p), opacity));
1043 out.colored_vertex(p - n * feathering, color_fill);
1044
1045 if connect_with_previous {
1046 out.add_triangle(idx + 3 * i0 + 0, idx + 3 * i0 + 1, idx + 3 * i1 + 0);
1047 out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i1 + 0, idx + 3 * i1 + 1);
1048
1049 out.add_triangle(idx + 3 * i0 + 1, idx + 3 * i0 + 2, idx + 3 * i1 + 1);
1050 out.add_triangle(idx + 3 * i0 + 2, idx + 3 * i1 + 1, idx + 3 * i1 + 2);
1051 }
1052
1053 i0 = i1;
1054 }
1055
1056 if color_fill != Color32::TRANSPARENT {
1057 out.reserve_triangles(n as usize - 2);
1058 let idx_fill = idx + 2;
1059 for i in 2..n {
1060 out.add_triangle(idx_fill + 3 * (i - 1), idx_fill, idx_fill + 3 * i);
1061 }
1062 }
1063 } else {
1064 let inner_rad = 0.5 * (stroke.width - feathering);
1077 let outer_rad = 0.5 * (stroke.width + feathering);
1078
1079 match path_type {
1080 PathType::Closed => {
1081 out.reserve_triangles(6 * n as usize);
1082 out.reserve_vertices(4 * n as usize);
1083
1084 let mut i0 = n - 1;
1085 for i1 in 0..n {
1086 let p1 = path[i1 as usize];
1087 let p = p1.pos;
1088 let n = p1.normal;
1089 out.colored_vertex(p + n * outer_rad, color_outer);
1090 out.colored_vertex(
1091 p + n * inner_rad,
1092 get_color(color_middle, p + n * inner_rad),
1093 );
1094 out.colored_vertex(
1095 p - n * inner_rad,
1096 get_color(color_middle, p - n * inner_rad),
1097 );
1098 out.colored_vertex(p - n * outer_rad, color_fill);
1099
1100 out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
1101 out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
1102
1103 out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
1104 out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1105
1106 out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
1107 out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1108
1109 i0 = i1;
1110 }
1111
1112 if color_fill != Color32::TRANSPARENT {
1113 out.reserve_triangles(n as usize - 2);
1114 let idx_fill = idx + 3;
1115 for i in 2..n {
1116 out.add_triangle(idx_fill + 4 * (i - 1), idx_fill, idx_fill + 4 * i);
1117 }
1118 }
1119 }
1120 PathType::Open => {
1121 out.reserve_triangles(6 * n as usize + 4);
1140 out.reserve_vertices(4 * n as usize);
1141
1142 {
1143 let end = path[0];
1144 let p = end.pos;
1145 let n = end.normal;
1146 let back_extrude = n.rot90() * feathering;
1147 out.colored_vertex(p + n * outer_rad + back_extrude, color_outer);
1148 out.colored_vertex(
1149 p + n * inner_rad,
1150 get_color(color_middle, p + n * inner_rad),
1151 );
1152 out.colored_vertex(
1153 p - n * inner_rad,
1154 get_color(color_middle, p - n * inner_rad),
1155 );
1156 out.colored_vertex(p - n * outer_rad + back_extrude, color_outer);
1157
1158 out.add_triangle(idx + 0, idx + 1, idx + 2);
1159 out.add_triangle(idx + 0, idx + 2, idx + 3);
1160 }
1161
1162 let mut i0 = 0;
1163 for i1 in 1..n - 1 {
1164 let point = path[i1 as usize];
1165 let p = point.pos;
1166 let n = point.normal;
1167 out.colored_vertex(p + n * outer_rad, color_outer);
1168 out.colored_vertex(
1169 p + n * inner_rad,
1170 get_color(color_middle, p + n * inner_rad),
1171 );
1172 out.colored_vertex(
1173 p - n * inner_rad,
1174 get_color(color_middle, p - n * inner_rad),
1175 );
1176 out.colored_vertex(p - n * outer_rad, color_outer);
1177
1178 out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
1179 out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
1180
1181 out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
1182 out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1183
1184 out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
1185 out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1186
1187 i0 = i1;
1188 }
1189
1190 {
1191 let i1 = n - 1;
1192 let end = path[i1 as usize];
1193 let p = end.pos;
1194 let n = end.normal;
1195 let back_extrude = -n.rot90() * feathering;
1196 out.colored_vertex(p + n * outer_rad + back_extrude, color_outer);
1197 out.colored_vertex(
1198 p + n * inner_rad,
1199 get_color(color_middle, p + n * inner_rad),
1200 );
1201 out.colored_vertex(
1202 p - n * inner_rad,
1203 get_color(color_middle, p - n * inner_rad),
1204 );
1205 out.colored_vertex(p - n * outer_rad + back_extrude, color_outer);
1206
1207 out.add_triangle(idx + 4 * i0 + 0, idx + 4 * i0 + 1, idx + 4 * i1 + 0);
1208 out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i1 + 0, idx + 4 * i1 + 1);
1209
1210 out.add_triangle(idx + 4 * i0 + 1, idx + 4 * i0 + 2, idx + 4 * i1 + 1);
1211 out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1212
1213 out.add_triangle(idx + 4 * i0 + 2, idx + 4 * i0 + 3, idx + 4 * i1 + 2);
1214 out.add_triangle(idx + 4 * i0 + 3, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1215
1216 out.add_triangle(idx + 4 * i1 + 0, idx + 4 * i1 + 1, idx + 4 * i1 + 2);
1218 out.add_triangle(idx + 4 * i1 + 0, idx + 4 * i1 + 2, idx + 4 * i1 + 3);
1219 }
1220 }
1221 }
1222 }
1223 } else {
1224 out.reserve_triangles(2 * n as usize);
1226 out.reserve_vertices(2 * n as usize);
1227
1228 let last_index = if path_type == PathType::Closed {
1229 n
1230 } else {
1231 n - 1
1232 };
1233 for i in 0..last_index {
1234 out.add_triangle(
1235 idx + (2 * i + 0) % (2 * n),
1236 idx + (2 * i + 1) % (2 * n),
1237 idx + (2 * i + 2) % (2 * n),
1238 );
1239 out.add_triangle(
1240 idx + (2 * i + 2) % (2 * n),
1241 idx + (2 * i + 1) % (2 * n),
1242 idx + (2 * i + 3) % (2 * n),
1243 );
1244 }
1245
1246 let thin_line = stroke.width <= feathering;
1247 if thin_line {
1248 let opacity = stroke.width / feathering;
1250 let radius = feathering / 2.0;
1251 for p in path.iter_mut() {
1252 out.colored_vertex(
1253 p.pos + radius * p.normal,
1254 mul_color(get_color(&stroke.color, p.pos + radius * p.normal), opacity),
1255 );
1256 out.colored_vertex(
1257 p.pos - radius * p.normal,
1258 mul_color(get_color(&stroke.color, p.pos - radius * p.normal), opacity),
1259 );
1260 }
1261 } else {
1262 let radius = stroke.width / 2.0;
1263 for p in path.iter_mut() {
1264 out.colored_vertex(
1265 p.pos + radius * p.normal,
1266 get_color(&stroke.color, p.pos + radius * p.normal),
1267 );
1268 out.colored_vertex(
1269 p.pos - radius * p.normal,
1270 get_color(&stroke.color, p.pos - radius * p.normal),
1271 );
1272 }
1273 }
1274
1275 if color_fill != Color32::TRANSPARENT {
1276 for point in &mut *path {
1281 point.pos -= 0.5 * stroke.width * point.normal;
1282 }
1283 fill_closed_path(feathering, path, color_fill, out);
1285 }
1286 }
1287}
1288
1289fn mul_color(color: Color32, factor: f32) -> Color32 {
1290 color.gamma_multiply(factor)
1293}
1294
1295#[derive(Clone)]
1301pub struct Tessellator {
1302 pixels_per_point: f32,
1303 options: TessellationOptions,
1304 font_tex_size: [usize; 2],
1305
1306 prepared_discs: Vec<PreparedDisc>,
1308
1309 feathering: f32,
1311
1312 clip_rect: Rect,
1314
1315 scratchpad_points: Vec<Pos2>,
1316 scratchpad_path: Path,
1317}
1318
1319impl Tessellator {
1320 pub fn new(
1328 pixels_per_point: f32,
1329 options: TessellationOptions,
1330 font_tex_size: [usize; 2],
1331 prepared_discs: Vec<PreparedDisc>,
1332 ) -> Self {
1333 let feathering = if options.feathering {
1334 let pixel_size = 1.0 / pixels_per_point;
1335 options.feathering_size_in_pixels * pixel_size
1336 } else {
1337 0.0
1338 };
1339 Self {
1340 pixels_per_point,
1341 options,
1342 font_tex_size,
1343 prepared_discs,
1344 feathering,
1345 clip_rect: Rect::EVERYTHING,
1346 scratchpad_points: Default::default(),
1347 scratchpad_path: Default::default(),
1348 }
1349 }
1350
1351 pub fn set_clip_rect(&mut self, clip_rect: Rect) {
1353 self.clip_rect = clip_rect;
1354 }
1355
1356 pub fn tessellate_clipped_shape(
1358 &mut self,
1359 clipped_shape: ClippedShape,
1360 out_primitives: &mut Vec<ClippedPrimitive>,
1361 ) {
1362 let ClippedShape { clip_rect, shape } = clipped_shape;
1363
1364 if !clip_rect.is_positive() {
1365 return; }
1367
1368 if let Shape::Vec(shapes) = shape {
1369 for shape in shapes {
1370 self.tessellate_clipped_shape(ClippedShape { clip_rect, shape }, out_primitives);
1371 }
1372 return;
1373 }
1374
1375 if let Shape::Callback(callback) = shape {
1376 out_primitives.push(ClippedPrimitive {
1377 clip_rect,
1378 primitive: Primitive::Callback(callback),
1379 });
1380 return;
1381 }
1382
1383 let start_new_mesh = match out_primitives.last() {
1384 None => true,
1385 Some(output_clipped_primitive) => {
1386 output_clipped_primitive.clip_rect != clip_rect
1387 || match &output_clipped_primitive.primitive {
1388 Primitive::Mesh(output_mesh) => {
1389 output_mesh.texture_id != shape.texture_id()
1390 }
1391 Primitive::Callback(_) => true,
1392 }
1393 }
1394 };
1395
1396 if start_new_mesh {
1397 out_primitives.push(ClippedPrimitive {
1398 clip_rect,
1399 primitive: Primitive::Mesh(Mesh::default()),
1400 });
1401 }
1402
1403 #[expect(clippy::unwrap_used)] let out = out_primitives.last_mut().unwrap();
1405
1406 if let Primitive::Mesh(out_mesh) = &mut out.primitive {
1407 self.clip_rect = clip_rect;
1408 self.tessellate_shape(shape, out_mesh);
1409 } else {
1410 unreachable!();
1411 }
1412 }
1413
1414 pub fn tessellate_shape(&mut self, shape: Shape, out: &mut Mesh) {
1421 match shape {
1422 Shape::Noop => {}
1423 Shape::Vec(vec) => {
1424 for shape in vec {
1425 self.tessellate_shape(shape, out);
1426 }
1427 }
1428 Shape::Circle(circle) => {
1429 self.tessellate_circle(circle, out);
1430 }
1431 Shape::Ellipse(ellipse) => {
1432 self.tessellate_ellipse(ellipse, out);
1433 }
1434 Shape::Mesh(mesh) => {
1435 profiling::scope!("mesh");
1436
1437 if self.options.validate_meshes && !mesh.is_valid() {
1438 debug_assert!(false, "Invalid Mesh in Shape::Mesh");
1439 return;
1440 }
1441 if self.options.coarse_tessellation_culling
1444 && !self.clip_rect.intersects(mesh.calc_bounds())
1445 {
1446 return;
1447 }
1448
1449 out.append_ref(&mesh);
1450 }
1451 Shape::LineSegment { points, stroke } => {
1452 self.tessellate_line_segment(points, stroke, out);
1453 }
1454 Shape::Path(path_shape) => {
1455 self.tessellate_path(&path_shape, out);
1456 }
1457 Shape::Rect(rect_shape) => {
1458 self.tessellate_rect(&rect_shape, out);
1459 }
1460 Shape::Text(text_shape) => {
1461 if self.options.debug_paint_text_rects {
1462 let rect = text_shape.galley.rect.translate(text_shape.pos.to_vec2());
1463 self.tessellate_rect(
1464 &RectShape::stroke(rect, 2.0, (0.5, Color32::GREEN), StrokeKind::Outside),
1465 out,
1466 );
1467 }
1468 self.tessellate_text(&text_shape, out);
1469 }
1470 Shape::QuadraticBezier(quadratic_shape) => {
1471 self.tessellate_quadratic_bezier(&quadratic_shape, out);
1472 }
1473 Shape::CubicBezier(cubic_shape) => self.tessellate_cubic_bezier(&cubic_shape, out),
1474 Shape::Callback(_) => {
1475 panic!("Shape::Callback passed to Tessellator");
1476 }
1477 }
1478 }
1479
1480 pub fn tessellate_circle(&mut self, shape: CircleShape, out: &mut Mesh) {
1485 let CircleShape {
1486 center,
1487 radius,
1488 mut fill,
1489 stroke,
1490 } = shape;
1491
1492 if radius <= 0.0 {
1493 return;
1494 }
1495
1496 if self.options.coarse_tessellation_culling
1497 && !self
1498 .clip_rect
1499 .expand(radius + stroke.width)
1500 .contains(center)
1501 {
1502 return;
1503 }
1504
1505 if self.options.prerasterized_discs && fill != Color32::TRANSPARENT {
1506 let radius_px = radius * self.pixels_per_point;
1507 let cutoff_radius = radius_px * 2.0_f32.powf(0.25);
1509
1510 for disc in &self.prepared_discs {
1513 if cutoff_radius <= disc.r {
1514 let side = radius_px * disc.w / (self.pixels_per_point * disc.r);
1515 let rect = Rect::from_center_size(center, Vec2::splat(side));
1516 out.add_rect_with_uv(rect, disc.uv, fill);
1517
1518 if stroke.is_empty() {
1519 return; } else {
1521 fill = Color32::TRANSPARENT; break;
1524 }
1525 }
1526 }
1527 }
1528
1529 let path_stroke = PathStroke::from(stroke).outside();
1530 self.scratchpad_path.clear();
1531 self.scratchpad_path.add_circle(center, radius);
1532 self.scratchpad_path
1533 .fill_and_stroke(self.feathering, fill, &path_stroke, out);
1534 }
1535
1536 pub fn tessellate_ellipse(&mut self, shape: EllipseShape, out: &mut Mesh) {
1541 let EllipseShape {
1542 center,
1543 radius,
1544 fill,
1545 stroke,
1546 angle,
1547 } = shape;
1548
1549 if radius.x <= 0.0 || radius.y <= 0.0 {
1550 return;
1551 }
1552
1553 if self.options.coarse_tessellation_culling
1554 && !self
1555 .clip_rect
1556 .expand2(radius + Vec2::splat(stroke.width))
1557 .contains(center)
1558 {
1559 return;
1560 }
1561
1562 let max_radius = (radius.max_elem() * self.pixels_per_point) as u32;
1564
1565 let num_points = u32::max(8, max_radius / 16);
1567
1568 let ratio = ((radius.y / radius.x) / 2.0).clamp(0.0, 1.0);
1570
1571 let quarter: Vec<Vec2> = (1..num_points)
1573 .map(|i| {
1574 let percent = i as f32 / num_points as f32;
1575
1576 let eased = 2.0 * (percent - percent.powf(2.0)) * ratio + percent.powf(2.0);
1578
1579 let t = eased * std::f32::consts::FRAC_PI_2;
1581 Vec2::new(radius.x * f32::cos(t), radius.y * f32::sin(t))
1582 })
1583 .collect();
1584
1585 let mut points = Vec::new();
1588 points.push(center + Vec2::new(radius.x, 0.0));
1589 points.extend(quarter.iter().map(|p| center + *p));
1590 points.push(center + Vec2::new(0.0, radius.y));
1591 points.extend(quarter.iter().rev().map(|p| center + Vec2::new(-p.x, p.y)));
1592 points.push(center + Vec2::new(-radius.x, 0.0));
1593 points.extend(quarter.iter().map(|p| center - *p));
1594 points.push(center + Vec2::new(0.0, -radius.y));
1595 points.extend(quarter.iter().rev().map(|p| center + Vec2::new(p.x, -p.y)));
1596
1597 if angle != 0.0 {
1599 let rot = emath::Rot2::from_angle(angle);
1600 for point in &mut points {
1601 *point = center + rot * (*point - center);
1602 }
1603 }
1604
1605 let path_stroke = PathStroke::from(stroke).outside();
1606 self.scratchpad_path.clear();
1607 self.scratchpad_path.add_line_loop(&points);
1608 self.scratchpad_path
1609 .fill_and_stroke(self.feathering, fill, &path_stroke, out);
1610 }
1611
1612 pub fn tessellate_mesh(&self, mesh: &Mesh, out: &mut Mesh) {
1617 if !mesh.is_valid() {
1618 debug_assert!(false, "Invalid Mesh in Shape::Mesh");
1619 return;
1620 }
1621
1622 if self.options.coarse_tessellation_culling
1623 && !self.clip_rect.intersects(mesh.calc_bounds())
1624 {
1625 return;
1626 }
1627
1628 out.append_ref(mesh);
1629 }
1630
1631 pub fn tessellate_line_segment(
1636 &mut self,
1637 mut points: [Pos2; 2],
1638 stroke: impl Into<Stroke>,
1639 out: &mut Mesh,
1640 ) {
1641 let stroke = stroke.into();
1642 if stroke.is_empty() {
1643 return;
1644 }
1645
1646 if self.options.coarse_tessellation_culling
1647 && !self
1648 .clip_rect
1649 .intersects(Rect::from_two_pos(points[0], points[1]).expand(stroke.width))
1650 {
1651 return;
1652 }
1653
1654 if self.options.round_line_segments_to_pixels {
1655 let feathering = self.feathering;
1656 let pixels_per_point = self.pixels_per_point;
1657
1658 let quarter_pixel = 0.25 * feathering; let [a, b] = &mut points;
1661 if a.x == b.x {
1662 let mut x = a.x;
1664 stroke.round_center_to_pixel(self.pixels_per_point, &mut x);
1665 a.x = x;
1666 b.x = x;
1667
1668 if a.y < b.y {
1675 a.y = (a.y + quarter_pixel).round_to_pixel_center(pixels_per_point);
1676 b.y = (b.y - quarter_pixel).round_to_pixel_center(pixels_per_point);
1677 } else {
1678 a.y = (a.y - quarter_pixel).round_to_pixel_center(pixels_per_point);
1679 b.y = (b.y + quarter_pixel).round_to_pixel_center(pixels_per_point);
1680 }
1681 }
1682 if a.y == b.y {
1683 let mut y = a.y;
1685 stroke.round_center_to_pixel(self.pixels_per_point, &mut y);
1686 a.y = y;
1687 b.y = y;
1688
1689 if a.x < b.x {
1691 a.x = (a.x + quarter_pixel).round_to_pixel_center(pixels_per_point);
1692 b.x = (b.x - quarter_pixel).round_to_pixel_center(pixels_per_point);
1693 } else {
1694 a.x = (a.x - quarter_pixel).round_to_pixel_center(pixels_per_point);
1695 b.x = (b.x + quarter_pixel).round_to_pixel_center(pixels_per_point);
1696 }
1697 }
1698 }
1699
1700 self.scratchpad_path.clear();
1701 self.scratchpad_path.add_line_segment(points);
1702 self.scratchpad_path
1703 .stroke_open(self.feathering, &stroke.into(), out);
1704 }
1705
1706 pub fn tessellate_path(&mut self, path_shape: &PathShape, out: &mut Mesh) {
1711 if path_shape.points.len() < 2 {
1712 return;
1713 }
1714
1715 if self.options.coarse_tessellation_culling
1716 && !path_shape.visual_bounding_rect().intersects(self.clip_rect)
1717 {
1718 return;
1719 }
1720
1721 profiling::function_scope!();
1722
1723 let PathShape {
1724 points,
1725 closed,
1726 fill,
1727 stroke,
1728 } = path_shape;
1729
1730 self.scratchpad_path.clear();
1731
1732 if *closed {
1733 self.scratchpad_path.add_line_loop(points);
1734
1735 self.scratchpad_path
1736 .fill_and_stroke(self.feathering, *fill, stroke, out);
1737 } else {
1738 debug_assert_eq!(
1739 *fill,
1740 Color32::TRANSPARENT,
1741 "You asked to fill a path that is not closed. That makes no sense."
1742 );
1743
1744 self.scratchpad_path.add_open_points(points);
1745
1746 self.scratchpad_path
1747 .stroke(self.feathering, PathType::Open, stroke, out);
1748 }
1749 }
1750
1751 pub fn tessellate_rect(&mut self, rect_shape: &RectShape, out: &mut Mesh) {
1756 if self.options.coarse_tessellation_culling
1757 && !rect_shape.visual_bounding_rect().intersects(self.clip_rect)
1758 {
1759 return;
1760 }
1761
1762 let brush = rect_shape.brush.as_ref();
1763 let RectShape {
1764 mut rect,
1765 corner_radius,
1766 mut fill,
1767 mut stroke,
1768 mut stroke_kind,
1769 round_to_pixels,
1770 mut blur_width,
1771 brush: _, angle,
1773 } = *rect_shape;
1774
1775 let mut corner_radius = CornerRadiusF32::from(corner_radius);
1776 let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels);
1777
1778 if stroke.width == 0.0 {
1779 stroke.color = Color32::TRANSPARENT;
1780 }
1781
1782 rect.min = rect.min.at_least(pos2(-1e7, -1e7));
1785 rect.max = rect.max.at_most(pos2(1e7, 1e7));
1786
1787 if !stroke.is_empty() {
1788 let rect_with_stroke = match stroke_kind {
1790 StrokeKind::Inside => rect,
1791 StrokeKind::Middle => rect.expand(stroke.width / 2.0),
1792 StrokeKind::Outside => rect.expand(stroke.width),
1793 };
1794
1795 if rect_with_stroke.size().min_elem() <= 2.0 * stroke.width + 0.5 * self.feathering {
1796 rect = rect_with_stroke;
1799
1800 fill = stroke.color;
1803
1804 stroke = Stroke::NONE;
1805 }
1806 }
1807
1808 if stroke.is_empty() && out.texture_id == TextureId::default() {
1809 if rect.width() <= 2.0 * self.feathering {
1812 return self.tessellate_line_segment(
1813 [rect.center_top(), rect.center_bottom()],
1814 (rect.width(), fill),
1815 out,
1816 );
1817 }
1818 if rect.height() <= 2.0 * self.feathering {
1819 return self.tessellate_line_segment(
1820 [rect.left_center(), rect.right_center()],
1821 (rect.height(), fill),
1822 out,
1823 );
1824 }
1825 }
1826
1827 if round_to_pixels {
1829 match stroke_kind {
1832 StrokeKind::Inside => {
1833 rect = rect.round_to_pixels(self.pixels_per_point);
1844 }
1845 StrokeKind::Middle => {
1846 stroke.round_rect_to_pixel(self.pixels_per_point, &mut rect);
1848 }
1849 StrokeKind::Outside => {
1850 rect = rect.round_to_pixels(self.pixels_per_point);
1858 }
1859 }
1860 }
1861
1862 let old_feathering = self.feathering;
1863
1864 if self.feathering < blur_width {
1865 let eps = 0.1; blur_width = blur_width
1871 .at_most(rect.size().min_elem() - eps - 2.0 * stroke.width)
1872 .at_least(0.0);
1873
1874 corner_radius += 0.5 * blur_width;
1875
1876 self.feathering = self.feathering.max(blur_width);
1877 }
1878
1879 {
1880 let original_cr = corner_radius;
1885
1886 match stroke_kind {
1887 StrokeKind::Inside => {}
1888 StrokeKind::Middle => {
1889 rect = rect.expand(stroke.width / 2.0);
1890 corner_radius += stroke.width / 2.0;
1891 }
1892 StrokeKind::Outside => {
1893 rect = rect.expand(stroke.width);
1894 corner_radius += stroke.width;
1895 }
1896 }
1897
1898 stroke_kind = StrokeKind::Inside;
1899
1900 let min_inside_cr = 0.1; let min_outside_cr = stroke.width + min_inside_cr;
1908
1909 let extra_cr_tweak = 0.4; if original_cr.nw == 0.0 {
1912 corner_radius.nw = 0.0;
1913 } else {
1914 corner_radius.nw += extra_cr_tweak;
1915 corner_radius.nw = corner_radius.nw.at_least(min_outside_cr);
1916 }
1917 if original_cr.ne == 0.0 {
1918 corner_radius.ne = 0.0;
1919 } else {
1920 corner_radius.ne += extra_cr_tweak;
1921 corner_radius.ne = corner_radius.ne.at_least(min_outside_cr);
1922 }
1923 if original_cr.sw == 0.0 {
1924 corner_radius.sw = 0.0;
1925 } else {
1926 corner_radius.sw += extra_cr_tweak;
1927 corner_radius.sw = corner_radius.sw.at_least(min_outside_cr);
1928 }
1929 if original_cr.se == 0.0 {
1930 corner_radius.se = 0.0;
1931 } else {
1932 corner_radius.se += extra_cr_tweak;
1933 corner_radius.se = corner_radius.se.at_least(min_outside_cr);
1934 }
1935 }
1936
1937 let path = &mut self.scratchpad_path;
1938 path.clear();
1939 path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius);
1940
1941 if angle != 0.0 {
1943 let rot = emath::Rot2::from_angle(angle);
1944 let center = rect.center();
1945 for point in &mut self.scratchpad_points {
1946 *point = center + rot * (*point - center);
1947 }
1948 }
1949
1950 path.add_line_loop(&self.scratchpad_points);
1951
1952 let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind);
1953
1954 if let Some(brush) = brush {
1955 let fill_rect = match stroke_kind {
1958 StrokeKind::Inside => rect.shrink(stroke.width),
1959 StrokeKind::Middle => rect.shrink(stroke.width / 2.0),
1960 StrokeKind::Outside => rect,
1961 };
1962
1963 if fill_rect.is_positive() {
1964 let crate::Brush {
1965 fill_texture_id,
1966 uv,
1967 } = **brush;
1968 let uv_from_pos = |p: Pos2| {
1969 pos2(
1970 remap(p.x, rect.x_range(), uv.x_range()),
1971 remap(p.y, rect.y_range(), uv.y_range()),
1972 )
1973 };
1974 path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out);
1975 }
1976
1977 if !stroke.is_empty() {
1978 path.stroke_closed(self.feathering, &path_stroke, out);
1979 }
1980 } else {
1981 path.fill_and_stroke(self.feathering, fill, &path_stroke, out);
1983 }
1984
1985 self.feathering = old_feathering; }
1987
1988 pub fn tessellate_text(&mut self, text_shape: &TextShape, out: &mut Mesh) {
1992 let TextShape {
1993 pos: galley_pos,
1994 galley,
1995 underline,
1996 override_text_color,
1997 fallback_color,
1998 opacity_factor,
1999 angle,
2000 } = text_shape;
2001
2002 if galley.is_empty() {
2003 return;
2004 }
2005
2006 if *opacity_factor <= 0.0 {
2007 return;
2008 }
2009
2010 if galley.pixels_per_point != self.pixels_per_point {
2011 log::warn!(
2012 "epaint: WARNING: pixels_per_point (dpi scale) have changed between text layout and tessellation. \
2013 You must recreate your text shapes if pixels_per_point changes."
2014 );
2015 }
2016
2017 out.vertices.reserve(galley.num_vertices);
2018 out.indices.reserve(galley.num_indices);
2019
2020 let galley_pos = if self.options.round_text_to_pixels {
2023 galley_pos.round_to_pixels(self.pixels_per_point)
2024 } else {
2025 *galley_pos
2026 };
2027
2028 let uv_normalizer = vec2(
2029 1.0 / self.font_tex_size[0] as f32,
2030 1.0 / self.font_tex_size[1] as f32,
2031 );
2032
2033 let rotator = Rot2::from_angle(*angle);
2034
2035 for row in &galley.rows {
2036 if row.visuals.mesh.is_empty() {
2037 continue;
2038 }
2039
2040 let final_row_pos = galley_pos + rotator * row.pos.to_vec2();
2041
2042 let mut row_rect = row.visuals.mesh_bounds;
2043 if *angle != 0.0 {
2044 row_rect = row_rect.rotate_bb(rotator);
2045 }
2046 row_rect = row_rect.translate(final_row_pos.to_vec2());
2047
2048 if self.options.coarse_tessellation_culling && !self.clip_rect.intersects(row_rect) {
2049 continue;
2052 }
2053
2054 let index_offset = out.vertices.len() as u32;
2055
2056 out.indices.extend(
2057 row.visuals
2058 .mesh
2059 .indices
2060 .iter()
2061 .map(|index| index + index_offset),
2062 );
2063
2064 out.vertices.extend(
2065 row.visuals
2066 .mesh
2067 .vertices
2068 .iter()
2069 .enumerate()
2070 .map(|(i, vertex)| {
2071 let Vertex { pos, uv, mut color } = *vertex;
2072
2073 if let Some(override_text_color) = override_text_color {
2074 if row.visuals.glyph_vertex_range.contains(&i) {
2076 color = *override_text_color;
2077 }
2078 } else if color == Color32::PLACEHOLDER {
2079 color = *fallback_color;
2080 }
2081
2082 if *opacity_factor < 1.0 {
2083 color = color.gamma_multiply(*opacity_factor);
2084 }
2085
2086 debug_assert!(color != Color32::PLACEHOLDER, "A placeholder color made it to the tessellator. You forgot to set a fallback color.");
2087
2088 let offset = if *angle == 0.0 {
2089 pos.to_vec2()
2090 } else {
2091 rotator * pos.to_vec2()
2092 };
2093
2094 Vertex {
2095 pos: final_row_pos + offset,
2096 uv: (uv.to_vec2() * uv_normalizer).to_pos2(),
2097 color,
2098 }
2099 }),
2100 );
2101
2102 if *underline != Stroke::NONE {
2103 self.tessellate_line_segment(
2104 [row_rect.left_bottom(), row_rect.right_bottom()],
2105 *underline,
2106 out,
2107 );
2108 }
2109 }
2110 }
2111
2112 pub fn tessellate_quadratic_bezier(
2117 &mut self,
2118 quadratic_shape: &QuadraticBezierShape,
2119 out: &mut Mesh,
2120 ) {
2121 let options = &self.options;
2122 let clip_rect = self.clip_rect;
2123
2124 if options.coarse_tessellation_culling
2125 && !quadratic_shape.visual_bounding_rect().intersects(clip_rect)
2126 {
2127 return;
2128 }
2129
2130 let points = quadratic_shape.flatten(Some(options.bezier_tolerance));
2131
2132 self.tessellate_bezier_complete(
2133 &points,
2134 quadratic_shape.fill,
2135 quadratic_shape.closed,
2136 &quadratic_shape.stroke,
2137 out,
2138 );
2139 }
2140
2141 pub fn tessellate_cubic_bezier(&mut self, cubic_shape: &CubicBezierShape, out: &mut Mesh) {
2146 let options = &self.options;
2147 let clip_rect = self.clip_rect;
2148 if options.coarse_tessellation_culling
2149 && !cubic_shape.visual_bounding_rect().intersects(clip_rect)
2150 {
2151 return;
2152 }
2153
2154 let points_vec =
2155 cubic_shape.flatten_closed(Some(options.bezier_tolerance), Some(options.epsilon));
2156
2157 for points in points_vec {
2158 self.tessellate_bezier_complete(
2159 &points,
2160 cubic_shape.fill,
2161 cubic_shape.closed,
2162 &cubic_shape.stroke,
2163 out,
2164 );
2165 }
2166 }
2167
2168 fn tessellate_bezier_complete(
2169 &mut self,
2170 points: &[Pos2],
2171 fill: Color32,
2172 closed: bool,
2173 stroke: &PathStroke,
2174 out: &mut Mesh,
2175 ) {
2176 if points.len() < 2 {
2177 return;
2178 }
2179
2180 self.scratchpad_path.clear();
2181 if closed {
2182 self.scratchpad_path.add_line_loop(points);
2183
2184 self.scratchpad_path
2185 .fill_and_stroke(self.feathering, fill, stroke, out);
2186 } else {
2187 debug_assert_eq!(
2188 fill,
2189 Color32::TRANSPARENT,
2190 "You asked to fill a bezier path that is not closed. That makes no sense."
2191 );
2192
2193 self.scratchpad_path.add_open_points(points);
2194
2195 self.scratchpad_path
2196 .stroke(self.feathering, PathType::Open, stroke, out);
2197 }
2198 }
2199}
2200
2201impl Tessellator {
2202 #[allow(clippy::allow_attributes, unused_mut)]
2218 pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {
2219 profiling::function_scope!();
2220
2221 #[cfg(feature = "rayon")]
2222 if self.options.parallel_tessellation {
2223 self.parallel_tessellation_of_large_shapes(&mut shapes);
2224 }
2225
2226 let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default();
2227
2228 {
2229 profiling::scope!("tessellate");
2230 for clipped_shape in shapes {
2231 self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives);
2232 }
2233 }
2234
2235 if self.options.debug_paint_clip_rects {
2236 clipped_primitives = self.add_clip_rects(clipped_primitives);
2237 }
2238
2239 if self.options.debug_ignore_clip_rects {
2240 for clipped_primitive in &mut clipped_primitives {
2241 clipped_primitive.clip_rect = Rect::EVERYTHING;
2242 }
2243 }
2244
2245 clipped_primitives.retain(|p| {
2246 p.clip_rect.is_positive()
2247 && match &p.primitive {
2248 Primitive::Mesh(mesh) => !mesh.is_empty(),
2249 Primitive::Callback(_) => true,
2250 }
2251 });
2252
2253 for clipped_primitive in &clipped_primitives {
2254 if let Primitive::Mesh(mesh) = &clipped_primitive.primitive {
2255 debug_assert!(mesh.is_valid(), "Tessellator generated invalid Mesh");
2256 }
2257 }
2258
2259 clipped_primitives
2260 }
2261
2262 #[cfg(feature = "rayon")]
2265 fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) {
2266 profiling::function_scope!();
2267
2268 use rayon::prelude::*;
2269
2270 fn should_parallelize(shape: &Shape) -> bool {
2274 match shape {
2275 Shape::Vec(shapes) => 4 < shapes.len() || shapes.iter().any(should_parallelize),
2276
2277 Shape::Path(path_shape) => 32 < path_shape.points.len(),
2278
2279 Shape::QuadraticBezier(_) | Shape::CubicBezier(_) | Shape::Ellipse(_) => true,
2280
2281 Shape::Noop
2282 | Shape::Text(_)
2283 | Shape::Circle(_)
2284 | Shape::Mesh(_)
2285 | Shape::LineSegment { .. }
2286 | Shape::Rect(_)
2287 | Shape::Callback(_) => false,
2288 }
2289 }
2290
2291 let tessellated: Vec<(usize, Mesh)> = shapes
2292 .par_iter()
2293 .enumerate()
2294 .filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape))
2295 .map(|(index, clipped_shape)| {
2296 profiling::scope!("tessellate_big_shape");
2297 let mut tessellator = (*self).clone();
2299 let mut mesh = Mesh::default();
2300 tessellator.tessellate_shape(clipped_shape.shape.clone(), &mut mesh);
2301 (index, mesh)
2302 })
2303 .collect();
2304
2305 profiling::scope!("distribute results", tessellated.len().to_string());
2306 for (index, mesh) in tessellated {
2307 shapes[index].shape = Shape::Mesh(mesh.into());
2308 }
2309 }
2310
2311 fn add_clip_rects(
2312 &mut self,
2313 clipped_primitives: Vec<ClippedPrimitive>,
2314 ) -> Vec<ClippedPrimitive> {
2315 self.clip_rect = Rect::EVERYTHING;
2316 let stroke = Stroke::new(2.0, Color32::from_rgb(150, 255, 150));
2317
2318 clipped_primitives
2319 .into_iter()
2320 .flat_map(|clipped_primitive| {
2321 let mut clip_rect_mesh = Mesh::default();
2322 self.tessellate_shape(
2323 Shape::rect_stroke(
2324 clipped_primitive.clip_rect,
2325 0.0,
2326 stroke,
2327 StrokeKind::Outside,
2328 ),
2329 &mut clip_rect_mesh,
2330 );
2331
2332 [
2333 clipped_primitive,
2334 ClippedPrimitive {
2335 clip_rect: Rect::EVERYTHING, primitive: Primitive::Mesh(clip_rect_mesh),
2337 },
2338 ]
2339 })
2340 .collect()
2341 }
2342}
2343
2344#[test]
2345fn test_tessellator() {
2346 use crate::*;
2347
2348 let mut shapes = Vec::with_capacity(2);
2349
2350 let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
2351 let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
2352
2353 let mut mesh = Mesh::with_texture(TextureId::Managed(1));
2354 mesh.add_rect_with_uv(rect, uv, Color32::WHITE);
2355 shapes.push(Shape::mesh(mesh));
2356
2357 let mut mesh = Mesh::with_texture(TextureId::Managed(2));
2358 mesh.add_rect_with_uv(rect, uv, Color32::WHITE);
2359 shapes.push(Shape::mesh(mesh));
2360
2361 let shape = Shape::Vec(shapes);
2362 let clipped_shapes = vec![ClippedShape {
2363 clip_rect: rect,
2364 shape,
2365 }];
2366
2367 let font_tex_size = [1024, 1024]; let prepared_discs = vec![]; let primitives = Tessellator::new(1.0, Default::default(), font_tex_size, prepared_discs)
2371 .tessellate_shapes(clipped_shapes);
2372
2373 assert_eq!(primitives.len(), 2);
2374}
2375
2376#[test]
2377fn path_bounding_box() {
2378 use crate::*;
2379
2380 for i in 1..=100 {
2381 let width = i as f32;
2382
2383 let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(10.0, 10.0));
2384 let expected_rect = rect.expand((width / 2.0) + 1.5);
2385
2386 let mut mesh = Mesh::default();
2387
2388 let mut path = Path::default();
2389 path.add_open_points(&[
2390 pos2(0.0, 0.0),
2391 pos2(2.0, 0.0),
2392 pos2(5.0, 5.0),
2393 pos2(0.0, 5.0),
2394 pos2(0.0, 7.0),
2395 pos2(10.0, 10.0),
2396 ]);
2397
2398 path.stroke(
2399 1.5,
2400 PathType::Closed,
2401 &PathStroke::new_uv(width, move |r, p| {
2402 assert_eq!(r, expected_rect);
2403 assert!(
2406 r.distance_to_pos(p) <= 0.55,
2407 "passed rect {r:?} didn't contain point {p:?} (distance: {})",
2408 r.distance_to_pos(p)
2409 );
2410 assert!(
2411 expected_rect.distance_to_pos(p) <= 0.55,
2412 "expected rect {expected_rect:?} didn't contain point {p:?}"
2413 );
2414 Color32::WHITE
2415 }),
2416 &mut mesh,
2417 );
2418 }
2419}