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