1use std::ops::Sub;
2
3use glam::Vec2;
4
5use crate::winding_order::WindingOrder;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Corner {
18 TopRight,
19 TopLeft,
20 BottomLeft,
21 BottomRight,
22 Invalid,
24}
25
26impl Corner {
27 pub const TOP_RIGHT: Vec2 = Vec2::ONE;
29
30 pub const TOP_LEFT: Vec2 = Vec2::new(-1.0, 1.0);
32
33 pub const BOTTOM_LEFT: Vec2 = Vec2::NEG_ONE;
35
36 pub const BOTTOM_RIGHT: Vec2 = Vec2::new(1.0, -1.0);
38}
39
40impl Corner {
41 #[inline]
43 pub const fn new(value: Vec2) -> Self {
44 match value {
45 Self::TOP_RIGHT => Self::TopRight,
46 Self::TOP_LEFT => Self::TopLeft,
47 Self::BOTTOM_LEFT => Self::BottomLeft,
48 Self::BOTTOM_RIGHT => Self::BottomRight,
49 _ => Self::Invalid,
50 }
51 }
52
53 #[inline]
55 pub fn calculate(prev: Vec2, current: Vec2, next: Vec2) -> Self {
56 let center = prev.midpoint(next);
57 let sign = current.sub(center).signum();
58 Self::new(sign)
59 }
60
61 #[inline]
63 pub const fn as_vec2(&self) -> Vec2 {
64 match self {
65 Self::TopRight => Self::TOP_RIGHT,
66 Self::TopLeft => Self::TOP_LEFT,
67 Self::BottomLeft => Self::BOTTOM_LEFT,
68 Self::BottomRight => Self::BOTTOM_RIGHT,
69 Self::Invalid => Vec2::ZERO,
70 }
71 }
72}
73
74#[derive(Clone, Copy)]
81pub struct CornerPathParams {
82 pub a: f32,
83 pub b: f32,
84 pub c: f32,
85 pub d: f32,
86 pub p: f32,
87 pub corner_radius: f32,
88 pub arc_section_length: f32,
89 pub arc_theta: f32,
90}
91
92impl CornerPathParams {
93 #[inline]
94 pub fn new(mut corner_radius: f32, max_radius: f32, smoothness: f32) -> Self {
95 let mut p = (1.0 + smoothness) * corner_radius;
97
98 if p > max_radius {
99 p = max_radius;
100 corner_radius = p / (1.0 + smoothness);
101 }
102
103 let quarter = f32::to_radians(45.0);
104 let angle_alpha = quarter * smoothness;
105 let angle_beta = (90.0 * (1.0 - smoothness)).to_radians();
106 let arc_theta = quarter - angle_beta * 0.5;
107
108 let p3_to_p4_distance = corner_radius * arc_theta.tan() * 0.5;
111
112 let arc_section_length = corner_radius * f32::sqrt(2.0) * angle_beta.sin() * 0.5;
114
115 let c = p3_to_p4_distance * angle_alpha.cos();
118 let d = c * angle_alpha.tan();
119 let b = (p - arc_section_length - c - d) / 3.0;
120 let a = b * 2.0;
121
122 Self {
123 a,
124 b,
125 c,
126 d,
127 p,
128 corner_radius,
129 arc_section_length,
130 arc_theta,
131 }
132 }
133
134 #[inline]
139 pub fn squircle(self, current: Vec2, corner: Corner, winding_order: WindingOrder) -> Squircle {
140 let Self {
141 a,
142 b,
143 c,
144 d,
145 p,
146 corner_radius,
147 arc_theta,
148 ..
149 } = self;
150
151 let edges = corner.as_vec2();
153 let orientation = winding_order.as_f32();
154 let Vec2 { x, y } = edges;
155 let product = x * y;
156
157 let is_ccw = orientation == 1.0;
159 let is_tl_or_br = product == -1.0;
161 let should_swap = is_ccw ^ is_tl_or_br;
163
164 let radii = Vec2::splat(corner_radius);
166 let center = current - radii * edges;
167 let sweep_angle = arc_theta * orientation;
168
169 let d = product * -d;
171
172 let mut h = {
174 let p0 = current - Vec2::new(p, 0.0) * x;
175 let ctrl1 = p0 + Vec2::new(a, 0.0) * x;
176 let ctrl2 = p0 + Vec2::new(a + b, 0.0) * x;
177 let to0 = p0 + Vec2::new(a + b + c, d) * x;
178 [p0, ctrl1, ctrl2, to0]
179 };
180 let mut v = {
182 let p0 = current - Vec2::new(0.0, p) * y;
183 let ctrl1 = p0 + Vec2::new(0.0, a) * y;
184 let ctrl2 = p0 + Vec2::new(0.0, a + b) * y;
185 let to0 = p0 + Vec2::new(d, a + b + c) * y;
186 [p0, ctrl1, ctrl2, to0]
187 };
188
189 if should_swap {
190 ::core::mem::swap(&mut h, &mut v);
191 }
192
193 Squircle {
194 h,
195 v,
196 center,
197 radii,
198 sweep_angle,
199 }
200 }
201}
202
203#[derive(Clone, Copy)]
224pub struct Squircle {
225 pub h: [Vec2; 4],
226 pub v: [Vec2; 4],
227 pub center: Vec2,
228 pub radii: Vec2,
229 pub sweep_angle: f32,
230}