Skip to main content
← PSoXide SDK guides and examples · SDK f2fec1c301f1 · All Cargo features enabled; check feature gates before use.

psx_math/
attributed_clip.rs

1// SPDX-License-Identifier: GPL-2.0-or-later
2//! Allocation-free convex clipping shared by the PS1 renderers.
3//!
4//! This module owns Sutherland-Hodgman traversal and buffer order. Each
5//! renderer supplies a monomorphized plane adapter for its distance domain,
6//! fixed-point interpolation and attribute rounding. The adapters are an A/B
7//! bridge: once baseline equivalence is proven, all three games can be moved
8//! deliberately to one measured numeric policy.
9
10use crate::int32::div_u64_by_u32;
11use core::mem::MaybeUninit;
12
13/// Cyclic traversal order for one clip plane.
14///
15/// The two variants retain the same convex region but choose a different
16/// first output vertex. That affects triangle-fan diagonals and is therefore
17/// preserved explicitly during equivalence work.
18#[derive(Copy, Clone, Debug, Eq, PartialEq)]
19pub enum ClipTraversal {
20    /// Visit previous-to-current, writing a crossing before the current point.
21    PreviousToCurrent,
22    /// Visit current-to-next, writing the current point before a crossing.
23    CurrentToNext,
24}
25
26/// Q12 fraction for a crossing whose signed endpoint distances straddle zero.
27///
28/// Callers canonicalise the geometric endpoint order before calling this
29/// helper. That makes two faces which visit a shared edge in opposite
30/// directions produce the same fixed-point fraction and therefore the same
31/// clipped vertex. The division deliberately truncates: it is the cheapest
32/// R3000 policy and matches HL-PSX's crack-free reference implementation.
33#[inline(always)]
34pub fn crossing_fraction_q12_i32(first_distance: i32, second_distance: i32) -> i32 {
35    ratio_q12_i32(first_distance, first_distance.wrapping_sub(second_distance))
36}
37
38/// Exact Q16 crossing fraction of an edge from its two signed plane distances.
39///
40/// `(first << 16) / (first - second)`, clamped to `0..=65536`, computed with
41/// 32-bit operations only: the R3000A has no 64-bit divide, and the earlier
42/// `i64` form of this function was the last 64-bit division helper linked
43/// into the PXBSP world path. The magnitude runs through the hardware divide
44/// when `first << 16` fits, and through [`div_u64_by_u32`] otherwise; both
45/// are exact, so the result equals the wide form bit for bit.
46#[inline(always)]
47pub fn crossing_fraction_q16_i32(first_distance: i32, second_distance: i32) -> u32 {
48    // Sign of the ratio: positive only when the numerator and the difference
49    // agree, which is every straddling edge; equal-sign inputs clamp to an end.
50    let difference_positive = first_distance > second_distance;
51    if first_distance == second_distance {
52        return 1 << 16;
53    }
54    if (first_distance >= 0) != difference_positive {
55        return 0;
56    }
57    let numerator = first_distance.unsigned_abs();
58    let denominator = first_distance.abs_diff(second_distance);
59    let magnitude = if numerator < (1 << 16) {
60        (numerator << 16) / denominator
61    } else {
62        // `numerator >> 16 < denominator` because `numerator <= denominator`
63        // fails only when the ratio exceeds one, and `div_u64_by_u32` still
64        // needs `hi < divisor`; guard that case by the clamp below.
65        if (numerator >> 16) >= denominator {
66            return 1 << 16;
67        }
68        div_u64_by_u32(numerator >> 16, numerator << 16, denominator)
69    };
70    magnitude.min(1 << 16)
71}
72
73/// Exact `first + (second - first) * fraction / 65536`, truncating toward
74/// negative infinity like a shift, in 32-bit operations.
75///
76/// The product is split so nothing exceeds `i32` for `|second - first|` below
77/// `2^23`: enough for `i16` positions and screen coordinates, byte attributes
78/// and Q12 depths, which is everything a clipped world vertex carries.
79#[inline(always)]
80pub fn lerp_q16_i32_exact(first: i32, second: i32, fraction_q16: u32) -> i32 {
81    let delta = second.wrapping_sub(first);
82    debug_assert!(delta.unsigned_abs() < (1 << 23));
83    let high = (fraction_q16 >> 8) as i32;
84    let low = (fraction_q16 & 0xff) as i32;
85    // delta * f / 2^16 == delta * (f >> 8) / 2^8 + delta * (f & 255) / 2^16,
86    // and floor distributes here because the first term is exact after one
87    // more shift: (delta * high + ((delta * low) >> 8)) >> 8.
88    first.wrapping_add(
89        (delta
90            .wrapping_mul(high)
91            .wrapping_add((delta.wrapping_mul(low)) >> 8))
92            >> 8,
93    )
94}
95
96/// Bounded Q12 interpolation with truncation toward the lower fixed point.
97///
98/// This is the cheap path for Quake/PXBSP `i16` positions and byte attributes:
99/// their maximum delta times 4096 fits in `i32`. Renderers with wider source
100/// coordinates must use [`lerp_q12_i32_wide`].
101#[inline(always)]
102pub fn lerp_q12_i32(first: i32, second: i32, fraction: i32) -> i32 {
103    let delta = second.wrapping_sub(first);
104    first.wrapping_add(delta.wrapping_mul(fraction) >> 12)
105}
106
107/// Bounded Q12 interpolation rounded to nearest, matching Quake-PSX's
108/// cached-depth near clip.
109#[inline(always)]
110pub fn lerp_q12_i32_rounded(first: i32, second: i32, fraction: i32) -> i32 {
111    first.wrapping_add(
112        (second
113            .wrapping_sub(first)
114            .wrapping_mul(fraction)
115            .wrapping_add(1 << 11))
116            >> 12,
117    )
118}
119
120/// Overflow-safe form of [`lerp_q12_i32`] for large screen-space coordinates.
121#[inline(always)]
122pub fn lerp_q12_i32_wide(first: i32, second: i32, fraction: i32) -> i32 {
123    let delta = second.wrapping_sub(first);
124    first
125        .wrapping_add((delta / 4096).wrapping_mul(fraction))
126        .wrapping_add(((delta % 4096).wrapping_mul(fraction)) >> 12)
127}
128
129/// Overflow-safe non-negative Q12 ratio, clamped to one.
130///
131/// HL-PSX also uses this for projection and guard-band interpolation outside
132/// polygon traversal, preventing a second overflow fix under a game-specific
133/// name.
134#[inline(always)]
135pub fn ratio_q12_i32(numerator: i32, denominator: i32) -> i32 {
136    if denominator == 0 || (numerator < 0) != (denominator < 0) {
137        return 0;
138    }
139    let mut numerator = numerator.unsigned_abs();
140    let mut denominator = denominator.unsigned_abs();
141    while numerator > (i32::MAX as u32 >> 12) {
142        numerator = (numerator + 1) >> 1;
143        denominator = (denominator + 1) >> 1;
144    }
145    if denominator == 0 {
146        return 0;
147    }
148    ((numerator << 12) / denominator).min(4096) as i32
149}
150
151/// Renderer-specific policy for one attributed clip plane.
152///
153/// Implement this on a small adapter type. Generic dispatch is resolved at
154/// compile time; no trait object or indirect call enters the console hot path.
155/// `source_index` allows callers to reuse cached transformed distances.
156pub trait AttributedClipPlane<Vertex> {
157    /// Signed-distance representation used by the renderer.
158    type Distance: Copy;
159
160    /// Signed distance of `vertex` from the retained half-space.
161    fn distance(&self, source_index: usize, vertex: &Vertex) -> Self::Distance;
162
163    /// Whether `distance` lies in the retained half-space.
164    fn inside(&self, distance: Self::Distance) -> bool;
165
166    /// Interpolate every authored attribute at an edge/plane crossing.
167    fn intersection(
168        &self,
169        first_index: usize,
170        first: &Vertex,
171        first_distance: Self::Distance,
172        second_index: usize,
173        second: &Vertex,
174        second_distance: Self::Distance,
175    ) -> Vertex;
176}
177
178/// `destination` filled before the clip finished. The vertices written so far
179/// are not a usable polygon.
180#[derive(Clone, Copy, Debug, PartialEq, Eq)]
181pub struct ClipOverflow;
182
183impl core::fmt::Display for ClipOverflow {
184    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
185        f.write_str("clip output does not fit the destination")
186    }
187}
188
189/// Clip one attributed polygon against one half-space into `destination`,
190/// returning how many vertices it wrote.
191///
192/// Every write is bounds-checked. A convex `source` clipped by a plane whose
193/// `inside` answer is the same each time a vertex is evaluated gains at most
194/// one vertex, so `source.len() + 1` slots always suffice for it; any input
195/// fits `2 * source.len()`, the most one vertex and one crossing per source
196/// vertex can emit.
197///
198/// # Errors
199///
200/// [`ClipOverflow`] when `destination` is too small.
201#[inline(always)]
202pub fn clip_to_plane<Vertex: Copy, Plane: AttributedClipPlane<Vertex>>(
203    source: &[Vertex],
204    destination: &mut [Vertex],
205    plane: &Plane,
206    traversal: ClipTraversal,
207) -> Result<usize, ClipOverflow> {
208    // SAFETY: `destination` is a live exclusive slice, so its pointer is
209    // valid for writes of `destination.len()` elements and cannot alias
210    // `source`; with `CHECK_CAPACITY` the kernel bounds every write by that
211    // length.
212    unsafe {
213        clip_convex_plane_raw::<_, _, true>(
214            source,
215            destination.as_mut_ptr(),
216            destination.len(),
217            plane,
218            traversal,
219        )
220    }
221    .map_err(|_| ClipOverflow)
222}
223
224/// [`clip_to_plane`] without the bounds check on each write, for fixed
225/// scratch whose size is proven.
226///
227/// # Safety
228///
229/// `destination.len()` must be at least `2 * source.len()`; or at least
230/// `source.len() + 1` when `source` is convex and `plane.inside` gives the
231/// same answer every time a vertex's distance is evaluated (the
232/// `CurrentToNext` traversal evaluates each distance twice).
233#[inline(always)]
234pub unsafe fn clip_to_plane_unchecked<Vertex: Copy, Plane: AttributedClipPlane<Vertex>>(
235    source: &[Vertex],
236    destination: &mut [Vertex],
237    plane: &Plane,
238    traversal: ClipTraversal,
239) -> usize {
240    // SAFETY: `destination` is a live exclusive slice, valid for writes of
241    // its length and not aliasing `source`; this fn's `# Safety` bounds the
242    // emitted count by that length. Without `CHECK_CAPACITY` the kernel never
243    // returns `Err`.
244    match unsafe {
245        clip_convex_plane_raw::<_, _, false>(
246            source,
247            destination.as_mut_ptr(),
248            destination.len(),
249            plane,
250            traversal,
251        )
252    } {
253        Ok(written) | Err(written) => written,
254    }
255}
256
257/// Clip one convex attributed polygon against one half-space.
258///
259/// With `CHECK_CAPACITY` true this is [`clip_to_plane`] returning the count
260/// written before an overflow; with it false, [`clip_to_plane_unchecked`].
261///
262/// # Safety
263///
264/// When `CHECK_CAPACITY` is false, [`clip_to_plane_unchecked`]'s contract.
265/// `source.len() + 1` slots are enough only for a convex source and a plane
266/// whose `inside` answer is stable per vertex; `2 * source.len()` always is.
267#[deprecated(
268    note = "use the safe `clip_to_plane` (checked) or `clip_to_plane_unchecked` (proven capacity)"
269)]
270#[inline(always)]
271pub unsafe fn clip_convex_plane<
272    Vertex: Copy,
273    Plane: AttributedClipPlane<Vertex>,
274    const CHECK_CAPACITY: bool,
275>(
276    source: &[Vertex],
277    destination: &mut [Vertex],
278    plane: &Plane,
279    traversal: ClipTraversal,
280) -> usize {
281    // SAFETY: `destination` is a live exclusive slice, so its pointer is
282    // valid for writes of `destination.len()` elements and cannot alias
283    // `source`. The capacity requirement when `CHECK_CAPACITY` is false is
284    // forwarded unchanged from this fn's `# Safety` section.
285    match unsafe {
286        clip_convex_plane_raw::<_, _, CHECK_CAPACITY>(
287            source,
288            destination.as_mut_ptr(),
289            destination.len(),
290            plane,
291            traversal,
292        )
293    } {
294        Ok(written) | Err(written) => written,
295    }
296}
297
298/// [`clip_to_plane`] into uninitialised scratch, so console renderers can
299/// keep stack or scratchpad scratch as `MaybeUninit` and pay only for the
300/// vertices clipping emits. On `Ok(n)` the first `n` entries are initialised.
301///
302/// # Errors
303///
304/// [`ClipOverflow`] when `destination` is too small.
305#[inline(always)]
306pub fn clip_to_plane_uninit<Vertex: Copy, Plane: AttributedClipPlane<Vertex>>(
307    source: &[Vertex],
308    destination: &mut [MaybeUninit<Vertex>],
309    plane: &Plane,
310    traversal: ClipTraversal,
311) -> Result<usize, ClipOverflow> {
312    // SAFETY: `MaybeUninit<Vertex>` has `Vertex`'s layout, so the cast
313    // pointer is aligned and valid for writes of `destination.len()`
314    // vertices; the kernel only writes destination slots, bounds-checked
315    // with `CHECK_CAPACITY`, and the exclusive borrow rules out aliasing.
316    unsafe {
317        clip_convex_plane_raw::<_, _, true>(
318            source,
319            destination.as_mut_ptr().cast::<Vertex>(),
320            destination.len(),
321            plane,
322            traversal,
323        )
324    }
325    .map_err(|_| ClipOverflow)
326}
327
328/// [`clip_to_plane_uninit`] without the bounds check on each write. The
329/// first returned-count entries of `destination` are initialised.
330///
331/// # Safety
332///
333/// [`clip_to_plane_unchecked`]'s capacity contract.
334#[inline(always)]
335pub unsafe fn clip_to_plane_uninit_unchecked<Vertex: Copy, Plane: AttributedClipPlane<Vertex>>(
336    source: &[Vertex],
337    destination: &mut [MaybeUninit<Vertex>],
338    plane: &Plane,
339    traversal: ClipTraversal,
340) -> usize {
341    // SAFETY: as in `clip_to_plane_uninit`, with the capacity bound from this
342    // fn's `# Safety`. Without `CHECK_CAPACITY` the kernel never returns `Err`.
343    match unsafe {
344        clip_convex_plane_raw::<_, _, false>(
345            source,
346            destination.as_mut_ptr().cast::<Vertex>(),
347            destination.len(),
348            plane,
349            traversal,
350        )
351    } {
352        Ok(written) | Err(written) => written,
353    }
354}
355
356/// [`clip_convex_plane`] into uninitialised scratch. The first
357/// returned-count entries of `destination` are initialised on return.
358///
359/// # Safety
360///
361/// [`clip_convex_plane`]'s contract.
362#[deprecated(
363    note = "use the safe `clip_to_plane_uninit` (checked) or `clip_to_plane_uninit_unchecked` (proven capacity)"
364)]
365#[inline(always)]
366pub unsafe fn clip_convex_plane_uninit<
367    Vertex: Copy,
368    Plane: AttributedClipPlane<Vertex>,
369    const CHECK_CAPACITY: bool,
370>(
371    source: &[Vertex],
372    destination: &mut [MaybeUninit<Vertex>],
373    plane: &Plane,
374    traversal: ClipTraversal,
375) -> usize {
376    // SAFETY: `MaybeUninit<Vertex>` has the same layout as `Vertex`, so the
377    // cast pointer is aligned and valid for writes of `destination.len()`
378    // vertices; the raw kernel only writes (never reads) destination slots.
379    // The exclusive borrow rules out aliasing `source`, and the capacity
380    // requirement is forwarded from this fn's `# Safety` section.
381    match unsafe {
382        clip_convex_plane_raw::<_, _, CHECK_CAPACITY>(
383            source,
384            destination.as_mut_ptr().cast::<Vertex>(),
385            destination.len(),
386            plane,
387            traversal,
388        )
389    } {
390        Ok(written) | Err(written) => written,
391    }
392}
393
394/// The Sutherland-Hodgman kernel. `Err(written)` when `CHECK_CAPACITY` found
395/// `destination` full; without it every write is the caller's to bound and
396/// the result is always `Ok`.
397#[inline(always)]
398unsafe fn clip_convex_plane_raw<
399    Vertex: Copy,
400    Plane: AttributedClipPlane<Vertex>,
401    const CHECK_CAPACITY: bool,
402>(
403    source: &[Vertex],
404    destination: *mut Vertex,
405    destination_len: usize,
406    plane: &Plane,
407    traversal: ClipTraversal,
408) -> Result<usize, usize> {
409    if source.is_empty() {
410        return Ok(0);
411    }
412    if !CHECK_CAPACITY {
413        debug_assert!(destination_len >= source.len().saturating_add(1));
414    }
415
416    #[inline(always)]
417    unsafe fn emit<Vertex: Copy, const CHECK_CAPACITY: bool>(
418        destination: *mut Vertex,
419        destination_len: usize,
420        written: &mut usize,
421        vertex: Vertex,
422    ) -> bool {
423        if CHECK_CAPACITY && *written >= destination_len {
424            return false;
425        }
426        debug_assert!(*written < destination_len);
427        // SAFETY: `*written < destination_len`, checked above when
428        // `CHECK_CAPACITY` is set and otherwise guaranteed by the caller of
429        // the public entry points; `destination` is valid for writes of
430        // `destination_len` elements, so slot `*written` is in bounds.
431        unsafe { destination.add(*written).write(vertex) };
432        *written += 1;
433        true
434    }
435
436    let mut written = 0usize;
437    match traversal {
438        ClipTraversal::PreviousToCurrent => {
439            let mut previous_index = source.len() - 1;
440            // SAFETY: `source` is non-empty (early return above), so
441            // `len - 1` is a valid index.
442            let mut previous = unsafe { *source.get_unchecked(previous_index) };
443            let mut previous_distance = plane.distance(previous_index, &previous);
444            let mut current_index = 0usize;
445            while current_index < source.len() {
446                // SAFETY: the loop condition keeps `current_index < len`.
447                let current = unsafe { *source.get_unchecked(current_index) };
448                let current_distance = plane.distance(current_index, &current);
449                let current_inside = plane.inside(current_distance);
450                if current_inside != plane.inside(previous_distance) {
451                    let crossing = plane.intersection(
452                        previous_index,
453                        &previous,
454                        previous_distance,
455                        current_index,
456                        &current,
457                        current_distance,
458                    );
459                    // SAFETY: `destination`/`destination_len` come straight from the
460                    // caller, who guarantees room for every emitted vertex when
461                    // `CHECK_CAPACITY` is false; with it set, `emit` bounds-checks.
462                    if unsafe {
463                        !emit::<Vertex, CHECK_CAPACITY>(
464                            destination,
465                            destination_len,
466                            &mut written,
467                            crossing,
468                        )
469                    } {
470                        return Err(written);
471                    }
472                }
473                if current_inside
474                    // SAFETY: same capacity contract as the crossing emit above:
475                    // the caller sized `destination`, or `emit` bounds-checks.
476                    && unsafe {
477                        !emit::<Vertex, CHECK_CAPACITY>(
478                            destination,
479                            destination_len,
480                            &mut written,
481                            current,
482                        )
483                    }
484                {
485                    return Err(written);
486                }
487                previous_index = current_index;
488                previous = current;
489                previous_distance = current_distance;
490                current_index += 1;
491            }
492        }
493        ClipTraversal::CurrentToNext => {
494            let mut current_index = 0usize;
495            while current_index < source.len() {
496                let next_index = if current_index + 1 == source.len() {
497                    0
498                } else {
499                    current_index + 1
500                };
501                // SAFETY: the loop condition keeps `current_index < len`.
502                let current = unsafe { *source.get_unchecked(current_index) };
503                // SAFETY: `next_index` is `current_index + 1` only when that is
504                // still `< len`, otherwise 0, which is valid for a non-empty slice.
505                let next = unsafe { *source.get_unchecked(next_index) };
506                let current_distance = plane.distance(current_index, &current);
507                let next_distance = plane.distance(next_index, &next);
508                let current_inside = plane.inside(current_distance);
509                if current_inside
510                    // SAFETY: same capacity contract as the crossing emit above:
511                    // the caller sized `destination`, or `emit` bounds-checks.
512                    && unsafe {
513                        !emit::<Vertex, CHECK_CAPACITY>(
514                            destination,
515                            destination_len,
516                            &mut written,
517                            current,
518                        )
519                    }
520                {
521                    return Err(written);
522                }
523                if current_inside != plane.inside(next_distance) {
524                    let crossing = plane.intersection(
525                        current_index,
526                        &current,
527                        current_distance,
528                        next_index,
529                        &next,
530                        next_distance,
531                    );
532                    // SAFETY: `destination`/`destination_len` come straight from the
533                    // caller, who guarantees room for every emitted vertex when
534                    // `CHECK_CAPACITY` is false; with it set, `emit` bounds-checks.
535                    if unsafe {
536                        !emit::<Vertex, CHECK_CAPACITY>(
537                            destination,
538                            destination_len,
539                            &mut written,
540                            crossing,
541                        )
542                    } {
543                        return Err(written);
544                    }
545                }
546                current_index += 1;
547            }
548        }
549    }
550    Ok(written)
551}
552
553#[cfg(test)]
554mod tests {
555    use super::*;
556
557    #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
558    struct Vertex(i32, i32);
559
560    struct Plane<'a>(Option<&'a [i32]>);
561
562    impl AttributedClipPlane<Vertex> for Plane<'_> {
563        type Distance = i32;
564
565        fn distance(&self, index: usize, vertex: &Vertex) -> i32 {
566            self.0.map_or(vertex.0, |distances| distances[index])
567        }
568
569        fn inside(&self, distance: i32) -> bool {
570            distance >= 0
571        }
572
573        fn intersection(
574            &self,
575            _: usize,
576            first: &Vertex,
577            first_distance: i32,
578            _: usize,
579            second: &Vertex,
580            second_distance: i32,
581        ) -> Vertex {
582            Vertex(
583                0,
584                first.1
585                    + (second.1 - first.1) * first_distance / (first_distance - second_distance),
586            )
587        }
588    }
589
590    fn clip<const CHECK: bool>(
591        source: &[Vertex],
592        traversal: ClipTraversal,
593    ) -> ([Vertex; 8], usize) {
594        let mut output = [Vertex::default(); 8];
595        assert!(2 * source.len() <= output.len());
596        let count = if CHECK {
597            clip_to_plane(source, &mut output, &Plane(None), traversal).expect("fits")
598        } else {
599            // SAFETY: one plane emits at most two vertices per source vertex,
600            // and the assert above leaves room for that.
601            unsafe { clip_to_plane_unchecked(source, &mut output, &Plane(None), traversal) }
602        };
603        (output, count)
604    }
605
606    #[test]
607    fn traversal_preserves_each_historical_fan_start() {
608        let source = [Vertex(-2, 0), Vertex(2, 40), Vertex(2, 80)];
609        let (previous, previous_count) = clip::<true>(&source, ClipTraversal::PreviousToCurrent);
610        assert_eq!(previous_count, 4);
611        assert_eq!(
612            &previous[..previous_count],
613            &[Vertex(0, 40), Vertex(0, 20), source[1], source[2]]
614        );
615        let (current, current_count) = clip::<false>(&source, ClipTraversal::CurrentToNext);
616        assert_eq!(current_count, 4);
617        assert_eq!(
618            &current[..current_count],
619            &[Vertex(0, 20), source[1], source[2], Vertex(0, 40)]
620        );
621    }
622
623    #[test]
624    fn cached_distance_domain_is_authoritative() {
625        let source = [Vertex(100, 0), Vertex(100, 40), Vertex(100, 80)];
626        let mut output = [Vertex::default(); 4];
627        let count = clip_to_plane(
628            &source,
629            &mut output,
630            &Plane(Some(&[-2, 2, 2])),
631            ClipTraversal::CurrentToNext,
632        );
633        assert_eq!(count, Ok(4));
634        assert_eq!(output, [Vertex(0, 20), source[1], source[2], Vertex(0, 40)]);
635    }
636
637    /// math-01: a non-convex source crossing the plane at every edge emits
638    /// two vertices per source vertex, more than the `len + 1` the old docs
639    /// called sufficient. The safe form reports it; `2 * len` always fits.
640    #[test]
641    fn non_convex_input_overflows_len_plus_one_but_fits_twice_len() {
642        let zigzag = [Vertex(1, 0), Vertex(-1, 10), Vertex(1, 20), Vertex(-1, 30)];
643        for traversal in [
644            ClipTraversal::PreviousToCurrent,
645            ClipTraversal::CurrentToNext,
646        ] {
647            let mut tight = [Vertex::default(); 5];
648            assert_eq!(
649                clip_to_plane(&zigzag, &mut tight, &Plane(None), traversal),
650                Err(ClipOverflow)
651            );
652            let mut roomy = [Vertex::default(); 8];
653            assert_eq!(
654                clip_to_plane(&zigzag, &mut roomy, &Plane(None), traversal),
655                Ok(6)
656            );
657            let mut uninit = [MaybeUninit::<Vertex>::uninit(); 5];
658            assert_eq!(
659                clip_to_plane_uninit(&zigzag, &mut uninit, &Plane(None), traversal),
660                Err(ClipOverflow)
661            );
662        }
663    }
664
665    /// The deprecated forwarders keep their old results: the checked form
666    /// returns the count written before the overflow.
667    #[allow(deprecated)]
668    #[test]
669    fn deprecated_forwarders_keep_partial_counts() {
670        let zigzag = [Vertex(1, 0), Vertex(-1, 10), Vertex(1, 20), Vertex(-1, 30)];
671        let mut tight = [Vertex::default(); 5];
672        // SAFETY: `CHECK_CAPACITY` is true, so every write is bounds-checked.
673        let count = unsafe {
674            clip_convex_plane::<_, _, true>(
675                &zigzag,
676                &mut tight,
677                &Plane(None),
678                ClipTraversal::PreviousToCurrent,
679            )
680        };
681        assert_eq!(count, 5);
682    }
683
684    #[test]
685    fn canonical_q12_crossing_is_direction_independent() {
686        let interpolate =
687            |mut first: i32, mut second: i32, mut first_distance: i32, mut second_distance: i32| {
688                if second < first {
689                    core::mem::swap(&mut first, &mut second);
690                    core::mem::swap(&mut first_distance, &mut second_distance);
691                }
692                lerp_q12_i32(
693                    first,
694                    second,
695                    crossing_fraction_q12_i32(first_distance, second_distance),
696                )
697            };
698        assert_eq!(interpolate(10, 110, 7, -3), 79);
699        assert_eq!(interpolate(110, 10, -3, 7), 79);
700    }
701
702    #[test]
703    fn measured_numeric_policies_keep_their_rounding_contracts() {
704        let q12 = crossing_fraction_q12_i32(7, -3);
705        assert_eq!(lerp_q12_i32(10, 110, q12), 79);
706        assert_eq!(lerp_q12_i32_rounded(10, 110, q12), 80);
707
708        let q16 = crossing_fraction_q16_i32(7, -3);
709        assert_eq!(lerp_q16_i32_exact(10, 110, q16), 79);
710    }
711
712    #[test]
713    fn all_inside_and_all_outside_are_stable() {
714        let inside = [Vertex(1, 1), Vertex(2, 2), Vertex(3, 3)];
715        let outside = [Vertex(-1, 1), Vertex(-2, 2), Vertex(-3, 3)];
716        let (inside_output, inside_count) = clip::<true>(&inside, ClipTraversal::PreviousToCurrent);
717        assert_eq!(inside_count, inside.len());
718        assert_eq!(&inside_output[..inside_count], &inside);
719        let (_, outside_count) = clip::<true>(&outside, ClipTraversal::PreviousToCurrent);
720        assert_eq!(outside_count, 0);
721    }
722
723    /// The `i64` forms these replaced, kept as the host oracles.
724    fn crossing_fraction_q16_i64(first: i64, second: i64) -> i64 {
725        ((first << 16) / first.wrapping_sub(second)).clamp(0, 1 << 16)
726    }
727
728    fn lerp_q16_i64(first: i32, second: i32, fraction: i64) -> i32 {
729        (first as i64 + ((second.wrapping_sub(first) as i64).wrapping_mul(fraction) >> 16)) as i32
730    }
731
732    #[test]
733    #[cfg_attr(
734        miri,
735        ignore = "exhaustive sweep, minutes under Miri; make test runs it natively"
736    )]
737    fn q16_crossing_and_lerp_match_the_wide_oracles() {
738        let mut state = 0x9e37_79b9_7f4a_7c15u64;
739        let mut next = || {
740            state ^= state << 13;
741            state ^= state >> 7;
742            state ^= state << 17;
743            state
744        };
745        for iteration in 0..100_000u32 {
746            let span: i32 = if iteration % 3 == 0 {
747                i32::MAX
748            } else {
749                1 << 24
750            };
751            let first = (next() as i32) % span;
752            let second = (next() as i32) % span;
753            if first == second {
754                continue;
755            }
756            let expected = crossing_fraction_q16_i64(i64::from(first), i64::from(second));
757            let actual = crossing_fraction_q16_i32(first, second);
758            assert_eq!(i64::from(actual), expected, "first {first} second {second}");
759            let a = (next() as i32) % (1 << 22);
760            let b = (next() as i32) % (1 << 22);
761            assert_eq!(
762                lerp_q16_i32_exact(a, b, actual),
763                lerp_q16_i64(a, b, expected),
764                "lerp {a} {b} fraction {actual}"
765            );
766        }
767    }
768}