use geo_types::Coord;
use robust::{orient2d, Coord as RobustCoord};
use std::cmp::Ordering;
pub mod parallel;
pub mod simd;
pub mod soa;
pub fn z_order_index(c: Coord<f64>) -> u64 {
let x = sortable_float(c.x);
let y = sortable_float(c.y);
part1by1(x >> 32) | (part1by1(y >> 32) << 1)
}
#[inline]
fn sortable_float(f: f64) -> u64 {
let bits = f.to_bits();
if bits & 0x8000000000000000 != 0 {
!bits
} else {
bits ^ 0x8000000000000000
}
}
#[inline]
fn part1by1(mut n: u64) -> u64 {
n &= 0x00000000FFFFFFFF;
n = (n | (n << 16)) & 0x0000FFFF0000FFFF;
n = (n | (n << 8)) & 0x00FF00FF00FF00FF;
n = (n | (n << 4)) & 0x0F0F0F0F0F0F0F0F;
n = (n | (n << 2)) & 0x3333333333333333;
n = (n | (n << 1)) & 0x5555555555555555;
n
}
pub fn compare_angular(center: Coord<f64>, target_a: Coord<f64>, target_b: Coord<f64>) -> Ordering {
if target_a == target_b {
return Ordering::Equal;
}
let quad_a = quadrant(center, target_a);
let quad_b = quadrant(center, target_b);
if quad_a != quad_b {
return quad_a.cmp(&quad_b);
}
let c = RobustCoord {
x: center.x,
y: center.y,
};
let a = RobustCoord {
x: target_a.x,
y: target_a.y,
};
let b = RobustCoord {
x: target_b.x,
y: target_b.y,
};
let orient = orient2d(c, a, b);
if orient > 0.0 {
Ordering::Less } else if orient < 0.0 {
Ordering::Greater } else {
let dx_a = target_a.x - center.x;
let dy_a = target_a.y - center.y;
let dist_a = dx_a * dx_a + dy_a * dy_a;
let dx_b = target_b.x - center.x;
let dy_b = target_b.y - center.y;
let dist_b = dx_b * dx_b + dy_b * dy_b;
dist_a.partial_cmp(&dist_b).unwrap_or(Ordering::Equal)
}
}
fn quadrant(c: Coord<f64>, t: Coord<f64>) -> u8 {
let dx = t.x - c.x;
let dy = t.y - c.y;
if dx > 0.0 && dy >= 0.0 {
0
} else if dx <= 0.0 && dy > 0.0 {
1
} else if dx < 0.0 && dy <= 0.0 {
2
} else {
3
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sortable_float() {
let values = vec![
f64::NEG_INFINITY,
-1000.0,
-2.0,
-1.0,
-0.0,
0.0,
1.0,
2.0,
1000.0,
f64::INFINITY,
];
for w in values.windows(2) {
let a = w[0];
let b = w[1];
assert!(
sortable_float(a) < sortable_float(b),
"Expected sortable_float({}) < sortable_float({}), but got {} >= {}",
a,
b,
sortable_float(a),
sortable_float(b)
);
}
let nan_mapped = sortable_float(f64::NAN);
assert!(nan_mapped > 0);
}
#[test]
fn test_part1by1() {
assert_eq!(part1by1(0b1), 0b1);
assert_eq!(part1by1(0b11), 0b0101);
assert_eq!(part1by1(0b101), 0b10001);
assert_eq!(part1by1(0xFFFFFFFF), 0x5555555555555555);
assert_eq!(part1by1(0x1_FFFFFFFF), 0x5555555555555555);
}
#[test]
fn test_z_order_index_locality() {
let p1 = Coord { x: 0.0, y: 0.0 };
let p2 = Coord {
x: 0.00001,
y: 0.00001,
};
let p3 = Coord { x: 100.0, y: 100.0 };
let z1 = z_order_index(p1);
let z2 = z_order_index(p2);
let z3 = z_order_index(p3);
let diff12 = z1.abs_diff(z2);
let diff13 = z1.abs_diff(z3);
assert!(
diff12 < diff13,
"Expected points closer in 2D space to have closer Z-order indices. \
diff12: {}, diff13: {}",
diff12,
diff13
);
}
#[test]
fn test_z_order_index_quadrants() {
let p_bl = Coord { x: -1.0, y: -1.0 }; let p_br = Coord { x: 1.0, y: -1.0 }; let p_tl = Coord { x: -1.0, y: 1.0 }; let p_tr = Coord { x: 1.0, y: 1.0 };
let z_bl = z_order_index(p_bl);
let z_br = z_order_index(p_br);
let z_tl = z_order_index(p_tl);
let z_tr = z_order_index(p_tr);
assert!(z_bl < z_br);
assert!(z_bl < z_tl);
assert!(z_br < z_tr);
assert!(z_tl < z_tr);
assert_ne!(z_bl, z_br);
assert_ne!(z_bl, z_tl);
assert_ne!(z_bl, z_tr);
assert_ne!(z_br, z_tl);
assert_ne!(z_br, z_tr);
assert_ne!(z_tl, z_tr);
}
}