Draws a rounded rectangle as a single triangle-fan mesh. This avoids the visual artifacts of multi-shape rendering and handles alpha correctly.
(x: f32, y: f32, w: f32, h: f32, cr: &CornerRadii, color: Color)
| 589 | /// Draws a rounded rectangle as a single triangle-fan mesh. |
| 590 | /// This avoids the visual artifacts of multi-shape rendering and handles alpha correctly. |
| 591 | fn draw_good_rounded_rectangle(x: f32, y: f32, w: f32, h: f32, cr: &CornerRadii, color: Color) { |
| 592 | use std::f32::consts::{FRAC_PI_2, PI}; |
| 593 | |
| 594 | if cr.top_left == 0.0 && cr.top_right == 0.0 && cr.bottom_left == 0.0 && cr.bottom_right == 0.0 { |
| 595 | draw_rectangle(x, y, w, h, color); |
| 596 | return; |
| 597 | } |
| 598 | |
| 599 | // Generate outline vertices for the rounded rectangle |
| 600 | // Pre-allocate: each corner produces ~(FRAC_PI_2 * radius / PIXELS_PER_POINT).max(6) + 1 vertices |
| 601 | let est_verts = [cr.top_left, cr.top_right, cr.bottom_left, cr.bottom_right] |
| 602 | .iter() |
| 603 | .map(|&r| if r <= 0.0 { 1 } else { ((FRAC_PI_2 * r) / PIXELS_PER_POINT).max(6.0) as usize + 1 }) |
| 604 | .sum::<usize>(); |
| 605 | let mut outline: Vec<Vec2> = Vec::with_capacity(est_verts); |
| 606 | |
| 607 | let add_arc = |outline: &mut Vec<Vec2>, cx: f32, cy: f32, radius: f32, start_angle: f32, end_angle: f32| { |
| 608 | if radius <= 0.0 { |
| 609 | outline.push(Vec2::new(cx, cy)); |
| 610 | return; |
| 611 | } |
| 612 | let sides = ((FRAC_PI_2 * radius) / PIXELS_PER_POINT).max(6.0) as usize; |
| 613 | // Use incremental rotation to avoid per-point cos/sin |
| 614 | let step = (end_angle - start_angle) / sides as f32; |
| 615 | let step_cos = step.cos(); |
| 616 | let step_sin = step.sin(); |
| 617 | let mut dx = start_angle.cos() * radius; |
| 618 | let mut dy = start_angle.sin() * radius; |
| 619 | for _ in 0..=sides { |
| 620 | outline.push(Vec2::new(cx + dx, cy + dy)); |
| 621 | let new_dx = dx * step_cos - dy * step_sin; |
| 622 | let new_dy = dx * step_sin + dy * step_cos; |
| 623 | dx = new_dx; |
| 624 | dy = new_dy; |
| 625 | } |
| 626 | }; |
| 627 | |
| 628 | // Top-left corner: arc from π to 3π/2 |
| 629 | add_arc(&mut outline, x + cr.top_left, y + cr.top_left, cr.top_left, |
| 630 | PI, 3.0 * FRAC_PI_2); |
| 631 | // Top-right corner: arc from 3π/2 to 2π |
| 632 | add_arc(&mut outline, x + w - cr.top_right, y + cr.top_right, cr.top_right, |
| 633 | 3.0 * FRAC_PI_2, 2.0 * PI); |
| 634 | // Bottom-right corner: arc from 0 to π/2 |
| 635 | add_arc(&mut outline, x + w - cr.bottom_right, y + h - cr.bottom_right, cr.bottom_right, |
| 636 | 0.0, FRAC_PI_2); |
| 637 | // Bottom-left corner: arc from π/2 to π |
| 638 | add_arc(&mut outline, x + cr.bottom_left, y + h - cr.bottom_left, cr.bottom_left, |
| 639 | FRAC_PI_2, PI); |
| 640 | |
| 641 | let n = outline.len(); |
| 642 | if n < 3 { return; } |
| 643 | |
| 644 | let color_bytes = [ |
| 645 | (color.r * 255.0) as u8, |
| 646 | (color.g * 255.0) as u8, |
| 647 | (color.b * 255.0) as u8, |
| 648 | (color.a * 255.0) as u8, |
no outgoing calls
no test coverage detected