tg-geom 0.0.0

Rust bindings for the `TG` Geometry library.
Documentation
//! Spatial indexing.

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u32)]
pub enum IndexKind {
    #[default]
    Default = 0,
    None = 1,
    Natural = 2,
    YStripes = 3,
}

impl From<tg_geom_sys::tg_index> for IndexKind {
    fn from(ix: tg_geom_sys::tg_index) -> Self {
        match ix {
            tg_geom_sys::tg_index_TG_DEFAULT => IndexKind::Default,
            tg_geom_sys::tg_index_TG_NONE => IndexKind::None,
            tg_geom_sys::tg_index_TG_NATURAL => IndexKind::Natural,
            tg_geom_sys::tg_index_TG_YSTRIPES => IndexKind::YStripes,
            _ => unreachable!("unknown index kind: {ix}"),
        }
    }
}

impl From<IndexKind> for tg_geom_sys::tg_index {
    fn from(ix: IndexKind) -> Self {
        match ix {
            IndexKind::Default => tg_geom_sys::tg_index_TG_DEFAULT,
            IndexKind::None => tg_geom_sys::tg_index_TG_NONE,
            IndexKind::Natural => tg_geom_sys::tg_index_TG_NATURAL,
            IndexKind::YStripes => tg_geom_sys::tg_index_TG_YSTRIPES,
        }
    }
}

pub fn set_default_index(ix: IndexKind) {
    unsafe { tg_geom_sys::tg_env_set_index(ix.into()) }
}

pub fn set_default_index_spread(spread: i32) {
    unsafe { tg_geom_sys::tg_env_set_index_spread(spread) }
}

pub fn set_print_fixed_floats(print: bool) {
    unsafe { tg_geom_sys::tg_env_set_print_fixed_floats(print) }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{Point, Ring};

    fn p(x: f64, y: f64) -> Point {
        Point::new(x, y)
    }

    fn octagon() -> Vec<Point> {
        vec![
            p(3.0, 0.0),
            p(7.0, 0.0),
            p(10.0, 3.0),
            p(10.0, 7.0),
            p(7.0, 10.0),
            p(3.0, 10.0),
            p(0.0, 7.0),
            p(0.0, 3.0),
            p(3.0, 0.0),
        ]
    }

    #[test]
    fn test_index_kind_variants() {
        assert_eq!(IndexKind::Default as u32, 0);
        assert_eq!(IndexKind::None as u32, 1);
        assert_eq!(IndexKind::Natural as u32, 2);
        assert_eq!(IndexKind::YStripes as u32, 3);
    }

    #[test]
    fn test_index_kind_default() {
        let ix: IndexKind = Default::default();
        assert_eq!(ix, IndexKind::Default);
    }

    #[test]
    fn test_index_kind_copy() {
        let ix1 = IndexKind::Natural;
        let ix2 = ix1;
        assert_eq!(ix1, ix2);
    }

    #[test]
    fn test_index_kind_eq() {
        assert_eq!(IndexKind::None, IndexKind::None);
        assert_ne!(IndexKind::None, IndexKind::Natural);
    }

    #[test]
    fn test_index_kind_debug() {
        let debug_str = format!("{:?}", IndexKind::Natural);
        assert!(debug_str.contains("Natural"));
    }

    #[test]
    fn test_index_kind_sys_roundtrip() {
        let kinds = [
            IndexKind::Default,
            IndexKind::None,
            IndexKind::Natural,
            IndexKind::YStripes,
        ];
        for kind in &kinds {
            let sys_kind: tg_geom_sys::tg_index = (*kind).into();
            let back: IndexKind = sys_kind.into();
            assert_eq!(*kind, back);
        }
    }

    #[test]
    fn test_set_default_index() {
        set_default_index(IndexKind::Natural);
        set_default_index(IndexKind::None);
        set_default_index(IndexKind::YStripes);
        set_default_index(IndexKind::Default);
    }

    #[test]
    fn test_set_default_index_spread() {
        set_default_index_spread(4);
        set_default_index_spread(8);
        set_default_index_spread(16);
    }

    #[test]
    fn test_set_print_fixed_floats() {
        set_print_fixed_floats(true);
        set_print_fixed_floats(false);
    }

    #[test]
    fn test_ring_index_kinds() {
        let points = octagon();

        let ring_default = Ring::new(&points).unwrap();
        let ring_none = Ring::new_ix(&points, IndexKind::None).unwrap();
        let ring_natural = Ring::new_ix(&points, IndexKind::Natural).unwrap();
        let ring_ystripes = Ring::new_ix(&points, IndexKind::YStripes).unwrap();

        assert_eq!(ring_default.num_points(), ring_none.num_points());
        assert_eq!(ring_default.num_points(), ring_natural.num_points());
        assert_eq!(ring_default.num_points(), ring_ystripes.num_points());

        assert_eq!(ring_default.area(), ring_none.area());
        assert_eq!(ring_default.area(), ring_natural.area());
        assert_eq!(ring_default.area(), ring_ystripes.area());
    }

    #[test]
    fn test_ring_index_properties() {
        let ring_natural = Ring::new_ix(&octagon(), IndexKind::Natural).unwrap();

        let spread = ring_natural.index_spread();
        let num_levels = ring_natural.index_num_levels();

        assert!(spread >= 0);
        assert!(num_levels >= 0);

        if num_levels > 0 {
            let level0_rects = ring_natural.index_level_num_rects(0);
            assert!(level0_rects >= 0);
        }
    }

    #[test]
    fn test_ring_none_index() {
        let ring_none = Ring::new_ix(&octagon(), IndexKind::None).unwrap();
        assert_eq!(ring_none.index_num_levels(), 0);
    }
}