bymsdfgen_core/geometry/
shape.rs1use super::contour::Contour;
5use super::convergent::convergent_curve_ordering;
6use super::edge_segment::EdgeSegment;
7use crate::math::scalar::{mix, sign};
8use crate::math::{Vector2, dot};
9
10const CORNER_DOT_EPSILON: f64 = 0.000_001;
12const DECONVERGE_OVERSHOOT: f64 = 1.111_111_111_111_111_1;
14const LARGE_VALUE: f64 = 1e240;
15
16#[derive(Debug, Clone, Copy, PartialEq)]
18pub struct Bounds {
19 pub l: f64,
20 pub b: f64,
21 pub r: f64,
22 pub t: f64,
23}
24
25#[derive(Debug, Clone, Default)]
26pub struct Shape {
27 pub contours: Vec<Contour>,
28 pub inverse_y_axis: bool,
30}
31
32fn deconverge_edge(edge: &mut EdgeSegment, param: i32, vector: Vector2) {
33 if matches!(edge, EdgeSegment::Quadratic(_)) {
34 *edge = edge.convert_to_cubic();
35 }
36 if let EdgeSegment::Cubic(p) = edge {
37 match param {
38 0 => p[1] = p[1] + (p[1] - p[0]).length() * vector,
39 1 => p[2] = p[2] + (p[2] - p[3]).length() * vector,
40 _ => {}
41 }
42 }
43}
44
45impl Shape {
46 pub fn new() -> Self {
47 Shape::default()
48 }
49
50 pub fn add_contour(&mut self, contour: Contour) {
51 self.contours.push(contour);
52 }
53
54 pub fn add_contour_mut(&mut self) -> &mut Contour {
56 self.contours.push(Contour::new());
57 self.contours.last_mut().unwrap()
58 }
59
60 pub fn validate(&self) -> bool {
63 for contour in &self.contours {
64 if let Some(last) = contour.segments.last() {
65 let mut corner = last.point(1.0);
66 for edge in &contour.segments {
67 if edge.point(0.0) != corner {
68 return false;
69 }
70 corner = edge.point(1.0);
71 }
72 }
73 }
74 true
75 }
76
77 pub fn normalize(&mut self) {
80 for contour in &mut self.contours {
81 let n = contour.segments.len();
82 if n == 1 {
83 let parts = contour.segments[0].split_in_thirds();
84 let color = contour.colors[0];
85 contour.segments = parts.to_vec();
86 contour.colors = vec![color; 3];
87 } else if n >= 2 {
88 let mut prev_idx = n - 1;
89 for cur_idx in 0..n {
90 let prev_dir = contour.segments[prev_idx].direction(1.0).normalize(false);
91 let cur_dir = contour.segments[cur_idx].direction(0.0).normalize(false);
92 if dot(prev_dir, cur_dir) < CORNER_DOT_EPSILON - 1.0 {
93 let e = CORNER_DOT_EPSILON - 1.0;
94 let factor = DECONVERGE_OVERSHOOT * (1.0 - e * e).sqrt() / e;
95 let mut axis = factor * (cur_dir - prev_dir).normalize(false);
96 if convergent_curve_ordering(
97 &contour.segments[prev_idx],
98 &contour.segments[cur_idx],
99 ) < 0
100 {
101 axis = -axis;
102 }
103 deconverge_edge(&mut contour.segments[prev_idx], 1, axis.orthogonal(true));
104 deconverge_edge(&mut contour.segments[cur_idx], 0, axis.orthogonal(false));
105 }
106 prev_idx = cur_idx;
107 }
108 }
109 }
110 }
111
112 pub fn bound(&self, min: &mut Vector2, max: &mut Vector2) {
114 for c in &self.contours {
115 c.bound(min, max);
116 }
117 }
118
119 pub fn get_bounds(&self, border: f64) -> Bounds {
121 let mut min = Vector2::new(LARGE_VALUE, LARGE_VALUE);
122 let mut max = Vector2::new(-LARGE_VALUE, -LARGE_VALUE);
123 self.bound(&mut min, &mut max);
124 let mut bounds = Bounds {
125 l: min.x,
126 b: min.y,
127 r: max.x,
128 t: max.y,
129 };
130 if border > 0.0 {
131 bounds.l -= border;
132 bounds.b -= border;
133 bounds.r += border;
134 bounds.t += border;
135 }
136 bounds
137 }
138
139 pub fn edge_count(&self) -> usize {
140 self.contours.iter().map(|c| c.segments.len()).sum()
141 }
142
143 pub fn orient_contours(&mut self) {
146 #[derive(Clone, Copy)]
147 struct Inter {
148 x: f64,
149 direction: i32,
150 contour_index: usize,
151 }
152
153 let ratio = 0.5 * (5.0_f64.sqrt() - 1.0); let nc = self.contours.len();
155 let mut orientations = vec![0i32; nc];
156 let mut intersections: Vec<Inter> = Vec::new();
157 let mut buf = [(0.0, 0); 3];
158
159 for i in 0..nc {
160 if orientations[i] != 0 || self.contours[i].segments.is_empty() {
161 continue;
162 }
163 let y0 = self.contours[i].segments[0].point(0.0).y;
165 let mut y1 = y0;
166 for edge in &self.contours[i].segments {
167 if y0 != y1 {
168 break;
169 }
170 y1 = edge.point(1.0).y;
171 }
172 for edge in &self.contours[i].segments {
173 if y0 != y1 {
174 break;
175 }
176 y1 = edge.point(ratio).y; }
178 let y = mix(y0, y1, ratio);
179
180 for (j, contour) in self.contours.iter().enumerate() {
181 for edge in &contour.segments {
182 let n = edge.scanline_intersections(y, &mut buf);
183 for &(x, dy) in buf.iter().take(n) {
184 intersections.push(Inter {
185 x,
186 direction: dy,
187 contour_index: j,
188 });
189 }
190 }
191 }
192
193 if !intersections.is_empty() {
194 intersections
195 .sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
196 for j in 1..intersections.len() {
198 if intersections[j].x == intersections[j - 1].x {
199 intersections[j].direction = 0;
200 intersections[j - 1].direction = 0;
201 }
202 }
203 for (j, inter) in intersections.iter().enumerate() {
204 if inter.direction != 0 {
205 orientations[inter.contour_index] +=
206 2 * (((j & 1) as i32) ^ ((inter.direction > 0) as i32)) - 1;
207 }
208 }
209 intersections.clear();
210 }
211 }
212
213 for (i, contour) in self.contours.iter_mut().enumerate() {
214 if orientations[i] < 0 {
215 contour.reverse();
216 }
217 }
218 let _ = sign;
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::geometry::edge_color::EdgeColor;
227
228 fn square() -> Shape {
229 let mut shape = Shape::new();
230 let c = shape.add_contour_mut();
231 let pts = [
232 Vector2::new(0.0, 0.0),
233 Vector2::new(10.0, 0.0),
234 Vector2::new(10.0, 10.0),
235 Vector2::new(0.0, 10.0),
236 ];
237 for i in 0..4 {
238 c.add_edge_colored(
239 EdgeSegment::line(pts[i], pts[(i + 1) % 4]),
240 EdgeColor::White,
241 );
242 }
243 shape
244 }
245
246 #[test]
247 fn validate_closed_square() {
248 assert!(square().validate());
249 }
250
251 #[test]
252 fn bounds_square() {
253 let b = square().get_bounds(0.0);
254 assert_eq!((b.l, b.b, b.r, b.t), (0.0, 0.0, 10.0, 10.0));
255 }
256
257 #[test]
258 fn winding_and_orient() {
259 let mut s = square();
260 assert_eq!(s.contours[0].winding(), -1);
262 s.orient_contours();
263 assert!(s.contours[0].winding() != 0);
265 }
266
267 #[test]
268 fn single_edge_contour_splits() {
269 let mut s = Shape::new();
270 let c = s.add_contour_mut();
271 c.add_edge(EdgeSegment::cubic(
273 Vector2::new(0.0, 0.0),
274 Vector2::new(2.0, 4.0),
275 Vector2::new(-2.0, 4.0),
276 Vector2::new(0.0, 0.0),
277 ));
278 s.normalize();
279 assert_eq!(s.contours[0].segments.len(), 3);
280 }
281}