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 #[deprecated = "Use `tessellate_line_segment` instead"]
1707 pub fn tessellate_line(
1708 &mut self,
1709 points: [Pos2; 2],
1710 stroke: impl Into<Stroke>,
1711 out: &mut Mesh,
1712 ) {
1713 self.tessellate_line_segment(points, stroke, out);
1714 }
1715
1716 pub fn tessellate_path(&mut self, path_shape: &PathShape, out: &mut Mesh) {
1721 if path_shape.points.len() < 2 {
1722 return;
1723 }
1724
1725 if self.options.coarse_tessellation_culling
1726 && !path_shape.visual_bounding_rect().intersects(self.clip_rect)
1727 {
1728 return;
1729 }
1730
1731 profiling::function_scope!();
1732
1733 let PathShape {
1734 points,
1735 closed,
1736 fill,
1737 stroke,
1738 } = path_shape;
1739
1740 self.scratchpad_path.clear();
1741
1742 if *closed {
1743 self.scratchpad_path.add_line_loop(points);
1744
1745 self.scratchpad_path
1746 .fill_and_stroke(self.feathering, *fill, stroke, out);
1747 } else {
1748 debug_assert_eq!(
1749 *fill,
1750 Color32::TRANSPARENT,
1751 "You asked to fill a path that is not closed. That makes no sense."
1752 );
1753
1754 self.scratchpad_path.add_open_points(points);
1755
1756 self.scratchpad_path
1757 .stroke(self.feathering, PathType::Open, stroke, out);
1758 }
1759 }
1760
1761 pub fn tessellate_rect(&mut self, rect_shape: &RectShape, out: &mut Mesh) {
1766 if self.options.coarse_tessellation_culling
1767 && !rect_shape.visual_bounding_rect().intersects(self.clip_rect)
1768 {
1769 return;
1770 }
1771
1772 let brush = rect_shape.brush.as_ref();
1773 let RectShape {
1774 mut rect,
1775 corner_radius,
1776 mut fill,
1777 mut stroke,
1778 mut stroke_kind,
1779 round_to_pixels,
1780 mut blur_width,
1781 brush: _, angle,
1783 } = *rect_shape;
1784
1785 let mut corner_radius = CornerRadiusF32::from(corner_radius);
1786 let round_to_pixels = round_to_pixels.unwrap_or(self.options.round_rects_to_pixels);
1787
1788 if stroke.width == 0.0 {
1789 stroke.color = Color32::TRANSPARENT;
1790 }
1791
1792 rect.min = rect.min.at_least(pos2(-1e7, -1e7));
1795 rect.max = rect.max.at_most(pos2(1e7, 1e7));
1796
1797 if !stroke.is_empty() {
1798 let rect_with_stroke = match stroke_kind {
1800 StrokeKind::Inside => rect,
1801 StrokeKind::Middle => rect.expand(stroke.width / 2.0),
1802 StrokeKind::Outside => rect.expand(stroke.width),
1803 };
1804
1805 if rect_with_stroke.size().min_elem() <= 2.0 * stroke.width + 0.5 * self.feathering {
1806 rect = rect_with_stroke;
1809
1810 fill = stroke.color;
1813
1814 stroke = Stroke::NONE;
1815 }
1816 }
1817
1818 if stroke.is_empty() && out.texture_id == TextureId::default() {
1819 if rect.width() <= 2.0 * self.feathering {
1822 return self.tessellate_line_segment(
1823 [rect.center_top(), rect.center_bottom()],
1824 (rect.width(), fill),
1825 out,
1826 );
1827 }
1828 if rect.height() <= 2.0 * self.feathering {
1829 return self.tessellate_line_segment(
1830 [rect.left_center(), rect.right_center()],
1831 (rect.height(), fill),
1832 out,
1833 );
1834 }
1835 }
1836
1837 if round_to_pixels {
1839 match stroke_kind {
1842 StrokeKind::Inside => {
1843 rect = rect.round_to_pixels(self.pixels_per_point);
1854 }
1855 StrokeKind::Middle => {
1856 stroke.round_rect_to_pixel(self.pixels_per_point, &mut rect);
1858 }
1859 StrokeKind::Outside => {
1860 rect = rect.round_to_pixels(self.pixels_per_point);
1868 }
1869 }
1870 }
1871
1872 let old_feathering = self.feathering;
1873
1874 if self.feathering < blur_width {
1875 let eps = 0.1; blur_width = blur_width
1881 .at_most(rect.size().min_elem() - eps - 2.0 * stroke.width)
1882 .at_least(0.0);
1883
1884 corner_radius += 0.5 * blur_width;
1885
1886 self.feathering = self.feathering.max(blur_width);
1887 }
1888
1889 {
1890 let original_cr = corner_radius;
1895
1896 match stroke_kind {
1897 StrokeKind::Inside => {}
1898 StrokeKind::Middle => {
1899 rect = rect.expand(stroke.width / 2.0);
1900 corner_radius += stroke.width / 2.0;
1901 }
1902 StrokeKind::Outside => {
1903 rect = rect.expand(stroke.width);
1904 corner_radius += stroke.width;
1905 }
1906 }
1907
1908 stroke_kind = StrokeKind::Inside;
1909
1910 let min_inside_cr = 0.1; let min_outside_cr = stroke.width + min_inside_cr;
1918
1919 let extra_cr_tweak = 0.4; if original_cr.nw == 0.0 {
1922 corner_radius.nw = 0.0;
1923 } else {
1924 corner_radius.nw += extra_cr_tweak;
1925 corner_radius.nw = corner_radius.nw.at_least(min_outside_cr);
1926 }
1927 if original_cr.ne == 0.0 {
1928 corner_radius.ne = 0.0;
1929 } else {
1930 corner_radius.ne += extra_cr_tweak;
1931 corner_radius.ne = corner_radius.ne.at_least(min_outside_cr);
1932 }
1933 if original_cr.sw == 0.0 {
1934 corner_radius.sw = 0.0;
1935 } else {
1936 corner_radius.sw += extra_cr_tweak;
1937 corner_radius.sw = corner_radius.sw.at_least(min_outside_cr);
1938 }
1939 if original_cr.se == 0.0 {
1940 corner_radius.se = 0.0;
1941 } else {
1942 corner_radius.se += extra_cr_tweak;
1943 corner_radius.se = corner_radius.se.at_least(min_outside_cr);
1944 }
1945 }
1946
1947 let path = &mut self.scratchpad_path;
1948 path.clear();
1949 path::rounded_rectangle(&mut self.scratchpad_points, rect, corner_radius);
1950
1951 if angle != 0.0 {
1953 let rot = emath::Rot2::from_angle(angle);
1954 let center = rect.center();
1955 for point in &mut self.scratchpad_points {
1956 *point = center + rot * (*point - center);
1957 }
1958 }
1959
1960 path.add_line_loop(&self.scratchpad_points);
1961
1962 let path_stroke = PathStroke::from(stroke).with_kind(stroke_kind);
1963
1964 if let Some(brush) = brush {
1965 let fill_rect = match stroke_kind {
1968 StrokeKind::Inside => rect.shrink(stroke.width),
1969 StrokeKind::Middle => rect.shrink(stroke.width / 2.0),
1970 StrokeKind::Outside => rect,
1971 };
1972
1973 if fill_rect.is_positive() {
1974 let crate::Brush {
1975 fill_texture_id,
1976 uv,
1977 } = **brush;
1978 let uv_from_pos = |p: Pos2| {
1979 pos2(
1980 remap(p.x, rect.x_range(), uv.x_range()),
1981 remap(p.y, rect.y_range(), uv.y_range()),
1982 )
1983 };
1984 path.fill_with_uv(self.feathering, fill, fill_texture_id, uv_from_pos, out);
1985 }
1986
1987 if !stroke.is_empty() {
1988 path.stroke_closed(self.feathering, &path_stroke, out);
1989 }
1990 } else {
1991 path.fill_and_stroke(self.feathering, fill, &path_stroke, out);
1993 }
1994
1995 self.feathering = old_feathering; }
1997
1998 pub fn tessellate_text(&mut self, text_shape: &TextShape, out: &mut Mesh) {
2002 let TextShape {
2003 pos: galley_pos,
2004 galley,
2005 underline,
2006 override_text_color,
2007 fallback_color,
2008 opacity_factor,
2009 angle,
2010 } = text_shape;
2011
2012 if galley.is_empty() {
2013 return;
2014 }
2015
2016 if *opacity_factor <= 0.0 {
2017 return;
2018 }
2019
2020 if galley.pixels_per_point != self.pixels_per_point {
2021 log::warn!(
2022 "epaint: WARNING: pixels_per_point (dpi scale) have changed between text layout and tessellation. \
2023 You must recreate your text shapes if pixels_per_point changes."
2024 );
2025 }
2026
2027 out.vertices.reserve(galley.num_vertices);
2028 out.indices.reserve(galley.num_indices);
2029
2030 let galley_pos = if self.options.round_text_to_pixels {
2033 galley_pos.round_to_pixels(self.pixels_per_point)
2034 } else {
2035 *galley_pos
2036 };
2037
2038 let uv_normalizer = vec2(
2039 1.0 / self.font_tex_size[0] as f32,
2040 1.0 / self.font_tex_size[1] as f32,
2041 );
2042
2043 let rotator = Rot2::from_angle(*angle);
2044
2045 for row in &galley.rows {
2046 if row.visuals.mesh.is_empty() {
2047 continue;
2048 }
2049
2050 let final_row_pos = galley_pos + rotator * row.pos.to_vec2();
2051
2052 let mut row_rect = row.visuals.mesh_bounds;
2053 if *angle != 0.0 {
2054 row_rect = row_rect.rotate_bb(rotator);
2055 }
2056 row_rect = row_rect.translate(final_row_pos.to_vec2());
2057
2058 if self.options.coarse_tessellation_culling && !self.clip_rect.intersects(row_rect) {
2059 continue;
2062 }
2063
2064 let index_offset = out.vertices.len() as u32;
2065
2066 out.indices.extend(
2067 row.visuals
2068 .mesh
2069 .indices
2070 .iter()
2071 .map(|index| index + index_offset),
2072 );
2073
2074 out.vertices.extend(
2075 row.visuals
2076 .mesh
2077 .vertices
2078 .iter()
2079 .enumerate()
2080 .map(|(i, vertex)| {
2081 let Vertex { pos, uv, mut color } = *vertex;
2082
2083 if let Some(override_text_color) = override_text_color {
2084 if row.visuals.glyph_vertex_range.contains(&i) {
2086 color = *override_text_color;
2087 }
2088 } else if color == Color32::PLACEHOLDER {
2089 color = *fallback_color;
2090 }
2091
2092 if *opacity_factor < 1.0 {
2093 color = color.gamma_multiply(*opacity_factor);
2094 }
2095
2096 debug_assert!(color != Color32::PLACEHOLDER, "A placeholder color made it to the tessellator. You forgot to set a fallback color.");
2097
2098 let offset = if *angle == 0.0 {
2099 pos.to_vec2()
2100 } else {
2101 rotator * pos.to_vec2()
2102 };
2103
2104 Vertex {
2105 pos: final_row_pos + offset,
2106 uv: (uv.to_vec2() * uv_normalizer).to_pos2(),
2107 color,
2108 }
2109 }),
2110 );
2111
2112 if *underline != Stroke::NONE {
2113 self.tessellate_line_segment(
2114 [row_rect.left_bottom(), row_rect.right_bottom()],
2115 *underline,
2116 out,
2117 );
2118 }
2119 }
2120 }
2121
2122 pub fn tessellate_quadratic_bezier(
2127 &mut self,
2128 quadratic_shape: &QuadraticBezierShape,
2129 out: &mut Mesh,
2130 ) {
2131 let options = &self.options;
2132 let clip_rect = self.clip_rect;
2133
2134 if options.coarse_tessellation_culling
2135 && !quadratic_shape.visual_bounding_rect().intersects(clip_rect)
2136 {
2137 return;
2138 }
2139
2140 let points = quadratic_shape.flatten(Some(options.bezier_tolerance));
2141
2142 self.tessellate_bezier_complete(
2143 &points,
2144 quadratic_shape.fill,
2145 quadratic_shape.closed,
2146 &quadratic_shape.stroke,
2147 out,
2148 );
2149 }
2150
2151 pub fn tessellate_cubic_bezier(&mut self, cubic_shape: &CubicBezierShape, out: &mut Mesh) {
2156 let options = &self.options;
2157 let clip_rect = self.clip_rect;
2158 if options.coarse_tessellation_culling
2159 && !cubic_shape.visual_bounding_rect().intersects(clip_rect)
2160 {
2161 return;
2162 }
2163
2164 let points_vec =
2165 cubic_shape.flatten_closed(Some(options.bezier_tolerance), Some(options.epsilon));
2166
2167 for points in points_vec {
2168 self.tessellate_bezier_complete(
2169 &points,
2170 cubic_shape.fill,
2171 cubic_shape.closed,
2172 &cubic_shape.stroke,
2173 out,
2174 );
2175 }
2176 }
2177
2178 fn tessellate_bezier_complete(
2179 &mut self,
2180 points: &[Pos2],
2181 fill: Color32,
2182 closed: bool,
2183 stroke: &PathStroke,
2184 out: &mut Mesh,
2185 ) {
2186 if points.len() < 2 {
2187 return;
2188 }
2189
2190 self.scratchpad_path.clear();
2191 if closed {
2192 self.scratchpad_path.add_line_loop(points);
2193
2194 self.scratchpad_path
2195 .fill_and_stroke(self.feathering, fill, stroke, out);
2196 } else {
2197 debug_assert_eq!(
2198 fill,
2199 Color32::TRANSPARENT,
2200 "You asked to fill a bezier path that is not closed. That makes no sense."
2201 );
2202
2203 self.scratchpad_path.add_open_points(points);
2204
2205 self.scratchpad_path
2206 .stroke(self.feathering, PathType::Open, stroke, out);
2207 }
2208 }
2209}
2210
2211impl Tessellator {
2212 #[allow(clippy::allow_attributes, unused_mut)]
2228 pub fn tessellate_shapes(&mut self, mut shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {
2229 profiling::function_scope!();
2230
2231 #[cfg(feature = "rayon")]
2232 if self.options.parallel_tessellation {
2233 self.parallel_tessellation_of_large_shapes(&mut shapes);
2234 }
2235
2236 let mut clipped_primitives: Vec<ClippedPrimitive> = Vec::default();
2237
2238 {
2239 profiling::scope!("tessellate");
2240 for clipped_shape in shapes {
2241 self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives);
2242 }
2243 }
2244
2245 if self.options.debug_paint_clip_rects {
2246 clipped_primitives = self.add_clip_rects(clipped_primitives);
2247 }
2248
2249 if self.options.debug_ignore_clip_rects {
2250 for clipped_primitive in &mut clipped_primitives {
2251 clipped_primitive.clip_rect = Rect::EVERYTHING;
2252 }
2253 }
2254
2255 clipped_primitives.retain(|p| {
2256 p.clip_rect.is_positive()
2257 && match &p.primitive {
2258 Primitive::Mesh(mesh) => !mesh.is_empty(),
2259 Primitive::Callback(_) => true,
2260 }
2261 });
2262
2263 for clipped_primitive in &clipped_primitives {
2264 if let Primitive::Mesh(mesh) = &clipped_primitive.primitive {
2265 debug_assert!(mesh.is_valid(), "Tessellator generated invalid Mesh");
2266 }
2267 }
2268
2269 clipped_primitives
2270 }
2271
2272 #[cfg(feature = "rayon")]
2275 fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) {
2276 profiling::function_scope!();
2277
2278 use rayon::prelude::*;
2279
2280 fn should_parallelize(shape: &Shape) -> bool {
2284 match shape {
2285 Shape::Vec(shapes) => 4 < shapes.len() || shapes.iter().any(should_parallelize),
2286
2287 Shape::Path(path_shape) => 32 < path_shape.points.len(),
2288
2289 Shape::QuadraticBezier(_) | Shape::CubicBezier(_) | Shape::Ellipse(_) => true,
2290
2291 Shape::Noop
2292 | Shape::Text(_)
2293 | Shape::Circle(_)
2294 | Shape::Mesh(_)
2295 | Shape::LineSegment { .. }
2296 | Shape::Rect(_)
2297 | Shape::Callback(_) => false,
2298 }
2299 }
2300
2301 let tessellated: Vec<(usize, Mesh)> = shapes
2302 .par_iter()
2303 .enumerate()
2304 .filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape))
2305 .map(|(index, clipped_shape)| {
2306 profiling::scope!("tessellate_big_shape");
2307 let mut tessellator = (*self).clone();
2309 let mut mesh = Mesh::default();
2310 tessellator.tessellate_shape(clipped_shape.shape.clone(), &mut mesh);
2311 (index, mesh)
2312 })
2313 .collect();
2314
2315 profiling::scope!("distribute results", tessellated.len().to_string());
2316 for (index, mesh) in tessellated {
2317 shapes[index].shape = Shape::Mesh(mesh.into());
2318 }
2319 }
2320
2321 fn add_clip_rects(
2322 &mut self,
2323 clipped_primitives: Vec<ClippedPrimitive>,
2324 ) -> Vec<ClippedPrimitive> {
2325 self.clip_rect = Rect::EVERYTHING;
2326 let stroke = Stroke::new(2.0, Color32::from_rgb(150, 255, 150));
2327
2328 clipped_primitives
2329 .into_iter()
2330 .flat_map(|clipped_primitive| {
2331 let mut clip_rect_mesh = Mesh::default();
2332 self.tessellate_shape(
2333 Shape::rect_stroke(
2334 clipped_primitive.clip_rect,
2335 0.0,
2336 stroke,
2337 StrokeKind::Outside,
2338 ),
2339 &mut clip_rect_mesh,
2340 );
2341
2342 [
2343 clipped_primitive,
2344 ClippedPrimitive {
2345 clip_rect: Rect::EVERYTHING, primitive: Primitive::Mesh(clip_rect_mesh),
2347 },
2348 ]
2349 })
2350 .collect()
2351 }
2352}
2353
2354#[test]
2355fn test_tessellator() {
2356 use crate::*;
2357
2358 let mut shapes = Vec::with_capacity(2);
2359
2360 let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
2361 let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0));
2362
2363 let mut mesh = Mesh::with_texture(TextureId::Managed(1));
2364 mesh.add_rect_with_uv(rect, uv, Color32::WHITE);
2365 shapes.push(Shape::mesh(mesh));
2366
2367 let mut mesh = Mesh::with_texture(TextureId::Managed(2));
2368 mesh.add_rect_with_uv(rect, uv, Color32::WHITE);
2369 shapes.push(Shape::mesh(mesh));
2370
2371 let shape = Shape::Vec(shapes);
2372 let clipped_shapes = vec![ClippedShape {
2373 clip_rect: rect,
2374 shape,
2375 }];
2376
2377 let font_tex_size = [1024, 1024]; let prepared_discs = vec![]; let primitives = Tessellator::new(1.0, Default::default(), font_tex_size, prepared_discs)
2381 .tessellate_shapes(clipped_shapes);
2382
2383 assert_eq!(primitives.len(), 2);
2384}
2385
2386#[test]
2387fn path_bounding_box() {
2388 use crate::*;
2389
2390 for i in 1..=100 {
2391 let width = i as f32;
2392
2393 let rect = Rect::from_min_max(pos2(0.0, 0.0), pos2(10.0, 10.0));
2394 let expected_rect = rect.expand((width / 2.0) + 1.5);
2395
2396 let mut mesh = Mesh::default();
2397
2398 let mut path = Path::default();
2399 path.add_open_points(&[
2400 pos2(0.0, 0.0),
2401 pos2(2.0, 0.0),
2402 pos2(5.0, 5.0),
2403 pos2(0.0, 5.0),
2404 pos2(0.0, 7.0),
2405 pos2(10.0, 10.0),
2406 ]);
2407
2408 path.stroke(
2409 1.5,
2410 PathType::Closed,
2411 &PathStroke::new_uv(width, move |r, p| {
2412 assert_eq!(r, expected_rect);
2413 assert!(
2416 r.distance_to_pos(p) <= 0.55,
2417 "passed rect {r:?} didn't contain point {p:?} (distance: {})",
2418 r.distance_to_pos(p)
2419 );
2420 assert!(
2421 expected_rect.distance_to_pos(p) <= 0.55,
2422 "expected rect {expected_rect:?} didn't contain point {p:?}"
2423 );
2424 Color32::WHITE
2425 }),
2426 &mut mesh,
2427 );
2428 }
2429}