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!((1..=16).contains(&params.bits));
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 `u32::BITS - te.leading_zeros()`.
81            // Capped at 16: Morton codes are u32, so each axis may use at most 16 bits.
82            let required_bits = u32::BITS - extent.leading_zeros();
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 mut chunks = data.chunks_exact(LANES);
142
143        for chunk in chunks.by_ref() {
144            let buf = [
145                chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], chunk[7],
146            ];
147            self.decode_chunk(buf, shift_vec, &mut out);
148        }
149
150        // Scalar tail for any codes that didn't fill a full SIMD chunk.
151        for &code in chunks.remainder() {
152            let coord = self.decode_one(code);
153            out.push(coord.x);
154            out.push(coord.y);
155        }
156
157        dec.adjust_alloc(&out, alloc_size)?;
158        Ok(out)
159    }
160
161    /// Decode delta-encoded Morton codes to flat `[x0, y0, x1, y1, ...]`, charging `dec` for the output.
162    ///
163    /// Each input value is a signed delta (stored as u32 with wrapping arithmetic)
164    /// relative to the previous Morton code. The sequential prefix sum is computed
165    /// in chunks of 8 into a stack-allocated buffer, which is then SIMD-decoded.
166    /// This keeps the working set in registers / L1 cache.
167    pub fn decode_delta(self, data: &[u32], dec: &mut Decoder) -> MltResult<Vec<i32>> {
168        let alloc_size = data.len() * 2;
169        let mut out = dec.alloc(alloc_size)?;
170        let shift_vec = u32x8::splat(self.shift);
171
172        let mut prev = 0i32;
173        let mut chunks = data.chunks_exact(LANES);
174
175        for chunk in chunks.by_ref() {
176            // Sequential prefix sum into a stack buffer — no heap allocation.
177            let mut buf = [0u32; LANES];
178            for (b, &d) in buf.iter_mut().zip(chunk.iter()) {
179                prev = prev.wrapping_add(d.cast_signed());
180                *b = prev.cast_unsigned();
181            }
182            self.decode_chunk(buf, shift_vec, &mut out);
183        }
184
185        // Scalar tail for any codes that didn't fill a full SIMD chunk.
186        for &d in chunks.remainder() {
187            prev = prev.wrapping_add(d.cast_signed());
188            let coord = self.decode_one(prev.cast_unsigned());
189            out.push(coord.x);
190            out.push(coord.y);
191        }
192
193        dec.adjust_alloc(&out, alloc_size)?;
194        Ok(out)
195    }
196
197    /// SIMD-decode a chunk of exactly 8 resolved Morton codes into the output buffer.
198    ///
199    /// Each code has already been resolved to its absolute value (no delta pending).
200    /// Even-indexed bits encode x, odd-indexed bits encode y.
201    #[inline]
202    fn decode_chunk(self, buf: [u32; LANES], shift_vec: u32x8, out: &mut Vec<i32>) {
203        let codes = u32x8::from(buf);
204        // Odd bits become even after shifting right by 1, giving the y component.
205        let codes_y = codes >> 1;
206
207        let mut x_vec = u32x8::ZERO;
208        let mut y_vec = u32x8::ZERO;
209
210        for i in 0..self.bits {
211            // Mask for the bit position 2*i in the original Morton code.
212            let bit_mask = u32x8::splat(1u32 << (2 * i));
213            // Extract bit 2*i from each code and shift it down to position i.
214            x_vec |= (codes & bit_mask) >> i;
215            y_vec |= (codes_y & bit_mask) >> i;
216        }
217
218        let xs: [u32; LANES] = (x_vec - shift_vec).into();
219        let ys: [u32; LANES] = (y_vec - shift_vec).into();
220
221        for lane in 0..LANES {
222            out.push(xs[lane].cast_signed());
223            out.push(ys[lane].cast_signed());
224        }
225    }
226}
227
228#[cfg(test)]
229mod tests {
230
231    use super::*;
232    use crate::test_helpers::dec;
233
234    const fn c(x: i32, y: i32) -> Coord<i32> {
235        Coord::<i32> { x, y }
236    }
237
238    const fn p(shift: u32, bits: u32) -> CurveParams {
239        CurveParams { shift, bits }
240    }
241
242    // ── interleave_bits / morton_sort_key ─────────────────────────────────────
243
244    /// Spread the lower 16 bits of `tx` into the even bit positions (0, 2, 4, …)
245    /// of a 32-bit word, inserting a 0 between every original bit.
246    fn spread_bits(mut tx: u32) -> u32 {
247        tx = (tx | (tx << 8)) & 0x00FF_00FF;
248        tx = (tx | (tx << 4)) & 0x0F0F_0F0F;
249        tx = (tx | (tx << 2)) & 0x3333_3333;
250        tx = (tx | (tx << 1)) & 0x5555_5555;
251        tx
252    }
253
254    /// Compact the bits at even positions (0, 2, 4, …) of `tx` into the lower
255    /// 16 bits, discarding the interleaved zeros.
256    fn compact_bits(mut tx: u32) -> u32 {
257        tx &= 0x5555_5555;
258        tx = (tx | (tx >> 1)) & 0x3333_3333;
259        tx = (tx | (tx >> 2)) & 0x0F0F_0F0F;
260        tx = (tx | (tx >> 4)) & 0x00FF_00FF;
261        tx = (tx | (tx >> 8)) & 0x0000_FFFF;
262        tx
263    }
264
265    #[test]
266    fn spread_then_compact_is_identity() {
267        for x in 0u32..=0xFFFF {
268            assert_eq!(compact_bits(spread_bits(x)), x, "round-trip failed for {x}");
269        }
270    }
271
272    #[test]
273    fn spread_bits_places_bit0_at_position0() {
274        assert_eq!(spread_bits(1), 1);
275    }
276
277    #[test]
278    fn spread_bits_places_bit1_at_position2() {
279        assert_eq!(spread_bits(2), 4);
280    }
281
282    #[test]
283    fn spread_bits_places_bit2_at_position4() {
284        assert_eq!(spread_bits(4), 16);
285    }
286
287    #[test]
288    fn origin_maps_to_zero() {
289        assert_eq!(morton_sort_key(c(0, 0), p(0, 16)), 0);
290    }
291
292    #[test]
293    fn x_axis_produces_even_bits() {
294        // x=1, y=0  →  only bit 0 of x is set → Morton bit 0 set → code = 1
295        assert_eq!(morton_sort_key(c(1, 0), p(0, 16)), 1);
296        // x=2, y=0  →  only bit 1 of x is set → Morton bit 2 set → code = 4
297        assert_eq!(morton_sort_key(c(2, 0), p(0, 16)), 4);
298    }
299
300    #[test]
301    fn y_axis_produces_odd_bits() {
302        // x=0, y=1  →  only bit 0 of y is set → Morton bit 1 set → code = 2
303        assert_eq!(morton_sort_key(c(0, 1), p(0, 16)), 2);
304        // x=0, y=2  →  only bit 1 of y is set → Morton bit 3 set → code = 8
305        assert_eq!(morton_sort_key(c(0, 2), p(0, 16)), 8);
306    }
307
308    #[test]
309    fn negative_coords_shift_correctly() {
310        // Shifting (-1, -1) by 1 maps to (0, 0) → Morton code 0
311        assert_eq!(morton_sort_key(c(-1, -1), p(1, 16)), 0);
312        // Shifting (-1, 0) by 1 maps to (0, 1) → Morton code 2
313        assert_eq!(morton_sort_key(c(-1, 0), p(1, 16)), 2);
314    }
315
316    #[test]
317    fn spatial_locality_z_order() {
318        // After shifting, (0,0) < (1,0) < (0,1) < (1,1) in Z-order
319        let k00 = morton_sort_key(c(0, 0), p(0, 16));
320        let k10 = morton_sort_key(c(1, 0), p(0, 16));
321        let k01 = morton_sort_key(c(0, 1), p(0, 16));
322        let k11 = morton_sort_key(c(1, 1), p(0, 16));
323        assert!(k00 < k10);
324        assert!(k10 < k01);
325        assert!(k01 < k11);
326    }
327
328    #[test]
329    fn interleave_round_trips_via_deinterleave() {
330        // Reconstruct x and y from interleaved bits and verify round-trip.
331        for x in 0u32..16 {
332            for y in 0u32..16 {
333                let code = interleave_bits((x, y).into());
334                let mut rx = 0u32;
335                let mut ry = 0u32;
336                for bit in 0..16 {
337                    rx |= ((code >> (2 * bit)) & 1) << bit;
338                    ry |= ((code >> (2 * bit + 1)) & 1) << bit;
339                }
340                assert_eq!(rx, x, "x mismatch for ({x}, {y})");
341                assert_eq!(ry, y, "y mismatch for ({x}, {y})");
342            }
343        }
344    }
345
346    // ── Morton encode/decode tests ────────────────────────────────────────────
347
348    const NUM_BITS: u32 = 15;
349    const COORD_SHIFT: u32 = 1 << (NUM_BITS - 1); // 16384
350    const MORTON: Morton = Morton {
351        bits: NUM_BITS,
352        shift: COORD_SHIFT,
353    };
354
355    /// Interleave `x` and `y` into a single Morton code using 15 bits per component.
356    ///
357    /// Even bit positions encode `x`, odd positions encode `y`.
358    /// This is the inverse of [`Morton::decode_codes`] / [`Morton::decode_delta`].
359    #[must_use]
360    #[inline]
361    pub fn encode_morton_15(coord: Coord<u32>) -> u32 {
362        let mut code = 0u32;
363        for bit in 0..15 {
364            code |= ((coord.x >> bit) & 1) << (2 * bit);
365            code |= ((coord.y >> bit) & 1) << (2 * bit + 1);
366        }
367        code
368    }
369
370    #[test]
371    fn test_decode_morton_codes_empty() {
372        assert!(MORTON.decode_codes(&[], &mut dec()).unwrap().is_empty());
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!(MORTON.decode_delta(&[], &mut dec()).unwrap().is_empty());
441    }
442
443    #[test]
444    fn test_decode_morton_delta_identity_with_zero_deltas() {
445        // All-zero deltas: every resolved code is 0, which decodes to (-COORD_SHIFT, -COORD_SHIFT).
446        let deltas = vec![0u32; 3];
447        let result = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
448        let shift = -COORD_SHIFT.cast_signed();
449        assert_eq!(result, vec![shift, shift, shift, shift, shift, shift]);
450    }
451
452    #[test]
453    fn test_decode_morton_delta_matches_codes_after_prefix_sum() {
454        // Build a sequence of absolute codes, compute their deltas, then verify that
455        // decode_delta produces the same output as decode_codes on the original absolute codes.
456        let pairs: Vec<Coord<u32>> = (0..11u32)
457            .map(|i| (i * 5 % 200, i * 9 % 200).into())
458            .collect();
459        let codes: Vec<u32> = pairs.iter().map(|&c| encode_morton_15(c)).collect();
460        let deltas = signed_deltas(&codes);
461
462        let from_codes = MORTON.decode_codes(&codes, &mut dec()).unwrap();
463        let from_deltas = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
464        assert_eq!(from_codes, from_deltas);
465    }
466
467    #[test]
468    fn test_decode_morton_delta_scalar_tail() {
469        // 3 codes via deltas — scalar tail path only.
470        let codes: Vec<u32> = vec![
471            encode_morton_15((10, 20).into()),
472            encode_morton_15((30, 40).into()),
473            encode_morton_15((50, 60).into()),
474        ];
475        let deltas = signed_deltas(&codes);
476        let from_codes = MORTON.decode_codes(&codes, &mut dec()).unwrap();
477        let from_deltas = MORTON.decode_delta(&deltas, &mut dec()).unwrap();
478        assert_eq!(from_codes, from_deltas);
479    }
480
481    #[test]
482    fn test_decode_morton_delta_wrapping() {
483        // A single wrapping delta: start from a large code, subtract more than it — should
484        // still round-trip correctly via wrapping arithmetic.
485        let code_a = encode_morton_15((500, 300).into());
486        let code_b = encode_morton_15((10, 10).into()); // numerically smaller than code_a
487        let delta_b = code_b
488            .cast_signed()
489            .wrapping_sub(code_a.cast_signed())
490            .cast_unsigned();
491        assert_eq!(
492            MORTON.decode_delta(&[code_a, delta_b], &mut dec()).unwrap(),
493            MORTON.decode_codes(&[code_a, code_b], &mut dec()).unwrap()
494        );
495    }
496
497    /// Compute expected decoded `[x0, y0, x1, y1, ...]` from raw (pre-shift) coordinate pairs.
498    fn expected_coords(pairs: &[Coord<u32>]) -> Vec<i32> {
499        pairs
500            .iter()
501            .flat_map(|&Coord { x, y }| {
502                [
503                    x.cast_signed() - COORD_SHIFT.cast_signed(),
504                    y.cast_signed() - COORD_SHIFT.cast_signed(),
505                ]
506            })
507            .collect()
508    }
509
510    /// Compute wrapping signed deltas between consecutive Morton codes.
511    fn signed_deltas(codes: &[u32]) -> Vec<u32> {
512        let mut prev = 0i32;
513        codes
514            .iter()
515            .map(|&c| {
516                let delta = c.cast_signed().wrapping_sub(prev).cast_unsigned();
517                prev = c.cast_signed();
518                delta
519            })
520            .collect()
521    }
522}