Skip to main content

mlt_core/codecs/
morton.rs

1use geo_types::Coord;
2use wide::u32x8;
3
4use crate::decoder::Morton;
5use crate::encoder::model::CurveParams;
6use crate::{Decoder, MltError, MltResult};
7
8const LANES: usize = 8;
9
10// ── Bit interleaving ─────────────────────────────────────────────────────────
11
12/// Interleave the lower 16 bits of `x` and `y` into a 32-bit Morton code.
13///
14/// Even bit positions (0, 2, 4, …) encode `x`; odd positions (1, 3, 5, …)
15/// encode `y`. Spatially adjacent `(x, y)` pairs produce numerically
16/// adjacent codes, giving Z-order locality when used as a sort key.
17#[must_use]
18#[inline]
19pub fn interleave_bits(coord: Coord<u32>) -> u32 {
20    // Spread each input's lower 16 bits into every other bit position, then
21    // OR the two together: x occupies even positions (0, 2, 4, …) and y
22    // occupies odd positions (1, 3, 5, …).
23    let mut sx = coord.x & 0xFFFF;
24    sx = (sx | (sx << 8)) & 0x00FF_00FF;
25    sx = (sx | (sx << 4)) & 0x0F0F_0F0F;
26    sx = (sx | (sx << 2)) & 0x3333_3333;
27    sx = (sx | (sx << 1)) & 0x5555_5555;
28
29    let mut sy = coord.y & 0xFFFF;
30    sy = (sy | (sy << 8)) & 0x00FF_00FF;
31    sy = (sy | (sy << 4)) & 0x0F0F_0F0F;
32    sy = (sy | (sy << 2)) & 0x3333_3333;
33    sy = (sy | (sy << 1)) & 0x5555_5555;
34
35    sx | (sy << 1)
36}
37
38/// Compute a Z-order (Morton) sort key from signed integer coordinates.
39///
40/// `shift` is applied to both axes before bit-interleaving to move the
41/// coordinate origin into the non-negative range. It should be computed
42/// once across the entire feature set (typically `min.unsigned_abs()` when
43/// `min < 0`, else `0`) so that the keys are comparable across features.
44///
45/// Each shifted component is truncated to 16 bits before interleaving, so
46/// the returned key fits in a `u32` (32 interleaved bits). This is
47/// sufficient for any tile coordinate system with extent ≤ 65 535.
48#[must_use]
49#[inline]
50pub fn morton_sort_key(c: Coord<i32>, params: CurveParams) -> u32 {
51    debug_assert!(params.bits >= 1);
52    #[expect(
53        clippy::cast_possible_truncation,
54        clippy::cast_sign_loss,
55        reason = "shift brings value into [0, extent]; masked to 16 bits immediately after"
56    )]
57    let sx = ((i64::from(c.x) + i64::from(params.shift)) as u32) & 0xFFFF;
58    #[expect(
59        clippy::cast_possible_truncation,
60        clippy::cast_sign_loss,
61        reason = "shift brings value into [0, extent]; masked to 16 bits immediately after"
62    )]
63    let sy = ((i64::from(c.y) + i64::from(params.shift)) as u32) & 0xFFFF;
64    interleave_bits((sx, sy).into())
65}
66
67// ── Encoder ─────────────────────────────────────────────────────────────────
68impl Morton {
69    /// Compute `ZOrderCurve` parameters from the vertex value range.
70    ///
71    /// Returns a [`Morton`] whose `bits` and `shift` match Java's
72    /// `SpaceFillingCurve` implementation.
73    pub fn from_vertices(vertices: &[i32]) -> MltResult<Self> {
74        let min_v = vertices.iter().copied().min().unwrap_or(0);
75        let max_v = vertices.iter().copied().max().unwrap_or(0);
76        let shift: u32 = if min_v < 0 { min_v.unsigned_abs() } else { 0 };
77        let tile_extent = i64::from(max_v) + i64::from(shift);
78        let bits = if let Ok(extent) = u32::try_from(tile_extent) {
79            // ceil(log2(extent + 1)), matching Java's Math.ceil(Math.log(...) / Math.log(2)).
80            // Computed with integer arithmetic: for te >= 1, this equals `te.bit_width()`.
81            // Capped at 16: Morton codes are u32, so each axis may use at most 16 bits.
82            let required_bits = extent.bit_width();
83            if required_bits > 16 {
84                return Err(MltError::VertexMortonNotCompatibleWithExtent {
85                    extent,
86                    required_bits,
87                });
88            }
89            required_bits
90        } else {
91            0u32
92        };
93        Self::new(bits, shift)
94    }
95
96    /// Encode a single `(x, y)` coordinate pair to its Z-order (Morton) code.
97    ///
98    /// `bits` (≤ 16) bits are used per axis; `shift` is added to each
99    /// component before interleaving so that negative coordinates map to non-negative values.
100    #[inline]
101    pub fn encode_morton(self, x: i32, y: i32) -> MltResult<u32> {
102        let sx = u32::try_from(i64::from(x) + i64::from(self.shift))?;
103        let sy = u32::try_from(i64::from(y) + i64::from(self.shift))?;
104        let mut code = 0u32;
105        for i in 0..self.bits {
106            // bits are capped at 16, so 2*i+1 ≤ 31 - no shift overflow.
107            code |= ((sx >> i) & 1) << (2 * i);
108            code |= ((sy >> i) & 1) << (2 * i + 1);
109        }
110        Ok(code)
111    }
112}
113
114impl Morton {
115    /// Decode a single Morton code to a `Coord<i32>`, applying `shift`.
116    #[inline]
117    fn decode_one(self, morton_code: u32) -> Coord<i32> {
118        let mut x = 0u32;
119        let mut y = 0u32;
120        for i in 0..self.bits {
121            let bit_mask = 1u32 << (2 * i);
122            x |= (morton_code & bit_mask) >> i;
123            y |= ((morton_code >> 1) & bit_mask) >> i;
124        }
125        Coord::<i32> {
126            x: x.wrapping_sub(self.shift).cast_signed(),
127            y: y.wrapping_sub(self.shift).cast_signed(),
128        }
129    }
130
131    /// Decode Morton codes (no delta) to flat `[x0, y0, x1, y1, ...]`, charging `dec` for the output.
132    ///
133    /// Processes 8 codes at a time with `wide::u32x8`. Each lane extracts the
134    /// compacted even-bit (x) and odd-bit (y) components in parallel, then applies
135    /// the coordinate shift. A scalar tail handles any remaining codes.
136    pub fn decode_codes(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {
137        let alloc_size = data.len() * 2;
138        let mut out = dec.alloc(alloc_size)?;
139        let shift_vec = u32x8::splat(self.shift);
140
141        let (chunks, remainder) = data.as_chunks::<LANES>();
142
143        for &chunk in chunks {
144            self.decode_chunk(chunk, shift_vec, &mut out);
145        }
146
147        // Scalar tail for any codes that didn't fill a full SIMD chunk.
148        for &code in remainder {
149            let coord = self.decode_one(code);
150            out.push(coord.x);
151            out.push(coord.y);
152        }
153
154        dec.adjust_alloc(&out, alloc_size)?;
155        Ok(out)
156    }
157
158    /// Decode delta-encoded Morton codes to flat `[x0, y0, x1, y1, ...]`, charging `dec` for the output.
159    ///
160    /// Each input value is a signed delta (stored as u32 with wrapping arithmetic)
161    /// relative to the previous Morton code. The sequential prefix sum is computed
162    /// in chunks of 8 into a stack-allocated buffer, which is then SIMD-decoded.
163    /// This keeps the working set in registers / L1 cache.
164    pub fn decode_delta(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {
165        let alloc_size = data.len() * 2;
166        let mut out = dec.alloc(alloc_size)?;
167        let shift_vec = u32x8::splat(self.shift);
168
169        let mut prev = 0i32;
170        let (chunks, remainder) = data.as_chunks::<LANES>();
171
172        for chunk in chunks {
173            // Sequential prefix sum into a stack buffer - no heap allocation.
174            let mut buf = [0u32; LANES];
175            for (b, &d) in buf.iter_mut().zip(chunk.iter()) {
176                prev = prev.wrapping_add(d.cast_signed());
177                *b = prev.cast_unsigned();
178            }
179            self.decode_chunk(buf, shift_vec, &mut out);
180        }
181
182        // Scalar tail for any codes that didn't fill a full SIMD chunk.
183        for &d in remainder {
184            prev = prev.wrapping_add(d.cast_signed());
185            let coord = self.decode_one(prev.cast_unsigned());
186            out.push(coord.x);
187            out.push(coord.y);
188        }
189
190        dec.adjust_alloc(&out, alloc_size)?;
191        Ok(out)
192    }
193
194    /// SIMD-decode a chunk of exactly 8 resolved Morton codes into the output buffer.
195    ///
196    /// Each code has already been resolved to its absolute value (no delta pending).
197    /// Even-indexed bits encode x, odd-indexed bits encode y.
198    #[inline]
199    fn decode_chunk(self, buf: [u32; LANES], shift_vec: u32x8, out: &mut Vec<i32>) {
200        let codes = u32x8::from(buf);
201        // Odd bits become even after shifting right by 1, giving the y component.
202        let codes_y = codes >> 1;
203
204        let mut x_vec = u32x8::ZERO;
205        let mut y_vec = u32x8::ZERO;
206
207        for i in 0..self.bits {
208            // Mask for the bit position 2*i in the original Morton code.
209            let bit_mask = u32x8::splat(1u32 << (2 * i));
210            // Extract bit 2*i from each code and shift it down to position i.
211            x_vec |= (codes & bit_mask) >> i;
212            y_vec |= (codes_y & bit_mask) >> i;
213        }
214
215        let xs: [u32; LANES] = (x_vec - shift_vec).into();
216        let ys: [u32; LANES] = (y_vec - shift_vec).into();
217
218        for lane in 0..LANES {
219            out.push(xs[lane].cast_signed());
220            out.push(ys[lane].cast_signed());
221        }
222    }
223}
224
225#[cfg(test)]
226mod tests {
227
228    use super::*;
229    use crate::test_helpers::dec;
230
231    const fn c(x: i32, y: i32) -> Coord<i32> {
232        Coord::<i32> { x, y }
233    }
234
235    const fn p(shift: u32, bits: u32) -> CurveParams {
236        CurveParams { shift, bits }
237    }
238
239    // ── interleave_bits / morton_sort_key ─────────────────────────────────────
240
241    /// Spread the lower 16 bits of `tx` into the even bit positions (0, 2, 4, …)
242    /// of a 32-bit word, inserting a 0 between every original bit.
243    fn spread_bits(mut tx: u32) -> u32 {
244        tx = (tx | (tx << 8)) & 0x00FF_00FF;
245        tx = (tx | (tx << 4)) & 0x0F0F_0F0F;
246        tx = (tx | (tx << 2)) & 0x3333_3333;
247        tx = (tx | (tx << 1)) & 0x5555_5555;
248        tx
249    }
250
251    /// Compact the bits at even positions (0, 2, 4, …) of `tx` into the lower
252    /// 16 bits, discarding the interleaved zeros.
253    fn compact_bits(mut tx: u32) -> u32 {
254        tx &= 0x5555_5555;
255        tx = (tx | (tx >> 1)) & 0x3333_3333;
256        tx = (tx | (tx >> 2)) & 0x0F0F_0F0F;
257        tx = (tx | (tx >> 4)) & 0x00FF_00FF;
258        tx = (tx | (tx >> 8)) & 0x0000_FFFF;
259        tx
260    }
261
262    #[test]
263    fn spread_then_compact_is_identity() {
264        for x in 0u32..=0xFFFF {
265            assert_eq!(compact_bits(spread_bits(x)), x, "round-trip failed for {x}");
266        }
267    }
268
269    #[test]
270    fn spread_bits_places_bit0_at_position0() {
271        assert_eq!(spread_bits(1), 1);
272    }
273
274    #[test]
275    fn spread_bits_places_bit1_at_position2() {
276        assert_eq!(spread_bits(2), 4);
277    }
278
279    #[test]
280    fn spread_bits_places_bit2_at_position4() {
281        assert_eq!(spread_bits(4), 16);
282    }
283
284    #[test]
285    fn origin_maps_to_zero() {
286        assert_eq!(morton_sort_key(c(0, 0), p(0, 16)), 0);
287    }
288
289    #[test]
290    fn x_axis_produces_even_bits() {
291        // x=1, y=0  ->  only bit 0 of x is set -> Morton bit 0 set -> code = 1
292        assert_eq!(morton_sort_key(c(1, 0), p(0, 16)), 1);
293        // x=2, y=0  ->  only bit 1 of x is set -> Morton bit 2 set -> code = 4
294        assert_eq!(morton_sort_key(c(2, 0), p(0, 16)), 4);
295    }
296
297    #[test]
298    fn y_axis_produces_odd_bits() {
299        // x=0, y=1  ->  only bit 0 of y is set -> Morton bit 1 set -> code = 2
300        assert_eq!(morton_sort_key(c(0, 1), p(0, 16)), 2);
301        // x=0, y=2  ->  only bit 1 of y is set -> Morton bit 3 set -> code = 8
302        assert_eq!(morton_sort_key(c(0, 2), p(0, 16)), 8);
303    }
304
305    #[test]
306    fn negative_coords_shift_correctly() {
307        // Shifting (-1, -1) by 1 maps to (0, 0) -> Morton code 0
308        assert_eq!(morton_sort_key(c(-1, -1), p(1, 16)), 0);
309        // Shifting (-1, 0) by 1 maps to (0, 1) -> Morton code 2
310        assert_eq!(morton_sort_key(c(-1, 0), p(1, 16)), 2);
311    }
312
313    #[test]
314    fn spatial_locality_z_order() {
315        // After shifting, (0,0) < (1,0) < (0,1) < (1,1) in Z-order
316        let k00 = morton_sort_key(c(0, 0), p(0, 16));
317        let k10 = morton_sort_key(c(1, 0), p(0, 16));
318        let k01 = morton_sort_key(c(0, 1), p(0, 16));
319        let k11 = morton_sort_key(c(1, 1), p(0, 16));
320        assert!(k00 < k10);
321        assert!(k10 < k01);
322        assert!(k01 < k11);
323    }
324
325    #[test]
326    fn interleave_round_trips_via_deinterleave() {
327        // Reconstruct x and y from interleaved bits and verify round-trip.
328        for x in 0u32..16 {
329            for y in 0u32..16 {
330                let code = interleave_bits((x, y).into());
331                let mut rx = 0u32;
332                let mut ry = 0u32;
333                for bit in 0..16 {
334                    rx |= ((code >> (2 * bit)) & 1) << bit;
335                    ry |= ((code >> (2 * bit + 1)) & 1) << bit;
336                }
337                assert_eq!(rx, x, "x mismatch for ({x}, {y})");
338                assert_eq!(ry, y, "y mismatch for ({x}, {y})");
339            }
340        }
341    }
342
343    // ── Morton encode/decode tests ────────────────────────────────────────────
344
345    const NUM_BITS: u32 = 15;
346    const COORD_SHIFT: u32 = 1 << (NUM_BITS - 1); // 16384
347    const MORTON: Morton = Morton {
348        bits: NUM_BITS,
349        shift: COORD_SHIFT,
350    };
351
352    /// Interleave `x` and `y` into a single Morton code using 15 bits per component.
353    ///
354    /// Even bit positions encode `x`, odd positions encode `y`.
355    /// This is the inverse of [`Morton::decode_codes`] / [`Morton::decode_delta`].
356    #[must_use]
357    #[inline]
358    pub fn encode_morton_15(coord: Coord<u32>) -> u32 {
359        let mut code = 0u32;
360        for bit in 0..15 {
361            code |= ((coord.x >> bit) & 1) << (2 * bit);
362            code |= ((coord.y >> bit) & 1) << (2 * bit + 1);
363        }
364        code
365    }
366
367    #[test]
368    fn test_decode_morton_codes_empty() {
369        assert_eq!(
370            MORTON.decode_codes(&[], &mut dec()).unwrap(),
371            [] as [i32; 0]
372        );
373    }
374
375    #[test]
376    fn test_decode_morton_codes_origin() {
377        // Morton code for (COORD_SHIFT, COORD_SHIFT) should decode to (0, 0).
378        let code = encode_morton_15((COORD_SHIFT, COORD_SHIFT).into());
379        let decoded = MORTON.decode_codes(&[code], &mut dec()).unwrap();
380        assert_eq!(decoded, [0, 0]);
381    }
382
383    #[test]
384    fn test_decode_morton_codes_known_values() {
385        // x=1, y=2 (pre-shift) -> decoded (1 - COORD_SHIFT, 2 - COORD_SHIFT)
386        let x: u32 = 1;
387        let y: u32 = 2;
388        let code = encode_morton_15((x, y).into());
389        let expected_x = x.cast_signed() - COORD_SHIFT.cast_signed();
390        let expected_y = y.cast_signed() - COORD_SHIFT.cast_signed();
391        let decoded = MORTON.decode_codes(&[code], &mut dec()).unwrap();
392        assert_eq!(decoded, [expected_x, expected_y]);
393    }
394
395    #[test]
396    fn test_decode_morton_codes_scalar_tail() {
397        // 3 codes - exercises the scalar tail path (< 8 codes).
398        let pairs: [Coord<u32>; _] = [(0, 1).into(), (2, 3).into(), (4, 5).into()];
399        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
400        let result = MORTON.decode_codes(&codes, &mut dec()).unwrap();
401        let expected = expected_coords(&pairs);
402        assert_eq!(result, expected);
403    }
404
405    #[test]
406    fn test_decode_morton_codes_full_simd_chunk() {
407        // 8 codes - exercises exactly one SIMD chunk, no scalar tail.
408        let pairs: [Coord<u32>; _] = [
409            (0, 0).into(),
410            (1, 0).into(),
411            (0, 1).into(),
412            (1, 1).into(),
413            (2, 3).into(),
414            (7, 5).into(),
415            (10, 9).into(),
416            (15, 15).into(),
417        ];
418        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
419        let result = MORTON.decode_codes(&codes, &mut dec()).unwrap();
420        let expected = expected_coords(&pairs);
421        assert_eq!(result, expected);
422    }
423
424    #[test]
425    fn test_decode_morton_codes_simd_plus_tail() {
426        // 11 codes - one full SIMD chunk of 8 plus a scalar tail of 3.
427        let pairs: Vec<Coord<u32>> = (0..11u32)
428            .map(|i| (i * 3 % 100, i * 7 % 100).into())
429            .collect();
430        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
431        let result = MORTON.decode_codes(&codes, &mut dec()).unwrap();
432        let expected = expected_coords(&pairs);
433        assert_eq!(result, expected);
434    }
435
436    // --- decode_delta tests ---
437
438    #[test]
439    fn test_decode_morton_delta_empty() {
440        assert_eq!(
441            MORTON.decode_delta(&[], &mut dec()).unwrap(),
442            [] as [i32; 0]
443        );
444    }
445
446    #[test]
447    fn test_decode_morton_delta_identity_with_zero_deltas() {
448        // All-zero deltas: every resolved code is 0, which decodes to (-COORD_SHIFT, -COORD_SHIFT).
449        let deltas = vec![0u32; 3];
450        let result = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
451        let shift = -COORD_SHIFT.cast_signed();
452        assert_eq!(result, vec![shift, shift, shift, shift, shift, shift]);
453    }
454
455    #[test]
456    fn test_decode_morton_delta_matches_codes_after_prefix_sum() {
457        // Build a sequence of absolute codes, compute their deltas, then verify that
458        // decode_delta produces the same output as decode_codes on the original absolute codes.
459        let pairs: Vec<Coord<u32>> = (0..11u32)
460            .map(|i| (i * 5 % 200, i * 9 % 200).into())
461            .collect();
462        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
463        let deltas = signed_deltas(&codes);
464
465        let from_codes = MORTON.decode_codes(&codes, &mut dec()).unwrap();
466        let from_deltas = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
467        assert_eq!(from_codes, from_deltas);
468    }
469
470    #[test]
471    fn test_decode_morton_delta_scalar_tail() {
472        // 3 codes via deltas - scalar tail path only.
473        let codes: Vec<u32> = vec![
474            encode_morton_15((10, 20).into()),
475            encode_morton_15((30, 40).into()),
476            encode_morton_15((50, 60).into()),
477        ];
478        let deltas = signed_deltas(&codes);
479        let from_codes = MORTON.decode_codes(&codes, &mut dec()).unwrap();
480        let from_deltas = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
481        assert_eq!(from_codes, from_deltas);
482    }
483
484    #[test]
485    fn test_decode_morton_delta_wrapping() {
486        // A single wrapping delta: start from a large code, subtract more than it - should
487        // still round-trip correctly via wrapping arithmetic.
488        let code_a = encode_morton_15((500, 300).into());
489        let code_b = encode_morton_15((10, 10).into()); // numerically smaller than code_a
490        let delta_b = code_b
491            .cast_signed()
492            .wrapping_sub(code_a.cast_signed())
493            .cast_unsigned();
494        assert_eq!(
495            MORTON.decode_delta(&[code_a, delta_b], &mut dec()).unwrap(),
496            MORTON.decode_codes(&[code_a, code_b], &mut dec()).unwrap()
497        );
498    }
499
500    /// Compute expected decoded `[x0, y0, x1, y1, ...]` from raw (pre-shift) coordinate pairs.
501    fn expected_coords(pairs: &[Coord<u32>]) -> Vec<i32> {
502        pairs
503            .iter()
504            .flat_map(|&Coord { x, y }| {
505                [
506                    x.cast_signed() - COORD_SHIFT.cast_signed(),
507                    y.cast_signed() - COORD_SHIFT.cast_signed(),
508                ]
509            })
510            .collect()
511    }
512
513    /// Compute wrapping signed deltas between consecutive Morton codes.
514    fn signed_deltas(codes: &[u32]) -> Vec<u32> {
515        let mut prev = 0i32;
516        codes
517            .iter()
518            .map(|&c| {
519                let delta = c.cast_signed().wrapping_sub(prev).cast_unsigned();
520                prev = c.cast_signed();
521                delta
522            })
523            .collect()
524    }
525}