1use crate::{PathFillRule, Point, Rect};
11
12#[derive(Clone, Copy, Debug, PartialEq)]
20pub struct Trapezoid {
21 pub left: f32,
22 pub right: f32,
23 pub top: [f32; 2],
25 pub bottom: [f32; 2],
27 pub open_left: bool,
28 pub open_right: bool,
29}
30
31impl Trapezoid {
32 pub fn bounds(&self) -> Rect {
34 let top = self.top[0].min(self.top[1]);
35 let bottom = self.bottom[0].max(self.bottom[1]);
36 Rect {
37 x: self.left,
38 y: top,
39 width: self.right - self.left,
40 height: bottom - top,
41 }
42 }
43
44 pub fn translate(self, dx: f32, dy: f32) -> Self {
46 Self {
47 left: self.left + dx,
48 right: self.right + dx,
49 top: self.top.map(|y| y + dy),
50 bottom: self.bottom.map(|y| y + dy),
51 ..self
52 }
53 }
54
55 pub fn coverage(&self, point: Point) -> f32 {
59 let owned =
60 (self.open_left || point.x >= self.left) && (self.open_right || point.x < self.right);
61 if self.right <= self.left || !owned {
62 return 0.0;
63 }
64 let corner = |x: f32, y: f32| Point::new(x, y);
65 let below_top = edge_distance(
66 point,
67 corner(self.left, self.top[0]),
68 corner(self.right, self.top[1]),
69 1.0,
70 );
71 let above_bottom = edge_distance(
72 point,
73 corner(self.left, self.bottom[0]),
74 corner(self.right, self.bottom[1]),
75 -1.0,
76 );
77 let band = half_pixel(below_top) + half_pixel(above_bottom) - 1.0;
78 let left = if self.open_left {
79 half_pixel(point.x - self.left)
80 } else {
81 1.0
82 };
83 let right = if self.open_right {
84 half_pixel(self.right - point.x)
85 } else {
86 1.0
87 };
88 band.max(0.0) * (left + right - 1.0).clamp(0.0, 1.0)
89 }
90}
91
92fn edge_distance(point: Point, a: Point, b: Point, side: f32) -> f32 {
97 let (dx, dy) = (b.x - a.x, b.y - a.y);
98 let extent = (dx * dx + dy * dy).sqrt();
99 let (along_x, along_y) = (dx / extent, dy / extent);
100 let (offset_x, offset_y) = (point.x - a.x, point.y - a.y);
101 let across = (offset_y * along_x - offset_x * along_y) * side;
102 let t = offset_x * along_x + offset_y * along_y;
103 if (0.0..=extent).contains(&t) {
104 return across;
105 }
106 let end = if t < 0.0 { a } else { b };
107 let distance = ((point.x - end.x).powi(2) + (point.y - end.y).powi(2)).sqrt();
108 if across < 0.0 {
109 -distance
110 } else if across > 0.0 {
111 distance
112 } else {
113 0.0
114 }
115}
116
117fn half_pixel(distance: f32) -> f32 {
120 (distance + 0.5).clamp(0.0, 1.0)
121}
122
123const MAX_EDGES: usize = 1024;
125const MAX_SLICES: usize = 4096;
127const TOLERANCE: f32 = 1.0e-3;
129
130#[derive(Clone, Copy, Debug)]
133struct Edge {
134 x0: f32,
135 y0: f32,
136 x1: f32,
137 y1: f32,
138 winding: i32,
139}
140
141impl Edge {
142 fn y_at(&self, x: f32) -> f32 {
145 if x <= self.x0 {
146 self.y0
147 } else if x >= self.x1 {
148 self.y1
149 } else {
150 self.y0 + (x - self.x0) * (self.y1 - self.y0) / (self.x1 - self.x0)
151 }
152 }
153}
154
155#[derive(Clone, Copy, Debug)]
157struct Crossing {
158 edge: Edge,
159 start: f32,
160 end: f32,
161}
162
163#[derive(Debug, Default)]
166pub(crate) struct PathSlicer {
167 edges: Vec<Edge>,
168 events: Vec<f32>,
169 active: Vec<Edge>,
170 crossings: Vec<Crossing>,
171 slices: Vec<Trapezoid>,
172}
173
174impl PathSlicer {
175 pub(crate) fn slice<'a, I>(
180 &mut self,
181 contours: I,
182 fill_rule: PathFillRule,
183 ) -> Option<&[Trapezoid]>
184 where
185 I: IntoIterator<Item = &'a [Point]>,
186 I::IntoIter: Clone,
187 {
188 let filled = contours.into_iter().filter(|points| points.len() >= 3);
189 if let (Some(points), None) = (filled.clone().next(), filled.clone().nth(1)) {
190 match self.slice_monotone(points) {
191 ChainSlicing::Sliced => return Some(&self.slices),
192 ChainSlicing::Unsliceable => return None,
193 ChainSlicing::NotMonotone => {}
194 }
195 }
196 self.collect_edges(filled)?;
197 self.slices.clear();
198 self.active.clear();
199 self.events.clear();
200 self.events
201 .extend(self.edges.iter().flat_map(|edge| [edge.x0, edge.x1]));
202 self.events.sort_by(f32::total_cmp);
206 self.events.dedup();
207 self.edges.sort_by(|a, b| a.x0.total_cmp(&b.x0));
208 let mut next_edge = 0;
209 let mut previous = 0..0;
210 let mut event = 0;
211 let mut left = self.events.first().copied().unwrap_or_default();
212 while let Some(&next_event) = self.events.get(event + 1) {
213 while let Some(&edge) = self.edges.get(next_edge).filter(|edge| edge.x0 <= left) {
214 self.active.push(edge);
215 next_edge += 1;
216 }
217 self.active.retain(|edge| edge.x1 > left);
218 let cut = self.order_crossings(left, next_event);
219 let right = cut.unwrap_or(next_event);
220 let first = self.slices.len();
221 self.push_spans(left, right, fill_rule);
222 let (before, after) = self.slices.split_at_mut(first);
223 share_sides(&mut before[previous], after)?;
224 if self.slices.len() > MAX_SLICES {
225 return None;
226 }
227 previous = first..self.slices.len();
228 left = right;
229 if cut.is_none() {
230 event += 1;
231 }
232 }
233 Some(&self.slices)
234 }
235
236 fn slice_monotone(&mut self, points: &[Point]) -> ChainSlicing {
242 if points
243 .iter()
244 .any(|point| !(point.x.is_finite() && point.y.is_finite()))
245 {
246 return ChainSlicing::Unsliceable;
247 }
248 let Some((leftmost, rightmost)) = horizontal_extremes(points) else {
249 return ChainSlicing::NotMonotone;
250 };
251 if !chain_edges(points, leftmost, rightmost, 1, &mut self.edges)
252 || !chain_edges(points, leftmost, rightmost, -1, &mut self.active)
253 {
254 return ChainSlicing::NotMonotone;
255 }
256 if self.edges.len() + self.active.len() > MAX_EDGES {
257 return ChainSlicing::Unsliceable;
258 }
259 self.slices.clear();
260 let (mut first, mut second) = (0, 0);
261 let mut left = self.edges.first().map_or(0.0, |edge| edge.x0);
262 let mut previous_sliced = false;
263 while let (Some(&a), Some(&b)) = (self.edges.get(first), self.active.get(second)) {
264 let next = a.x1.min(b.x1);
265 let (top, bottom, cut) = ordered_pair(a, b, left, next);
266 let right = cut.unwrap_or(next);
267 let sliced = top.start != bottom.start || top.end != bottom.end;
268 if sliced {
269 if previous_sliced && let Some(previous) = self.slices.last_mut() {
270 previous.open_right = false;
271 }
272 self.slices.push(Trapezoid {
273 left,
274 right,
275 top: [top.start, top.end],
276 bottom: [bottom.start, bottom.end],
277 open_left: !previous_sliced,
278 open_right: true,
279 });
280 if self.slices.len() > MAX_SLICES {
281 return ChainSlicing::Unsliceable;
282 }
283 }
284 previous_sliced = sliced;
285 left = right;
286 if cut.is_none() {
287 first += usize::from(a.x1 <= left);
288 second += usize::from(b.x1 <= left);
289 }
290 }
291 ChainSlicing::Sliced
292 }
293
294 fn collect_edges<'a>(&mut self, contours: impl IntoIterator<Item = &'a [Point]>) -> Option<()> {
295 self.edges.clear();
296 for points in contours {
297 let closing = points.iter().skip(1).chain(points.first());
298 for (a, b) in points.iter().zip(closing) {
299 if !(a.x.is_finite() && a.y.is_finite() && b.x.is_finite() && b.y.is_finite()) {
300 return None;
301 }
302 let edge = |from: &Point, to: &Point, winding| Edge {
303 x0: from.x,
304 y0: from.y,
305 x1: to.x,
306 y1: to.y,
307 winding,
308 };
309 if a.x < b.x {
310 self.edges.push(edge(a, b, 1));
311 } else if a.x > b.x {
312 self.edges.push(edge(b, a, -1));
313 }
314 }
315 if self.edges.len() > MAX_EDGES {
316 return None;
317 }
318 }
319 Some(())
320 }
321
322 fn order_crossings(&mut self, left: f32, right: f32) -> Option<f32> {
326 self.crossings.clear();
327 self.crossings
328 .extend(self.active.iter().map(|&edge| Crossing {
329 edge,
330 start: edge.y_at(left),
331 end: edge.y_at(right),
332 }));
333 insertion_sort(&mut self.crossings, |upper, lower| {
338 upper
339 .start
340 .total_cmp(&lower.start)
341 .then_with(|| upper.end.total_cmp(&lower.end))
342 .is_gt()
343 });
344 insertion_sort(&mut self.crossings, |upper, lower| {
345 (lower.start - upper.start).abs() <= TOLERANCE && upper.end > lower.end
346 });
347 self.active.clear();
348 self.active
349 .extend(self.crossings.iter().map(|crossing| crossing.edge));
350 let cut = self
351 .crossings
352 .windows(2)
353 .filter_map(|pair| {
354 let gap_start = pair[1].start - pair[0].start;
355 let overlap_end = pair[0].end - pair[1].end;
356 (gap_start > TOLERANCE && overlap_end > TOLERANCE)
357 .then(|| left + (right - left) * gap_start / (gap_start + overlap_end))
358 })
359 .filter(|&x| x > left && x < right)
360 .min_by(f32::total_cmp)?;
361 for crossing in &mut self.crossings {
362 crossing.end = crossing.edge.y_at(cut);
363 }
364 Some(cut)
365 }
366
367 fn push_spans(&mut self, left: f32, right: f32, fill_rule: PathFillRule) {
371 let mut winding = 0;
372 let mut top = None;
373 for crossing in &self.crossings {
374 let was_inside = fill_rule.contains(winding);
375 winding += crossing.edge.winding;
376 match (was_inside, fill_rule.contains(winding)) {
377 (false, true) => top = Some(crossing),
378 (true, false) => {
379 let Some(top) = top.take() else {
380 continue;
381 };
382 if top.start == crossing.start && top.end == crossing.end {
383 continue;
384 }
385 self.slices.push(Trapezoid {
386 left,
387 right,
388 top: [top.start, top.end],
389 bottom: [crossing.start, crossing.end],
390 open_left: true,
391 open_right: true,
392 });
393 }
394 _ => {}
395 }
396 }
397 }
398}
399
400enum ChainSlicing {
403 Sliced,
404 Unsliceable,
406 NotMonotone,
407}
408
409fn horizontal_extremes(points: &[Point]) -> Option<(usize, usize)> {
412 let mut leftmost = 0;
413 let mut rightmost = 0;
414 for (index, point) in points.iter().enumerate() {
415 if point.x < points[leftmost].x {
416 leftmost = index;
417 }
418 if point.x > points[rightmost].x {
419 rightmost = index;
420 }
421 }
422 (leftmost != rightmost).then_some((leftmost, rightmost))
423}
424
425fn chain_edges(
430 points: &[Point],
431 from: usize,
432 to: usize,
433 step: isize,
434 edges: &mut Vec<Edge>,
435) -> bool {
436 edges.clear();
437 let count = points.len();
438 let mut index = from;
439 let mut upright_after_edge = false;
440 while index != to {
441 let next = index.wrapping_add_signed(step).wrapping_add(count) % count;
442 let (a, b) = (points[index], points[next]);
443 if a.x > b.x {
444 return false;
445 }
446 if a.x < b.x {
447 if upright_after_edge {
448 return false;
449 }
450 let winding = if step > 0 { 1 } else { -1 };
451 edges.push(Edge {
452 x0: a.x,
453 y0: a.y,
454 x1: b.x,
455 y1: b.y,
456 winding,
457 });
458 } else if a.y != b.y && !edges.is_empty() {
459 upright_after_edge = true;
460 }
461 index = next;
462 }
463 true
464}
465
466fn ordered_pair(a: Edge, b: Edge, left: f32, right: f32) -> (Crossing, Crossing, Option<f32>) {
470 let crossing = |edge: Edge| Crossing {
471 edge,
472 start: edge.y_at(left),
473 end: edge.y_at(right),
474 };
475 let (mut upper, mut lower) = (crossing(a), crossing(b));
476 if upper
477 .start
478 .total_cmp(&lower.start)
479 .then_with(|| upper.end.total_cmp(&lower.end))
480 .is_gt()
481 {
482 std::mem::swap(&mut upper, &mut lower);
483 }
484 if (lower.start - upper.start).abs() <= TOLERANCE && upper.end > lower.end {
485 std::mem::swap(&mut upper, &mut lower);
486 }
487 let gap_start = lower.start - upper.start;
488 let overlap_end = upper.end - lower.end;
489 let cut = (gap_start > TOLERANCE && overlap_end > TOLERANCE)
490 .then(|| left + (right - left) * gap_start / (gap_start + overlap_end))
491 .filter(|&x| x > left && x < right);
492 if let Some(cut) = cut {
493 upper.end = upper.edge.y_at(cut);
494 lower.end = lower.edge.y_at(cut);
495 }
496 (upper, lower, cut)
497}
498
499fn insertion_sort<T: Copy>(items: &mut [T], misplaced: impl Fn(&T, &T) -> bool) {
502 for index in 1..items.len() {
503 let mut at = index;
504 while at > 0 && misplaced(&items[at - 1], &items[at]) {
505 items.swap(at - 1, at);
506 at -= 1;
507 }
508 }
509}
510
511fn share_sides(before: &mut [Trapezoid], after: &mut [Trapezoid]) -> Option<()> {
514 for slice in before.iter_mut() {
515 let side = [slice.top[1], slice.bottom[1]];
516 slice.open_right =
517 !side_shared(side, after.iter().map(|next| [next.top[0], next.bottom[0]]))?;
518 }
519 for slice in after.iter_mut() {
520 let side = [slice.top[0], slice.bottom[0]];
521 slice.open_left = !side_shared(
522 side,
523 before
524 .iter()
525 .map(|previous| [previous.top[1], previous.bottom[1]]),
526 )?;
527 }
528 Some(())
529}
530
531fn side_shared(side: [f32; 2], others: impl Iterator<Item = [f32; 2]>) -> Option<bool> {
535 let mut runs = others.peekable();
536 while let Some([start, mut end]) = runs.next() {
537 while let Some(&[next_start, next_end]) = runs.peek() {
538 if next_start > end + TOLERANCE {
539 break;
540 }
541 end = end.max(next_end);
542 runs.next();
543 }
544 if side[0] >= start - TOLERANCE && side[1] <= end + TOLERANCE {
545 return Some(true);
546 }
547 if side[1].min(end) - side[0].max(start) > TOLERANCE {
548 return None;
549 }
550 }
551 Some(false)
552}
553
554impl PathFillRule {
555 pub(crate) fn contains(self, winding: i32) -> bool {
557 match self {
558 Self::NonZero => winding != 0,
559 Self::EvenOdd => winding % 2 != 0,
560 }
561 }
562}