use crate::constants::{
EPSILON, INV_RES0_U_GNOMONIC, MAX_H3_RES, M_AP7_ROT_RADS, M_ONETHIRD, M_RSQRT7,
M_SQRT3_2, M_SQRT7, NUM_HEX_VERTS, NUM_ICOSA_FACES, NUM_PENT_VERTS, RES0_U_GNOMONIC,
};
use crate::coords::ijk::{
_down_ap3, _down_ap3r, _down_ap7r, _hex2d_to_coord_ijk, _ijk_add, _ijk_normalize, _ijk_rotate60_ccw,
_ijk_rotate60_cw, _ijk_scale, _ijk_sub, _ijk_to_hex2d, _set_ijk,
};
use crate::latlng::{_geo_az_distance_rads, _geo_azimuth_rads, _pos_angle_rads};
use crate::math::vec2d::{_v2d_almost_equals, _v2d_intersect, _v2d_mag};
use crate::math::vec3d::{_geo_to_vec3d, _point_square_dist};
use crate::types::{CellBoundary, CoordIJK, FaceIJK, LatLng, Vec2d, Vec3d};
use crate::{h3_index, MAX_CELL_BNDRY_VERTS};
pub(crate) const IJ_QUADRANT: usize = 1; pub(crate) const KI_QUADRANT: usize = 2; pub(crate) const JK_QUADRANT: usize = 3;
#[rustfmt::skip]
static MAX_DIM_BY_CII_RES: [i32; (MAX_H3_RES + 2) as usize] = [ 2, -1, 14, -1, 98, -1, 686, -1, 4802, -1, 33614, -1, 235298, -1, 1_647_086, -1, 11_529_602, ];
#[rustfmt::skip]
static UNIT_SCALE_BY_CII_RES: [i32; (MAX_H3_RES + 2) as usize] = [
1, -1, 7, -1, 49, -1, 343, -1, 2401, -1, 16807, -1, 117649, -1, 823543, -1, 5_764_801, ];
#[rustfmt::skip]
pub(crate) static FACE_CENTER_GEO: [LatLng; NUM_ICOSA_FACES as usize] = [
LatLng { lat: 0.803_582_649_718_989_94, lng: 1.248_397_419_617_396 }, LatLng { lat: 1.307_747_883_455_638_2, lng: 2.536_945_009_877_921 }, LatLng { lat: 1.054_751_253_523_952, lng: -1.347_517_358_900_396_6 }, LatLng { lat: 0.600_191_595_538_186_8, lng: -0.450_603_909_469_755_75 }, LatLng { lat: 0.491_715_428_198_773_87, lng: 0.401_988_202_911_306_94 }, LatLng { lat: 0.172_745_327_415_618_7, lng: 1.678_146_885_280_433_7 }, LatLng { lat: 0.605_929_321_571_350_7, lng: 2.953_923_329_812_411_6 }, LatLng { lat: 0.427_370_518_328_979_64, lng: -1.888_876_200_336_285_4 }, LatLng { lat: -0.079_066_118_549_212_83, lng: -0.733_429_513_380_867_74 }, LatLng { lat: -0.230_961_644_455_383_64, lng: 0.506_495_587_332_349 }, LatLng { lat: 0.079_066_118_549_212_83, lng: 2.408_163_140_208_925_5 }, LatLng { lat: 0.230_961_644_455_383_64, lng: -2.635_097_066_257_444 }, LatLng { lat: -0.172_745_327_415_618_7, lng: -1.463_445_768_309_359_5 }, LatLng { lat: -0.605_929_321_571_350_7, lng: -0.187_669_323_777_381_62 }, LatLng { lat: -0.427_370_518_328_979_64, lng: 1.252_716_453_253_508 }, LatLng { lat: -0.600_191_595_538_186_8, lng: 2.690_988_744_120_037_5 }, LatLng { lat: -0.491_715_428_198_773_87, lng: -2.739_604_450_678_486_3 }, LatLng { lat: -0.803_582_649_718_989_94, lng: -1.893_195_233_972_397 }, LatLng { lat: -1.307_747_883_455_638_2, lng: -0.604_647_643_711_872_1 }, LatLng { lat: -1.054_751_253_523_952, lng: 1.794_075_294_689_396_6 }, ];
#[rustfmt::skip]
static FACE_CENTER_POINT: [Vec3d; NUM_ICOSA_FACES as usize] = [
Vec3d { x: 0.219_930_779_140_460_6, y: 0.658_369_178_027_499_6, z: 0.719_847_537_892_618_2 }, Vec3d { x: -0.213_923_483_450_142_1, y: 0.147_817_182_955_070_3, z: 0.965_601_793_521_420_5 }, Vec3d { x: 0.109_262_527_878_479_7, y: -0.481_195_157_287_321, z: 0.869_777_512_128_725_3 }, Vec3d { x: 0.742_856_730_158_679_1, y: -0.359_394_167_827_802_8, z: 0.564_800_593_651_703_3 }, Vec3d { x: 0.811_253_470_914_096_9, y: 0.344_895_323_763_938_4, z: 0.472_138_773_641_393 }, Vec3d { x: -0.105_549_814_961_392_1, y: 0.979_445_729_641_141_3, z: 0.171_887_461_000_936_5 }, Vec3d { x: -0.807_540_757_997_009_2, y: 0.153_355_248_589_881_8, z: 0.569_526_199_488_268_8 }, Vec3d { x: -0.284_614_806_978_790_7, y: -0.864_408_097_265_420_6, z: 0.414_479_255_247_354 }, Vec3d { x: 0.740_562_147_385_448_2, y: -0.667_329_956_456_552_4, z: -0.078_983_764_632_673_77 }, Vec3d { x: 0.851_230_398_647_429_3, y: 0.472_234_378_858_268_1, z: -0.228_913_738_868_780_8 }, Vec3d { x: -0.740_562_147_385_448_1, y: 0.667_329_956_456_552_4, z: 0.078_983_764_632_673_77 }, Vec3d { x: -0.851_230_398_647_429_2, y: -0.472_234_378_858_268_2, z: 0.228_913_738_868_780_8 }, Vec3d { x: 0.105_549_814_961_391_9, y: -0.979_445_729_641_141_3, z: -0.171_887_461_000_936_5 }, Vec3d { x: 0.807_540_757_997_009_2, y: -0.153_355_248_589_881_9, z: -0.569_526_199_488_268_8 }, Vec3d { x: 0.284_614_806_978_790_8, y: 0.864_408_097_265_420_4, z: -0.414_479_255_247_354 }, Vec3d { x: -0.742_856_730_158_679_1, y: 0.359_394_167_827_802_7, z: -0.564_800_593_651_703_3 }, Vec3d { x: -0.811_253_470_914_097_1, y: -0.344_895_323_763_938_2, z: -0.472_138_773_641_393 }, Vec3d { x: -0.219_930_779_140_460_7, y: -0.658_369_178_027_499_6, z: -0.719_847_537_892_618_2 }, Vec3d { x: 0.213_923_483_450_142, y: -0.147_817_182_955_070_4, z: -0.965_601_793_521_420_5 }, Vec3d { x: -0.109_262_527_878_479_6, y: 0.481_195_157_287_321, z: -0.869_777_512_128_725_3 }, ];
#[rustfmt::skip]
static FACE_AXES_AZ_RADS_CII: [[f64; 3]; NUM_ICOSA_FACES as usize] = [
[5.619_958_268_523_94, 3.525_563_166_130_744_5, 1.431_168_063_737_548_7], [5.760_339_081_714_187, 3.665_943_979_320_991_7, 1.571_548_876_927_796], [0.780_213_654_393_430_1, 4.969_003_859_179_821, 2.874_608_756_786_625_7], [0.430_469_363_979_999_9, 4.619_259_568_766_391, 2.524_864_466_373_195_5], [6.130_269_123_335_111, 4.035_874_020_941_916, 1.941_478_918_548_720_3], [2.692_877_706_530_643, 0.598_482_604_137_447_1, 4.787_272_808_923_838], [2.982_963_003_477_244, 0.888_567_901_084_048_4, 5.077_358_105_870_44], [3.532_912_002_790_141, 1.438_516_900_396_945_7, 5.627_307_105_183_337], [3.494_305_004_259_568, 1.399_909_901_866_372_9, 5.588_700_106_652_764], [3.003_214_169_499_538_4, 0.908_819_067_106_342_9, 5.097_609_271_892_734], [5.930_472_956_509_811_6, 3.836_077_854_116_616, 1.741_682_751_723_420_4], [0.138_378_484_090_254_85, 4.327_168_688_876_646, 2.232_773_586_483_45], [0.448_714_947_059_150_36, 4.637_505_151_845_541_5, 2.543_110_049_452_346], [0.158_629_650_112_549_36, 4.347_419_854_898_94, 2.253_024_752_505_745], [5.891_865_957_979_238_5, 3.797_470_855_586_043, 1.703_075_753_192_847_6], [2.711_123_289_609_793_3, 0.616_728_187_216_597_8, 4.805_518_392_002_988_7], [3.294_508_837_434_268, 1.200_113_735_041_073, 5.388_903_939_827_464], [3.804_819_692_245_44, 1.710_424_589_852_244_5, 5.899_214_794_638_635], [3.664_438_879_055_192_4, 1.570_043_776_661_997, 5.758_833_981_448_388], [2.361_378_999_196_363, 0.266_983_896_803_167_6, 4.455_774_101_589_558_6], ];
#[derive(Debug, Clone, Copy)]
pub(crate) struct FaceOrientIJK {
pub(crate) face: i32, pub(crate) translate: CoordIJK, pub(crate) ccw_rot60: i32, }
pub(crate) const INVALID_FACE: i32 = -1;
#[rustfmt::skip]
pub(crate) static FACE_NEIGHBORS: [[FaceOrientIJK; 4]; NUM_ICOSA_FACES as usize] = [
[ FaceOrientIJK { face: 0, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 }, FaceOrientIJK { face: 4, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 }, FaceOrientIJK { face: 1, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 }, FaceOrientIJK { face: 5, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ], [ FaceOrientIJK { face: 1, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 0, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 2, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 6, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 2, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 1, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 3, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 7, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 3, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 2, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 4, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 8, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 4, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 3, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 0, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 9, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 5, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 10,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 14,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 0, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 6, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 11,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 10,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 1, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 7, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 12,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 11,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 2, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 8, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 13,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 12,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 3, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 9, translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 14,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 13,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 4, translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 10,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 5, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 6, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 15,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 11,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 6, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 7, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 16,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 12,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 7, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 8, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 17,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 13,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 8, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 9, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 18,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 14,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 9, translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 3 },
FaceOrientIJK { face: 5, translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 3 },
FaceOrientIJK { face: 19,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 15,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 16,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 19,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 10,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 16,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 17,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 15,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 11,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 17,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 18,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 16,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 12,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 18,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 19,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 17,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 13,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
[ FaceOrientIJK { face: 19,translate: CoordIJK { i: 0, j: 0, k: 0 }, ccw_rot60: 0 },
FaceOrientIJK { face: 15,translate: CoordIJK { i: 2, j: 0, k: 2 }, ccw_rot60: 1 },
FaceOrientIJK { face: 18,translate: CoordIJK { i: 2, j: 2, k: 0 }, ccw_rot60: 5 },
FaceOrientIJK { face: 14,translate: CoordIJK { i: 0, j: 2, k: 2 }, ccw_rot60: 3 } ],
];
#[rustfmt::skip]
pub(crate) static ADJACENT_FACE_DIR: [[i32; NUM_ICOSA_FACES as usize]; NUM_ICOSA_FACES as usize] = [
[ 0, KI_QUADRANT as i32, -1, -1, IJ_QUADRANT as i32, JK_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[IJ_QUADRANT as i32, 0, KI_QUADRANT as i32, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[ -1, IJ_QUADRANT as i32, 0, KI_QUADRANT as i32, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[ -1, -1, IJ_QUADRANT as i32, 0, KI_QUADRANT as i32, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[KI_QUADRANT as i32, -1, -1, IJ_QUADRANT as i32, 0, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
[JK_QUADRANT as i32, -1, -1, -1, -1, 0, -1, -1, -1, -1, IJ_QUADRANT as i32, -1, -1, -1, KI_QUADRANT as i32, -1, -1, -1, -1, -1],
[ -1, JK_QUADRANT as i32, -1, -1, -1, -1, 0, -1, -1, -1, KI_QUADRANT as i32, IJ_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1, -1],
[ -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, 0, -1, -1, -1, KI_QUADRANT as i32, IJ_QUADRANT as i32, -1, -1, -1, -1, -1, -1, -1],
[ -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, 0, -1, -1, -1, KI_QUADRANT as i32, IJ_QUADRANT as i32, -1, -1, -1, -1, -1, -1],
[ -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, 0, -1, -1, -1, KI_QUADRANT as i32, IJ_QUADRANT as i32, -1, -1, -1, -1, -1],
[ -1, -1, -1, -1, -1, IJ_QUADRANT as i32, KI_QUADRANT as i32, -1, -1, -1, 0, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1],
[ -1, -1, -1, -1, -1, -1, IJ_QUADRANT as i32, KI_QUADRANT as i32, -1, -1, -1, 0, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1],
[ -1, -1, -1, -1, -1, -1, -1, IJ_QUADRANT as i32, KI_QUADRANT as i32, -1, -1, -1, 0, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1],
[ -1, -1, -1, -1, -1, -1, -1, -1, IJ_QUADRANT as i32, KI_QUADRANT as i32, -1, -1, -1, 0, -1, -1, -1, -1, JK_QUADRANT as i32, -1],
[ -1, -1, -1, -1, -1, KI_QUADRANT as i32, -1, -1, -1, IJ_QUADRANT as i32, -1, -1, -1, -1, 0, -1, -1, -1, -1, JK_QUADRANT as i32],
[ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, -1, 0, IJ_QUADRANT as i32, -1, -1, KI_QUADRANT as i32],
[ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, KI_QUADRANT as i32, 0, IJ_QUADRANT as i32, -1, -1],
[ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, KI_QUADRANT as i32, 0, IJ_QUADRANT as i32, -1],
[ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, JK_QUADRANT as i32, -1, -1, -1, KI_QUADRANT as i32, 0, IJ_QUADRANT as i32],
[ -1, -1, -1, -1, -1, KI_QUADRANT as i32, -1, -1, -1, IJ_QUADRANT as i32, -1, -1, -1, -1, JK_QUADRANT as i32, IJ_QUADRANT as i32, -1, -1, KI_QUADRANT as i32, 0], ];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Overage {
NoOverage = 0, FaceEdge = 1, NewFace = 2, }
#[inline]
pub(crate) fn _geo_to_closest_face(g: &LatLng, face: &mut i32, sqd: &mut f64) {
let mut v3d = Vec3d::default();
_geo_to_vec3d(g, &mut v3d);
*face = 0;
*sqd = 5.0;
for f_idx in 0..(NUM_ICOSA_FACES as usize) {
let current_face_center_v3d = &FACE_CENTER_POINT[f_idx];
let sqdt_to_current_face = _point_square_dist(current_face_center_v3d, &v3d);
if sqdt_to_current_face < *sqd {
*face = f_idx as i32;
*sqd = sqdt_to_current_face;
}
}
}
#[inline]
pub(crate) fn _geo_to_hex2d(g: &LatLng, res: i32, face: &mut i32, v: &mut Vec2d) {
let mut sqd = 0.0;
_geo_to_closest_face(g, face, &mut sqd);
let r_arg = (1.0 - sqd * 0.5).max(-1.0).min(1.0);
let r_angular = r_arg.acos();
if r_angular < EPSILON {
v.x = 0.0;
v.y = 0.0;
return;
}
let az_from_face_center_to_g = _geo_azimuth_rads(&FACE_CENTER_GEO[*face as usize], g);
let mut theta_from_i_axis =
_pos_angle_rads(FACE_AXES_AZ_RADS_CII[*face as usize][0] - _pos_angle_rads(az_from_face_center_to_g));
if crate::h3_index::is_resolution_class_iii(res) {
theta_from_i_axis = _pos_angle_rads(theta_from_i_axis - M_AP7_ROT_RADS);
}
let r_gnomonic_scaled = r_angular.tan();
let mut r_hex2d_scaled = r_gnomonic_scaled * INV_RES0_U_GNOMONIC;
for _ in 0..res {
r_hex2d_scaled *= M_SQRT7;
}
v.x = r_hex2d_scaled * theta_from_i_axis.cos();
v.y = r_hex2d_scaled * theta_from_i_axis.sin();
}
#[inline]
pub(crate) fn _hex2d_to_geo(v: &Vec2d, face_idx: i32, res: i32, substrate: bool, g: &mut LatLng) {
let r_hex2d_at_input_res = _v2d_mag(v);
if r_hex2d_at_input_res < EPSILON {
*g = FACE_CENTER_GEO[face_idx as usize];
return;
}
let theta_hex2d = v.y.atan2(v.x);
let mut r_hex2d_at_res0 = r_hex2d_at_input_res;
for _i_res_scale in 0..res {
r_hex2d_at_res0 *= M_RSQRT7;
}
if substrate {
r_hex2d_at_res0 *= M_ONETHIRD;
if crate::h3_index::is_resolution_class_iii(res) {
r_hex2d_at_res0 *= M_RSQRT7;
}
}
let r_gnomonic = r_hex2d_at_res0 * RES0_U_GNOMONIC;
let r_angular = r_gnomonic.atan();
let mut theta_for_az = theta_hex2d;
if !substrate && crate::h3_index::is_resolution_class_iii(res) {
theta_for_az = _pos_angle_rads(theta_for_az + M_AP7_ROT_RADS);
}
let az_from_face_center = _pos_angle_rads(FACE_AXES_AZ_RADS_CII[face_idx as usize][0] - theta_for_az);
_geo_az_distance_rads(&FACE_CENTER_GEO[face_idx as usize], az_from_face_center, r_angular, g);
}
#[inline]
pub(crate) fn _geo_to_face_ijk(g: &LatLng, res: i32, h: &mut FaceIJK) {
let mut v = Vec2d::default();
_geo_to_hex2d(g, res, &mut h.face, &mut v);
_hex2d_to_coord_ijk(&v, &mut h.coord);
}
#[inline]
pub(crate) fn _face_ijk_to_geo(h: &FaceIJK, res: i32, g: &mut LatLng) {
let mut v = Vec2d::default();
_ijk_to_hex2d(&h.coord, &mut v);
_hex2d_to_geo(&v, h.face, res, false, g);
}
#[inline]
pub(crate) fn _adjust_overage_class_ii(fijk: &mut FaceIJK, res: i32, pent_leading_4: bool, substrate: bool) -> Overage {
let mut overage_status = Overage::NoOverage;
let ijk = &mut fijk.coord;
let max_dim_base = MAX_DIM_BY_CII_RES[res as usize];
let mut current_max_dim = max_dim_base;
if substrate {
current_max_dim *= 3;
}
let coord_sum = ijk.i + ijk.j + ijk.k;
if substrate && (coord_sum == current_max_dim) {
overage_status = Overage::FaceEdge;
} else if coord_sum > current_max_dim {
overage_status = Overage::NewFace;
let fijk_orient: &FaceOrientIJK;
if ijk.k > 0 {
if ijk.j > 0 {
fijk_orient = &FACE_NEIGHBORS[fijk.face as usize][JK_QUADRANT];
} else {
fijk_orient = &FACE_NEIGHBORS[fijk.face as usize][KI_QUADRANT];
if pent_leading_4 {
let mut origin_pent_corner = CoordIJK::default();
_set_ijk(&mut origin_pent_corner, max_dim_base, 0, 0);
let mut tmp_coord = CoordIJK::default();
_ijk_sub(ijk, &origin_pent_corner, &mut tmp_coord);
_ijk_rotate60_cw(&mut tmp_coord);
let ijk_copy_for_add = tmp_coord; _ijk_add(&ijk_copy_for_add, &origin_pent_corner, ijk); }
}
} else {
fijk_orient = &FACE_NEIGHBORS[fijk.face as usize][IJ_QUADRANT];
}
fijk.face = fijk_orient.face;
for _i in 0..fijk_orient.ccw_rot60 {
_ijk_rotate60_ccw(ijk);
}
let mut trans_vec = fijk_orient.translate;
let mut unit_scale = UNIT_SCALE_BY_CII_RES[res as usize];
if substrate {
unit_scale *= 3;
}
_ijk_scale(&mut trans_vec, unit_scale);
let ijk_before_translate = *ijk; _ijk_add(&ijk_before_translate, &trans_vec, ijk);
_ijk_normalize(ijk);
let new_coord_sum = ijk.i + ijk.j + ijk.k;
if substrate && (new_coord_sum == current_max_dim) {
overage_status = Overage::FaceEdge;
}
} else {
}
overage_status
}
#[inline]
pub(crate) fn _adjust_pent_vert_overage(fijk: &mut FaceIJK, res: i32) -> Overage {
let mut overage;
loop {
overage = _adjust_overage_class_ii(fijk, res, false, true); if overage != Overage::NewFace {
break;
}
}
overage
}
pub(crate) fn _face_ijk_pent_to_cell_boundary(h: &FaceIJK, res: i32, start: i32, length: i32, g: &mut CellBoundary) {
let additional_iteration = if length == NUM_PENT_VERTS as i32 { 1 } else { 0 };
let mut adj_res = res;
let mut center_ijk_substrate = *h;
let mut fijk_substrate_verts: [FaceIJK; NUM_PENT_VERTS as usize] = [FaceIJK::default(); NUM_PENT_VERTS as usize];
_face_ijk_pent_to_verts(&mut center_ijk_substrate, &mut adj_res, &mut fijk_substrate_verts);
g.num_verts = 0;
let mut last_fijk_adjusted_geo = FaceIJK::default();
let center_face = h.face;
for vert_idx_loop in 0..(length + additional_iteration) {
let topological_v_idx = (start + vert_idx_loop) % (NUM_PENT_VERTS as i32);
let mut fijk_vert_for_geo = fijk_substrate_verts[topological_v_idx as usize];
let _ = _adjust_pent_vert_overage(&mut fijk_vert_for_geo, adj_res);
if h3_index::is_resolution_class_iii(res) && vert_idx_loop > 0 {
let prev_face = last_fijk_adjusted_geo.face;
let curr_face = fijk_vert_for_geo.face;
let face2 = if prev_face == center_face { curr_face } else { prev_face };
let edge_dir = ADJACENT_FACE_DIR[center_face as usize][face2 as usize];
if edge_dir >= 0 && edge_dir < 4 {
let orient = &FACE_NEIGHBORS[center_face as usize][edge_dir as usize];
let mut tmp = fijk_vert_for_geo;
tmp.face = orient.face;
for _ in 0..orient.ccw_rot60 {
_ijk_rotate60_ccw(&mut tmp.coord);
}
let mut trans = orient.translate;
_ijk_scale(&mut trans, UNIT_SCALE_BY_CII_RES[adj_res as usize] * 3);
let coord_copy = tmp.coord; _ijk_add(&coord_copy, &trans, &mut tmp.coord);
_ijk_normalize(&mut tmp.coord);
let mut prev2d = Vec2d::default();
_ijk_to_hex2d(&last_fijk_adjusted_geo.coord, &mut prev2d);
let mut curr2d = Vec2d::default();
_ijk_to_hex2d(&tmp.coord, &mut curr2d);
let max_dim = MAX_DIM_BY_CII_RES[adj_res as usize] * 3;
let v0 = Vec2d {
x: 3.0 * max_dim as f64,
y: 0.0,
};
let v1 = Vec2d {
x: -1.5 * max_dim as f64,
y: 3.0 * M_SQRT3_2 * max_dim as f64,
};
let v2 = Vec2d {
x: -1.5 * max_dim as f64,
y: -3.0 * M_SQRT3_2 * max_dim as f64,
};
let (e_a, e_b) = match edge_dir as usize {
IJ_QUADRANT => (&v0, &v1),
JK_QUADRANT => (&v1, &v2),
KI_QUADRANT => (&v2, &v0),
_ => (&v0, &v0),
};
let mut inter = Vec2d::default();
_v2d_intersect(&prev2d, &curr2d, e_a, e_b, &mut inter);
if g.num_verts < MAX_CELL_BNDRY_VERTS {
_hex2d_to_geo(&inter, center_face, adj_res, true, &mut g.verts[g.num_verts]);
g.num_verts += 1;
}
}
}
if vert_idx_loop < length {
let mut v2d = Vec2d::default();
_ijk_to_hex2d(&fijk_vert_for_geo.coord, &mut v2d);
_hex2d_to_geo(&v2d, fijk_vert_for_geo.face, adj_res, true, &mut g.verts[g.num_verts]);
g.num_verts += 1;
}
last_fijk_adjusted_geo = fijk_vert_for_geo;
}
}
pub(crate) fn _face_ijk_to_cell_boundary(h: &FaceIJK, res: i32, start: i32, length: i32, g: &mut CellBoundary) {
let mut adj_res = res;
let mut center_ijk_on_face = *h;
let mut fijk_verts: [FaceIJK; NUM_HEX_VERTS as usize] = [FaceIJK::default(); NUM_HEX_VERTS as usize];
_face_ijk_to_verts(&mut center_ijk_on_face, &mut adj_res, &mut fijk_verts);
let additional_iteration = if length == NUM_HEX_VERTS as i32 { 1 } else { 0 };
g.num_verts = 0;
let mut last_fijk_adj_for_distortion = FaceIJK::default(); let mut last_overage_status_for_distortion_check = Overage::NoOverage;
for vert_idx_loop in 0..(length + additional_iteration) {
let topological_v_idx = (start + vert_idx_loop) % (NUM_HEX_VERTS as i32);
let fijk_current_vert_substrate = fijk_verts[topological_v_idx as usize];
let mut fijk_current_vert_adj = fijk_current_vert_substrate; let current_overage_status = _adjust_overage_class_ii(&mut fijk_current_vert_adj, adj_res, false, true);
if h3_index::is_resolution_class_iii(res)
&& vert_idx_loop > 0 && fijk_current_vert_adj.face != last_fijk_adj_for_distortion.face && last_overage_status_for_distortion_check != Overage::FaceEdge
{
let last_topological_v_idx = (start + vert_idx_loop - 1) % (NUM_HEX_VERTS as i32);
let mut v2d_prev_topo_on_center_face = Vec2d::default();
_ijk_to_hex2d(
&fijk_verts[last_topological_v_idx as usize].coord,
&mut v2d_prev_topo_on_center_face,
);
let mut v2d_curr_topo_on_center_face = Vec2d::default();
_ijk_to_hex2d(
&fijk_verts[topological_v_idx as usize].coord,
&mut v2d_curr_topo_on_center_face,
);
let max_dim_substrate = MAX_DIM_BY_CII_RES[adj_res as usize] * 3;
let v0_icosa_edge = Vec2d {
x: 3.0 * max_dim_substrate as f64,
y: 0.0,
};
let v1_icosa_edge = Vec2d {
x: -1.5 * max_dim_substrate as f64,
y: 3.0 * M_SQRT3_2 * max_dim_substrate as f64,
};
let v2_icosa_edge = Vec2d {
x: -1.5 * max_dim_substrate as f64,
y: -3.0 * M_SQRT3_2 * max_dim_substrate as f64,
};
let crossed_to_face = if fijk_current_vert_adj.face != center_ijk_on_face.face {
fijk_current_vert_adj.face
} else {
last_fijk_adj_for_distortion.face
};
let edge_dir_idx = ADJACENT_FACE_DIR[center_ijk_on_face.face as usize][crossed_to_face as usize];
let icosa_edge_va: &Vec2d;
let icosa_edge_vb: &Vec2d;
let mut proceed_with_intersection = true;
match edge_dir_idx {
ij_quad if ij_quad == IJ_QUADRANT as i32 => {
icosa_edge_va = &v0_icosa_edge;
icosa_edge_vb = &v1_icosa_edge;
}
jk_quad if jk_quad == JK_QUADRANT as i32 => {
icosa_edge_va = &v1_icosa_edge;
icosa_edge_vb = &v2_icosa_edge;
}
ki_quad if ki_quad == KI_QUADRANT as i32 => {
icosa_edge_va = &v2_icosa_edge;
icosa_edge_vb = &v0_icosa_edge;
}
_ => {
proceed_with_intersection = false;
icosa_edge_va = &v0_icosa_edge; icosa_edge_vb = &v0_icosa_edge;
}
}
if proceed_with_intersection {
let mut intersection_hex2d = Vec2d::default();
_v2d_intersect(
&v2d_prev_topo_on_center_face,
&v2d_curr_topo_on_center_face,
icosa_edge_va, icosa_edge_vb,
&mut intersection_hex2d,
);
if !_v2d_almost_equals(&v2d_prev_topo_on_center_face, &intersection_hex2d)
&& !_v2d_almost_equals(&v2d_curr_topo_on_center_face, &intersection_hex2d)
{
if g.num_verts < MAX_CELL_BNDRY_VERTS {
_hex2d_to_geo(
&intersection_hex2d,
center_ijk_on_face.face,
adj_res,
true,
&mut g.verts[g.num_verts],
);
g.num_verts += 1;
} else {
}
} else {
}
} } else {
}
if vert_idx_loop < length {
if g.num_verts < MAX_CELL_BNDRY_VERTS {
let mut vec_for_geo = Vec2d::default();
_ijk_to_hex2d(&fijk_current_vert_adj.coord, &mut vec_for_geo);
_hex2d_to_geo(
&vec_for_geo,
fijk_current_vert_adj.face,
adj_res,
true,
&mut g.verts[g.num_verts],
);
g.num_verts += 1;
} else {
}
}
last_fijk_adj_for_distortion = fijk_current_vert_adj;
last_overage_status_for_distortion_check = current_overage_status;
}
}
pub(crate) fn _face_ijk_to_verts(
fijk: &mut FaceIJK,
res: &mut i32,
fijk_verts: &mut [FaceIJK; NUM_HEX_VERTS as usize],
) {
#[rustfmt::skip]
const VERTS_CII: [CoordIJK; NUM_HEX_VERTS as usize] = [
CoordIJK { i: 2, j: 1, k: 0 }, CoordIJK { i: 1, j: 2, k: 0 },
CoordIJK { i: 0, j: 2, k: 1 }, CoordIJK { i: 0, j: 1, k: 2 },
CoordIJK { i: 1, j: 0, k: 2 }, CoordIJK { i: 2, j: 0, k: 1 },
];
#[rustfmt::skip]
const VERTS_CIII: [CoordIJK; NUM_HEX_VERTS as usize] = [
CoordIJK { i: 5, j: 4, k: 0 }, CoordIJK { i: 1, j: 5, k: 0 },
CoordIJK { i: 0, j: 5, k: 4 }, CoordIJK { i: 0, j: 1, k: 5 },
CoordIJK { i: 4, j: 0, k: 5 }, CoordIJK { i: 5, j: 0, k: 1 },
];
let verts_ref = if h3_index::is_resolution_class_iii(*res) {
&VERTS_CIII
} else {
&VERTS_CII
};
_down_ap3(&mut fijk.coord);
_down_ap3r(&mut fijk.coord);
if h3_index::is_resolution_class_iii(*res) {
_down_ap7r(&mut fijk.coord);
*res += 1;
}
for v_idx in 0..(NUM_HEX_VERTS as usize) {
fijk_verts[v_idx].face = fijk.face;
_ijk_add(&fijk.coord, &verts_ref[v_idx], &mut fijk_verts[v_idx].coord);
_ijk_normalize(&mut fijk_verts[v_idx].coord);
}
}
pub(crate) fn _face_ijk_pent_to_verts(
fijk: &mut FaceIJK,
res: &mut i32,
fijk_verts: &mut [FaceIJK; NUM_PENT_VERTS as usize],
) {
#[rustfmt::skip]
const VERTS_CII_PENT: [CoordIJK; NUM_PENT_VERTS as usize] = [
CoordIJK { i: 2, j: 1, k: 0 }, CoordIJK { i: 1, j: 2, k: 0 },
CoordIJK { i: 0, j: 2, k: 1 }, CoordIJK { i: 0, j: 1, k: 2 },
CoordIJK { i: 1, j: 0, k: 2 },
];
#[rustfmt::skip]
const VERTS_CIII_PENT: [CoordIJK; NUM_PENT_VERTS as usize] = [
CoordIJK { i: 5, j: 4, k: 0 }, CoordIJK { i: 1, j: 5, k: 0 },
CoordIJK { i: 0, j: 5, k: 4 }, CoordIJK { i: 0, j: 1, k: 5 },
CoordIJK { i: 4, j: 0, k: 5 },
];
let verts_ref = if h3_index::is_resolution_class_iii(*res) {
&VERTS_CIII_PENT
} else {
&VERTS_CII_PENT
};
_down_ap3(&mut fijk.coord);
_down_ap3r(&mut fijk.coord);
if h3_index::is_resolution_class_iii(*res) {
_down_ap7r(&mut fijk.coord);
*res += 1;
}
for v_idx in 0..(NUM_PENT_VERTS as usize) {
fijk_verts[v_idx].face = fijk.face;
_ijk_add(&fijk.coord, &verts_ref[v_idx], &mut fijk_verts[v_idx].coord);
_ijk_normalize(&mut fijk_verts[v_idx].coord);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::constants::{EPSILON_DEG, EPSILON_RAD, M_PI_180, M_PI_2, NUM_HEX_VERTS, NUM_PENT_VERTS};
use crate::coords::ijk::_ijk_matches;
use crate::latlng::{_set_geo_degs, geo_almost_equal_threshold};
use crate::types::LatLng;
fn vec2d_almost_equals_threshold(v1: &Vec2d, v2: &Vec2d, threshold: f64) -> bool {
(v1.x - v2.x).abs() < threshold && (v1.y - v2.y).abs() < threshold
}
#[test]
fn test_geo_to_hex2d_exact() {
for f in 0..(NUM_ICOSA_FACES as usize) {
let mut face_calc: i32 = -1;
let mut v_calc = Vec2d::default();
_geo_to_hex2d(&FACE_CENTER_GEO[f], 0, &mut face_calc, &mut v_calc);
assert_eq!(face_calc, f as i32, "Face center {} should be on its own face", f);
assert!(
vec2d_almost_equals_threshold(&v_calc, &Vec2d { x: 0.0, y: 0.0 }, EPSILON),
"Face center {} should be at 0,0",
f
);
}
let mut p = LatLng::default();
_set_geo_degs(&mut p, 30.0, 30.0);
let mut face: i32 = -1;
let mut v = Vec2d::default();
_geo_to_hex2d(&p, 5, &mut face, &mut v);
assert!(face != -1, "Should find a face for (30,30)");
}
#[test]
fn test_hex2d_to_geo_roundtrip() {
for f_orig_idx in 0..(NUM_ICOSA_FACES as usize) {
for &res_orig_val in &[0, 1, 5] {
let res_orig = res_orig_val as i32;
let v_orig: Vec2d;
if res_orig == 0 {
v_orig = Vec2d { x: 0.0, y: 0.0 };
} else {
v_orig = Vec2d {
x: 0.1 * (f_orig_idx + 1) as f64,
y: -0.05 * (f_orig_idx + 1) as f64,
};
}
let mut geo_intermediate = LatLng::default();
_hex2d_to_geo(&v_orig, f_orig_idx as i32, res_orig, false, &mut geo_intermediate);
let mut f_roundtrip: i32 = -1;
let mut v_roundtrip = Vec2d::default();
_geo_to_hex2d(&geo_intermediate, res_orig, &mut f_roundtrip, &mut v_roundtrip);
assert_eq!(
f_roundtrip, f_orig_idx as i32,
"Roundtrip face mismatch res {}",
res_orig
);
let threshold = match res_orig {
0 => EPSILON,
1 => EPSILON * 1_000.0,
_ => EPSILON * 1_000_000.0,
};
assert!(
vec2d_almost_equals_threshold(&v_orig, &v_roundtrip, threshold),
"Roundtrip Vec2d mismatch res {}",
res_orig
);
}
}
}
#[test]
fn test_geo_to_closest_face_poles() {
let north_pole = LatLng { lat: M_PI_2, lng: 0.0 };
let south_pole = LatLng { lat: -M_PI_2, lng: 0.0 };
let mut face: i32 = -1;
let mut sqd: f64 = -1.0;
_geo_to_closest_face(&north_pole, &mut face, &mut sqd);
assert!(
face >= 0 && face < NUM_ICOSA_FACES as i32,
"North pole has a closest face"
);
assert!(
face == 1 || face == 0 || face == 2 || face == 3 || face == 4,
"North pole closest face got: {}",
face
);
_geo_to_closest_face(&south_pole, &mut face, &mut sqd);
assert!(
face >= 0 && face < NUM_ICOSA_FACES as i32,
"South pole has a closest face"
);
assert!(face >= 15 && face <= 19, "South pole closest face got: {}", face);
}
#[test]
fn test_face_ijk_to_geo_roundtrip() {
for f_orig in 0..(NUM_ICOSA_FACES as i32) {
for res_orig in 0..=3 {
let ijk_orig = CoordIJK {
i: res_orig + 1,
j: res_orig / 2,
k: 0,
};
let mut fijk_orig = FaceIJK {
face: f_orig,
coord: ijk_orig,
};
_ijk_normalize(&mut fijk_orig.coord);
let mut geo_intermediate = LatLng::default();
_face_ijk_to_geo(&fijk_orig, res_orig, &mut geo_intermediate);
let mut fijk_roundtrip = FaceIJK::default();
_geo_to_face_ijk(&geo_intermediate, res_orig, &mut fijk_roundtrip);
assert_eq!(
fijk_roundtrip.face, fijk_orig.face,
"Roundtrip FaceIJK face mismatch res {}",
res_orig
);
let mut v_orig_check = Vec2d::default();
_ijk_to_hex2d(&fijk_orig.coord, &mut v_orig_check);
let mut v_roundtrip_check = Vec2d::default();
_ijk_to_hex2d(&fijk_roundtrip.coord, &mut v_roundtrip_check);
assert!(
vec2d_almost_equals_threshold(&v_orig_check, &v_roundtrip_check, EPSILON_DEG * M_PI_180 * 10.0),
"Roundtrip FaceIJK IJK mismatch res {}",
res_orig
);
let mut geo_from_roundtrip_fijk = LatLng::default();
_face_ijk_to_geo(&fijk_roundtrip, res_orig, &mut geo_from_roundtrip_fijk);
assert!(
geo_almost_equal_threshold(&geo_intermediate, &geo_from_roundtrip_fijk, EPSILON_RAD),
"Geo coords from roundtripped FaceIJK mismatch res {}",
res_orig
);
}
}
}
#[test]
fn test_geo_to_face_ijk_face_centers() {
for f in 0..(NUM_ICOSA_FACES as usize) {
let center_geo = FACE_CENTER_GEO[f];
let mut fijk_calc = FaceIJK::default();
for res in 0..=MAX_H3_RES {
_geo_to_face_ijk(¢er_geo, res, &mut fijk_calc);
assert_eq!(fijk_calc.face, f as i32, "Geo center face {} res {} mismatch", f, res);
let expected_ijk = CoordIJK { i: 0, j: 0, k: 0 };
assert!(
_ijk_matches(&fijk_calc.coord, &expected_ijk),
"Geo center IJK mismatch face {} res {}",
f,
res
);
}
}
}
#[test]
fn test_adjust_overage_class_ii_noop() {
let mut fijk = FaceIJK {
face: 1,
coord: CoordIJK { i: 0, j: 0, k: 0 },
};
let res = 2;
let overage = _adjust_overage_class_ii(&mut fijk, res, false, false);
assert_eq!(overage, Overage::NoOverage, "No overage for center coord");
assert_eq!(fijk.face, 1, "Face should not change");
assert!(
_ijk_matches(&fijk.coord, &CoordIJK { i: 0, j: 0, k: 0 }),
"Coord should not change"
);
let mut fijk_on_edge = FaceIJK {
face: 1,
coord: CoordIJK { i: 42, j: 0, k: 0 },
}; let overage_edge = _adjust_overage_class_ii(&mut fijk_on_edge, res, false, true);
assert_eq!(overage_edge, Overage::FaceEdge, "On edge for substrate grid");
assert_eq!(fijk_on_edge.face, 1, "Face should not change for on-edge");
assert!(
_ijk_matches(&fijk_on_edge.coord, &CoordIJK { i: 42, j: 0, k: 0 }),
"Coord should not change for on-edge"
);
}
#[test]
fn test_adjust_overage_class_ii_new_face() {
let mut fijk = FaceIJK {
face: 0,
coord: CoordIJK { i: 3, j: 0, k: 0 },
};
let res = 0;
let overage = _adjust_overage_class_ii(&mut fijk, res, false, false);
assert_eq!(overage, Overage::NewFace, "Should be NewFace overage");
assert_eq!(fijk.face, 4, "Face should change to 4");
let expected_coord = CoordIJK { i: 3, j: 1, k: 0 };
assert!(
_ijk_matches(&fijk.coord, &expected_coord),
"Coord adjusted. Expected {:?}, got {:?}",
expected_coord,
fijk.coord
);
}
#[test]
fn test_adjust_overage_pent_leading_4() {
let mut fijk_pent_overage = FaceIJK {
face: 0,
coord: CoordIJK { i: 1, j: 0, k: 2 },
};
let res = 0;
let overage = _adjust_overage_class_ii(&mut fijk_pent_overage, res, true, false);
assert_eq!(overage, Overage::NewFace, "Pentagon leading 4 overage");
let expected_final_coord = CoordIJK { i: 3, j: 3, k: 0 };
assert_eq!(fijk_pent_overage.face, 1, "Pentagon leading 4, new face should be 1");
assert!(
_ijk_matches(&fijk_pent_overage.coord, &expected_final_coord),
"Pentagon leading 4, coord adjusted. Expected {:?}, got {:?}",
expected_final_coord,
fijk_pent_overage.coord
);
}
#[test]
fn test_adjust_pent_vert_overage() {
let mut fijk = FaceIJK {
face: 0,
coord: CoordIJK { i: 43, j: 0, k: 0 },
}; let res = 2;
let overage_status = _adjust_pent_vert_overage(&mut fijk, res);
assert_ne!(
overage_status,
Overage::NewFace,
"Should not be NewFace after multiple adjustments"
);
}
#[test]
fn test_face_ijk_to_cell_boundary_hexagon() {
let mut fijk = FaceIJK {
face: 1,
coord: CoordIJK { i: 1, j: 1, k: 0 },
};
_ijk_normalize(&mut fijk.coord);
let res = 2; let mut boundary = CellBoundary::default();
_face_ijk_to_cell_boundary(&fijk, res, 0, NUM_HEX_VERTS as i32, &mut boundary);
assert_eq!(
boundary.num_verts, NUM_HEX_VERTS as usize,
"Hexagon boundary should have 6 verts"
);
}
#[test]
fn test_face_ijk_to_cell_boundary_pentagon_class_iii() {
let fijk_pent = FaceIJK {
face: 0,
coord: CoordIJK { i: 2, j: 0, k: 0 },
}; let res = 1; let mut boundary = CellBoundary::default();
_face_ijk_pent_to_cell_boundary(&fijk_pent, res, 0, NUM_PENT_VERTS as i32, &mut boundary);
assert_eq!(
boundary.num_verts, 10,
"Class III pentagon boundary should have 10 verts (with distortion)"
);
}
#[test]
fn test_face_ijk_to_cell_boundary_pentagon_class_ii() {
let res2_pent_fijk = FaceIJK {
face: 0,
coord: CoordIJK { i: 14, j: 0, k: 0 },
}; let res = 2; let mut boundary = CellBoundary::default();
_face_ijk_pent_to_cell_boundary(&res2_pent_fijk, res, 0, NUM_PENT_VERTS as i32, &mut boundary);
assert_eq!(
boundary.num_verts, NUM_PENT_VERTS as usize,
"Class II pentagon boundary should have 5 verts"
);
}
#[test]
fn test_face_ijk_to_verts_and_pent_to_verts() {
let mut fijk_hex = FaceIJK {
face: 0,
coord: CoordIJK { i: 1, j: 1, k: 0 },
};
let mut res_hex: i32 = 2;
let mut verts_hex: [FaceIJK; NUM_HEX_VERTS as usize] = [Default::default(); NUM_HEX_VERTS as usize];
_face_ijk_to_verts(&mut fijk_hex, &mut res_hex, &mut verts_hex);
for vert in verts_hex.iter() {
assert_ne!(vert.face, -1, "Hex vertex valid face");
}
let mut fijk_pent = FaceIJK {
face: 0,
coord: CoordIJK { i: 2, j: 0, k: 0 },
};
let mut res_pent: i32 = 1; let mut verts_pent: [FaceIJK; NUM_PENT_VERTS as usize] = [Default::default(); NUM_PENT_VERTS as usize];
_face_ijk_pent_to_verts(&mut fijk_pent, &mut res_pent, &mut verts_pent);
assert_eq!(res_pent, 2, "Pentagon Class III res adjusted");
for vert in verts_pent.iter() {
assert_ne!(vert.face, -1, "Pent vertex valid face");
}
}
}