Variable: dggs
constdggs:object
Defined in: modules/geospatial/dggs/dggs.ts:9
WGSL helpers for 64-bit DGGS cell IDs encoded as two u32 words.
Type Declaration
name
readonlyname:"dggs"='dggs'
source
readonlysource: "const DGGS_H3_CELL_MODE: u32 = 1u;\nconst DGGS_H3_MAX_RESOLUTION: u32 = 15u;\nconst DGGS_H3_MAX_BASE_CELL: u32 = 121u;\nconst DGGS_H3_UNUSED_DIGIT: u32 = 7u;\nconst DGGS_H3_RES0_U_GNOMONIC: f32 = 0.381966011250105;\nconst DGGS_H3_RSQRT7: f32 = 0.3779644730092272;\nconst DGGS_H3_ONETHIRD: f32 = 0.3333333333333333;\nconst DGGS_H3_AP7_ROT_RADS: f32 = 0.3334731722518321;\nconst DGGS_H3_SQRT3_2: f32 = 0.8660254037844386;\nconst DGGS_S2_MAX_LEVEL: u32 = 30u;\nconst DGGS_GEOHASH_MAX_LENGTH: u32 = 12u;\nconst DGGS_GEOHASH_LENGTH_BIT_OFFSET: u32 = 60u;\nconst DGGS_QUADKEY_MAX_LENGTH: u32 = 29u;\nconst DGGS_QUADKEY_LENGTH_BIT_OFFSET: u32 = 58u;\nconst DGGS_A5_FIRST_HILBERT_RESOLUTION: u32 = 2u;\nconst DGGS_A5_MAX_RESOLUTION: u32 = 30u;\nconst DGGS_A5_HILBERT_START_BIT: u32 = 58u;\nconst DGGS_PI: f32 = 3.141592653589793;\nconst DGGS_TWO_PI: f32 = 6.283185307179586;\nconst DGGS_TWO_PI_OVER_5: f32 = 1.2566370614359172;\nconst DGGS_PI_OVER_5: f32 = 0.6283185307179586;\nconst DGGS_PI_OVER_2: f32 = 1.5707963267948966;\nconst DGGS_A5_DISTANCE_TO_EDGE: f32 = 0.6180339887498949;\nconst DGGS_A5_REFLECTED_TRIANGLE_SCALE: f32 = 3.23606797749979;\nconst DGGS_A5_LONGITUDE_OFFSET: f32 = 93.0;\nconst DGGS_RADIANS_TO_DEGREES: f32 = 57.29577951308232;\nconst DGGS_DEGREES_TO_RADIANS: f32 = 0.017453292519943295;\nconst DGGS_U64_HIGH_WORD_FLOAT_SCALE: f32 = 4294967296.0;\n\n// DGGS 64-bit helper functions use canonical word order vec2u(high, low).\n// Arrow BigInt64/BigUint64 buffers are read as little-endian vec2u(low, high),\n// so storage-buffer reads should pass through dggs_u64_from_little_endian_words().\nfn dggs_u64_make(high: u32, low: u32) -> vec2u {\n return vec2u(high, low);\n}\n\nfn dggs_u64_from_little_endian_words(words: vec2u) -> vec2u {\n return words.yx;\n}\n\nfn dggs_u64_to_little_endian_words(value: vec2u) -> vec2u {\n return value.yx;\n}\n\nfn dggs_u64_high(value: vec2u) -> u32 {\n return value.x;\n}\n\nfn dggs_u64_low(value: vec2u) -> u32 {\n return value.y;\n}\n\nfn dggs_u64_is_zero(value: vec2u) -> bool {\n return value.x == 0u && value.y == 0u;\n}\n\nfn dggs_u64_equal(a: vec2u, b: vec2u) -> bool {\n return a.x == b.x && a.y == b.y;\n}\n\nfn dggs_u64_compare(a: vec2u, b: vec2u) -> i32 {\n if (a.x != b.x) {\n return select(-1, 1, a.x > b.x);\n }\n if (a.y != b.y) {\n return select(-1, 1, a.y > b.y);\n }\n return 0;\n}\n\nfn dggs_u64_less(a: vec2u, b: vec2u) -> bool {\n return dggs_u64_compare(a, b) < 0;\n}\n\nfn dggs_u64_add(a: vec2u, b: vec2u) -> vec2u {\n let low = a.y + b.y;\n let carry = select(0u, 1u, low < a.y);\n return vec2u(a.x + b.x + carry, low);\n}\n\nfn dggs_u64_subtract(a: vec2u, b: vec2u) -> vec2u {\n let borrow = select(0u, 1u, a.y < b.y);\n return vec2u(a.x - b.x - borrow, a.y - b.y);\n}\n\nfn dggs_u64_to_f32(value: vec2u) -> f32 {\n return f32(value.x) * DGGS_U64_HIGH_WORD_FLOAT_SCALE + f32(value.y);\n}\n\nfn dggs_i64_from_little_endian_words(words: vec2u) -> vec2u {\n return dggs_u64_from_little_endian_words(words);\n}\n\nfn dggs_i64_to_little_endian_words(value: vec2u) -> vec2u {\n return dggs_u64_to_little_endian_words(value);\n}\n\nfn dggs_i64_is_negative(value: vec2u) -> bool {\n return (value.x & 0x80000000u) != 0u;\n}\n\nfn dggs_i64_negate(value: vec2u) -> vec2u {\n return dggs_u64_add(vec2u(~value.x, ~value.y), vec2u(0u, 1u));\n}\n\nfn dggs_i64_compare(a: vec2u, b: vec2u) -> i32 {\n let highA = bitcast<i32>(a.x);\n let highB = bitcast<i32>(b.x);\n if (highA != highB) {\n return select(-1, 1, highA > highB);\n }\n if (a.y != b.y) {\n return select(-1, 1, a.y > b.y);\n }\n return 0;\n}\n\nfn dggs_i64_less(a: vec2u, b: vec2u) -> bool {\n return dggs_i64_compare(a, b) < 0;\n}\n\nfn dggs_i64_subtract(a: vec2u, b: vec2u) -> vec2u {\n return dggs_u64_subtract(a, b);\n}\n\nfn dggs_i64_to_f32(value: vec2u) -> f32 {\n if (dggs_i64_is_negative(value)) {\n return -dggs_u64_to_f32(dggs_i64_negate(value));\n }\n return dggs_u64_to_f32(value);\n}\n\nfn dggs_u32_mask_low(bitCount: u32) -> u32 {\n if (bitCount == 0u) {\n return 0u;\n }\n if (bitCount >= 32u) {\n return 0xffffffffu;\n }\n return (1u << bitCount) - 1u;\n}\n\nfn dggs_u64_shift_left(value: vec2u, shift: u32) -> vec2u {\n if (shift == 0u) {\n return value;\n }\n if (shift < 32u) {\n return vec2u((value.x << shift) | (value.y >> (32u - shift)), value.y << shift);\n }\n if (shift == 32u) {\n return vec2u(value.y, 0u);\n }\n if (shift < 64u) {\n return vec2u(value.y << (shift - 32u), 0u);\n }\n return vec2u(0u);\n}\n\nfn dggs_u64_shift_right(value: vec2u, shift: u32) -> vec2u {\n if (shift == 0u) {\n return value;\n }\n if (shift < 32u) {\n return vec2u(value.x >> shift, (value.y >> shift) | (value.x << (32u - shift)));\n }\n if (shift == 32u) {\n return vec2u(0u, value.x);\n }\n if (shift < 64u) {\n return vec2u(0u, value.x >> (shift - 32u));\n }\n return vec2u(0u);\n}\n\nfn dggs_u64_extract_bits(value: vec2u, bitOffset: u32, bitCount: u32) -> u32 {\n if (bitCount == 0u || bitOffset >= 64u) {\n return 0u;\n }\n let shifted = dggs_u64_shift_right(value, bitOffset);\n return shifted.y & dggs_u32_mask_low(bitCount);\n}\n\nfn dggs_u64_set_bits(value: vec2u, bitOffset: u32, bitCount: u32, bits: u32) -> vec2u {\n if (bitCount == 0u || bitOffset >= 64u) {\n return value;\n }\n let bitMask = dggs_u32_mask_low(bitCount);\n let fieldMask = dggs_u64_shift_left(vec2u(0u, bitMask), bitOffset);\n let fieldBits = dggs_u64_shift_left(vec2u(0u, bits & bitMask), bitOffset);\n return (value & vec2u(~fieldMask.x, ~fieldMask.y)) | fieldBits;\n}\n\nfn dggs_u64_count_trailing_zeros(value: vec2u) -> u32 {\n if (value.y != 0u) {\n return countTrailingZeros(value.y);\n }\n if (value.x != 0u) {\n return 32u + countTrailingZeros(value.x);\n }\n return 64u;\n}\n\nstruct DggsA5Cell {\n origin : u32,\n segment : u32,\n resolution : u32,\n valid : u32,\n quintantShift : u32,\n hilbertResolution : u32,\n};\n\nstruct DggsA5Anchor {\n q : u32,\n offset : vec2i,\n flips : vec2i,\n orientation : u32,\n quintant : u32,\n};\n\nstruct DggsA5FaceTriangle {\n a : vec2f,\n b : vec2f,\n c : vec2f,\n};\n\nstruct DggsA5SphericalTriangle {\n a : vec3f,\n b : vec3f,\n c : vec3f,\n};\n\nstruct DggsH3FaceIJK {\n face : u32,\n coord : vec3i,\n valid : u32,\n};\n\nstruct DggsH3FaceIJKOverage {\n faceIJK : DggsH3FaceIJK,\n overage : u32,\n};\n\nstruct DggsH3BoundaryVertex {\n face : u32,\n coord : vec3i,\n resolution : u32,\n valid : u32,\n};\n\nfn dggs_boundary_point_to_fp64_split(point : vec2f) -> vec4f {\n return vec4f(point.x, point.y, 0.0, 0.0);\n}\n\nfn dggs_a5_positive_mod_i32(value : i32, modulus : i32) -> i32 {\n return ((value % modulus) + modulus) % modulus;\n}\n\nfn dggs_a5_has_bit(index : vec2u, bitOffset : u32) -> bool {\n return dggs_u64_extract_bits(index, bitOffset, 1u) != 0u;\n}\n\nfn dggs_a5_get_resolution(index : vec2u) -> u32 {\n if (dggs_u64_is_zero(index)) {\n return 0u;\n }\n if (\n dggs_a5_has_bit(index, 0u) ||\n dggs_u64_extract_bits(index, 0u, 3u) == 4u ||\n dggs_u64_extract_bits(index, 0u, 5u) == 16u\n ) {\n return DGGS_A5_MAX_RESOLUTION;\n }\n\n var resolution = DGGS_A5_MAX_RESOLUTION - 1u;\n var shifted = dggs_u64_shift_right(index, 1u);\n if (dggs_u64_is_zero(shifted)) {\n return 0u;\n }\n\n var remaining = shifted.y;\n if (remaining == 0u) {\n shifted = dggs_u64_shift_right(shifted, 32u);\n resolution -= 16u;\n remaining = shifted.y;\n }\n if ((remaining & 0xffffu) == 0u) {\n remaining >>= 16u;\n resolution -= 8u;\n }\n if (resolution >= 6u && (remaining & 0xffu) == 0u) {\n remaining >>= 8u;\n resolution -= 4u;\n }\n if (resolution >= 4u && (remaining & 0xfu) == 0u) {\n remaining >>= 4u;\n resolution -= 2u;\n }\n loop {\n if ((remaining & 1u) != 0u) {\n break;\n }\n if (resolution == 0u) {\n break;\n }\n resolution -= 1u;\n remaining >>= select(2u, 1u, resolution < DGGS_A5_FIRST_HILBERT_RESOLUTION);\n }\n return resolution;\n}\n\nfn dggs_a5_get_origin_first_quintant(origin : u32) -> u32 {\n let values = array<u32, 12>(\n 4u, 2u, 3u, 0u, 2u, 4u, 2u, 2u, 3u, 0u, 3u, 0u\n );\n return values[min(origin, 11u)];\n}\n\nfn dggs_a5_get_origin_step(origin : u32) -> i32 {\n let values = array<i32, 12>(\n -1, 1, 1, 1, -1, 1, -1, -1, 1, 1, 1, -1\n );\n return values[min(origin, 11u)];\n}\n\nfn dggs_a5_get_origin_orientation(origin : u32, faceRelativeQuintant : u32) -> u32 {\n // Orientation codes: uv=0, vu=1, uw=2, wu=3, vw=4, wv=5.\n let values = array<u32, 60>(\n 1u, 2u, 4u, 4u, 4u,\n 1u, 0u, 5u, 3u, 2u,\n 3u, 0u, 5u, 3u, 2u,\n 3u, 0u, 5u, 3u, 2u,\n 3u, 2u, 4u, 1u, 2u,\n 1u, 0u, 5u, 3u, 2u,\n 3u, 2u, 4u, 1u, 2u,\n 3u, 2u, 4u, 1u, 2u,\n 3u, 0u, 5u, 3u, 2u,\n 1u, 0u, 5u, 3u, 2u,\n 1u, 0u, 5u, 3u, 2u,\n 3u, 2u, 4u, 1u, 2u\n );\n return values[min(origin, 11u) * 5u + min(faceRelativeQuintant, 4u)];\n}\n\nfn dggs_a5_get_origin_angle(origin : u32) -> f32 {\n if (origin == 0u || origin == 9u) {\n return 0.0;\n }\n return DGGS_PI_OVER_5;\n}\n\nfn dggs_a5_get_origin_quaternion(origin : u32) -> vec4f {\n let values = array<vec4f, 12>(\n vec4f(0.0, 0.0, 0.0, 1.0),\n vec4f(0.0, 0.525731112119, 0.0, 0.850650808352),\n vec4f(-0.5, 0.688190960236, 0.0, 0.525731112119),\n vec4f(-0.809016994375, -0.262865556060, 0.0, 0.525731112119),\n vec4f(-0.5, 0.162459848116, 0.0, 0.850650808352),\n vec4f(-0.309016994375, -0.425325404176, 0.0, 0.850650808352),\n vec4f(0.309016994375, -0.425325404176, 0.0, 0.850650808352),\n vec4f(0.809016994375, -0.262865556060, 0.0, 0.525731112119),\n vec4f(0.0, -0.850650808352, 0.0, 0.525731112119),\n vec4f(0.0, -1.0, 0.0, 0.0),\n vec4f(0.5, 0.688190960236, 0.0, 0.525731112119),\n vec4f(0.5, 0.162459848116, 0.0, 0.850650808352)\n );\n return values[min(origin, 11u)];\n}\n\nfn dggs_a5_quaternion_rotate(point : vec3f, quaternion : vec4f) -> vec3f {\n let q = quaternion.xyz;\n let t = 2.0 * cross(q, point);\n return point + quaternion.w * t + cross(q, t);\n}\n\nfn dggs_a5_deserialize(index : vec2u) -> DggsA5Cell {\n if (dggs_u64_is_zero(index)) {\n return DggsA5Cell(0u, 0u, 0u, 0u, DGGS_A5_HILBERT_START_BIT, 0u);\n }\n\n let resolution = dggs_a5_get_resolution(index);\n var quintantShift = DGGS_A5_HILBERT_START_BIT;\n var quintantOffset = 0u;\n if (resolution == DGGS_A5_MAX_RESOLUTION) {\n var markerBits = 5u;\n if (dggs_a5_has_bit(index, 0u)) {\n markerBits = 1u;\n } else if (dggs_a5_has_bit(index, 2u)) {\n markerBits = 3u;\n }\n quintantShift = DGGS_A5_HILBERT_START_BIT + markerBits;\n quintantOffset = select(select(40u, 32u, markerBits == 3u), 0u, markerBits == 1u);\n }\n\n let topBits = dggs_u64_shift_right(index, quintantShift).y + quintantOffset;\n if (resolution == 0u) {\n if (topBits >= 12u) {\n return DggsA5Cell(0u, 0u, resolution, 0u, quintantShift, 0u);\n }\n return DggsA5Cell(topBits, 0u, resolution, 1u, quintantShift, 0u);\n }\n\n let origin = topBits / 5u;\n if (origin >= 12u) {\n return DggsA5Cell(0u, 0u, resolution, 0u, quintantShift, 0u);\n }\n let segment = (topBits + dggs_a5_get_origin_first_quintant(origin)) % 5u;\n let hilbertResolution = select(0u, resolution - 1u, resolution >= 2u);\n return DggsA5Cell(origin, segment, resolution, 1u, quintantShift, hilbertResolution);\n}\n\nfn dggs_a5_segment_to_anchor_info(cell : DggsA5Cell) -> DggsA5Anchor {\n let firstQuintant = dggs_a5_get_origin_first_quintant(cell.origin);\n let faceRelativeQuintant = (cell.segment + 5u - firstQuintant) % 5u;\n let orientation = dggs_a5_get_origin_orientation(cell.origin, faceRelativeQuintant);\n let step = dggs_a5_get_origin_step(cell.origin);\n let quintant = u32(dggs_a5_positive_mod_i32(\n i32(firstQuintant) + step * i32(faceRelativeQuintant),\n 5\n ));\n return DggsA5Anchor(0u, vec2i(0), vec2i(1), orientation, quintant);\n}\n\nfn dggs_a5_is_reverse_orientation(orientation : u32) -> bool {\n return orientation == 1u || orientation == 3u || orientation == 4u;\n}\n\nfn dggs_a5_is_invert_j_orientation(orientation : u32) -> bool {\n return orientation == 5u || orientation == 4u;\n}\n\nfn dggs_a5_is_flip_ij_orientation(orientation : u32) -> bool {\n return orientation == 3u || orientation == 2u;\n}\n\nfn dggs_a5_pattern_value(index : u32, flipIj : bool) -> u32 {\n let pattern = array<u32, 8>(0u, 1u, 3u, 4u, 5u, 6u, 7u, 2u);\n let flippedPattern = array<u32, 8>(0u, 1u, 2u, 7u, 3u, 4u, 5u, 6u);\n return select(pattern[min(index, 7u)], flippedPattern[min(index, 7u)], flipIj);\n}\n\nfn dggs_a5_quaternary_to_flips(digit : u32) -> vec2i {\n if (digit == 1u) {\n return vec2i(1, -1);\n }\n if (digit == 3u) {\n return vec2i(-1, 1);\n }\n return vec2i(1, 1);\n}\n\nfn dggs_a5_quaternary_to_kj(digit : u32, flips : vec2i) -> vec2i {\n var p = vec2i(0);\n var q = vec2i(0);\n if (flips.x == 1 && flips.y == 1) {\n p = vec2i(1, 0);\n q = vec2i(0, 1);\n } else if (flips.x == -1 && flips.y == 1) {\n p = vec2i(0, -1);\n q = vec2i(-1, 0);\n } else if (flips.x == 1 && flips.y == -1) {\n p = vec2i(0, 1);\n q = vec2i(1, 0);\n } else {\n p = vec2i(-1, 0);\n q = vec2i(0, -1);\n }\n\n if (digit == 1u) {\n return p;\n }\n if (digit == 2u) {\n return p + q;\n }\n if (digit == 3u) {\n return q + 2 * p;\n }\n return vec2i(0);\n}\n\nfn dggs_a5_kj_to_ij(kj : vec2i) -> vec2i {\n return vec2i(kj.x - kj.y, kj.y);\n}\n\nfn dggs_a5_shift_digits(\n digits : ptr<function, array<u32, 29>>,\n digitIndex : u32,\n flips : vec2i,\n invertJ : bool,\n flipIj : bool\n) {\n if (digitIndex == 0u) {\n return;\n }\n let parentK = (*digits)[digitIndex];\n let childK = (*digits)[digitIndex - 1u];\n let flipSum = flips.x + flips.y;\n var needsShift = true;\n var first = true;\n if (invertJ != (flipSum == 0)) {\n needsShift = parentK == 1u || parentK == 2u;\n first = parentK == 1u;\n } else {\n needsShift = parentK < 2u;\n first = parentK == 0u;\n }\n if (!needsShift) {\n return;\n }\n let source = select(childK + 4u, childK, first);\n let destination = dggs_a5_pattern_value(source, flipIj);\n (*digits)[digitIndex - 1u] = destination % 4u;\n (*digits)[digitIndex] = u32(dggs_a5_positive_mod_i32(\n i32(parentK) + 4 + i32(destination / 4u) - i32(source / 4u),\n 4\n ));\n}\n\nfn dggs_a5_make_anchor(index : vec2u, cell : DggsA5Cell, anchorInfo : DggsA5Anchor) -> DggsA5Anchor {\n if (cell.hilbertResolution == 0u) {\n return anchorInfo;\n }\n\n let reverse = dggs_a5_is_reverse_orientation(anchorInfo.orientation);\n let invertJ = dggs_a5_is_invert_j_orientation(anchorInfo.orientation);\n let flipIj = dggs_a5_is_flip_ij_orientation(anchorInfo.orientation);\n let hilbertBits = 2u * cell.hilbertResolution;\n let sBitOffset = cell.quintantShift - hilbertBits;\n var digits : array<u32, 29>;\n var digitIndex = 0u;\n loop {\n if (digitIndex >= cell.hilbertResolution) {\n break;\n }\n var digit = dggs_u64_extract_bits(index, sBitOffset + 2u * digitIndex, 2u);\n if (reverse) {\n digit = 3u - digit;\n }\n digits[digitIndex] = digit;\n digitIndex += 1u;\n }\n\n var flips = vec2i(1, 1);\n digitIndex = cell.hilbertResolution;\n loop {\n if (digitIndex == 0u) {\n break;\n }\n digitIndex -= 1u;\n dggs_a5_shift_digits(&digits, digitIndex, flips, invertJ, flipIj);\n flips *= dggs_a5_quaternary_to_flips(digits[digitIndex]);\n }\n\n flips = vec2i(1, 1);\n var offset = vec2i(0);\n digitIndex = cell.hilbertResolution;\n loop {\n if (digitIndex == 0u) {\n break;\n }\n digitIndex -= 1u;\n offset *= 2;\n offset += dggs_a5_quaternary_to_kj(digits[digitIndex], flips);\n flips *= dggs_a5_quaternary_to_flips(digits[digitIndex]);\n }\n\n var anchorOffset = dggs_a5_kj_to_ij(offset);\n if (flipIj) {\n let originalOffset = anchorOffset;\n anchorOffset = originalOffset.yx;\n if (flips.x == -1) {\n anchorOffset += vec2i(-1, 1);\n }\n if (flips.y == -1) {\n anchorOffset -= vec2i(-1, 1);\n }\n }\n if (invertJ) {\n let i = anchorOffset.x;\n let j = i32(1u << cell.hilbertResolution) - (i + anchorOffset.y);\n flips.x = -flips.x;\n anchorOffset.y = j;\n }\n\n return DggsA5Anchor(\n digits[0],\n anchorOffset,\n flips,\n anchorInfo.orientation,\n anchorInfo.quintant\n );\n}\n\nfn dggs_a5_rotate_face_point(point : vec2f, quintant : u32) -> vec2f {\n let angle = f32(quintant) * DGGS_TWO_PI_OVER_5;\n let cosine = cos(angle);\n let sine = sin(angle);\n return vec2f(point.x * cosine - point.y * sine, point.x * sine + point.y * cosine);\n}\n\nfn dggs_a5_get_base_pentagon_vertex(vertexIndex : u32) -> vec2f {\n let vertices = array<vec2f, 5>(\n vec2f(0.0, 0.0),\n vec2f(0.199381847431, 0.375413822391),\n vec2f(0.618033988750, 0.449027976580),\n vec2f(0.817415836181, 0.073614154188),\n vec2f(0.418652141319, -0.073614154188)\n );\n return vertices[min(vertexIndex, 4u)];\n}\n\nfn dggs_a5_get_base_pentagon_center() -> vec2f {\n return vec2f(0.4106967627362, 0.1648883597942);\n}\n\nfn dggs_a5_get_base_triangle_vertex(vertexIndex : u32) -> vec2f {\n let vertices = array<vec2f, 3>(\n vec2f(0.0, 0.0),\n vec2f(0.618033988750, 0.449027976580),\n vec2f(0.618033988750, -0.449027976580)\n );\n return vertices[min(vertexIndex, 2u)];\n}\n\nfn dggs_a5_get_face_vertex(vertexIndex : u32) -> vec2f {\n return dggs_a5_rotate_face_point(dggs_a5_get_base_triangle_vertex(1u), 4u - min(vertexIndex, 4u));\n}\n\nfn dggs_a5_get_triangle_vertex(quintant : u32, vertexIndex : u32) -> vec2f {\n return dggs_a5_rotate_face_point(dggs_a5_get_base_triangle_vertex(vertexIndex), quintant);\n}\n\nfn dggs_a5_get_pentagon_vertex(\n anchor : DggsA5Anchor,\n hilbertResolution : u32,\n vertexIndex : u32\n) -> vec2f {\n let flipSum = anchor.flips.x + anchor.flips.y;\n let reflectsY = ((flipSum == -2 || flipSum == 2) && anchor.q > 1u) ||\n (flipSum == 0 && (anchor.q == 0u || anchor.q == 3u));\n let sourceIndex = select(vertexIndex, 4u - vertexIndex, reflectsY);\n var point = dggs_a5_get_base_pentagon_vertex(sourceIndex);\n\n if (anchor.flips.x == 1 && anchor.flips.y == -1) {\n point = -point;\n }\n if (reflectsY) {\n point.y = -point.y;\n }\n if (anchor.flips.x == -1 && anchor.flips.y == -1) {\n point = -point;\n } else if (anchor.flips.x == -1) {\n point += vec2f(-0.618033988750, 0.449027976580);\n } else if (anchor.flips.y == -1) {\n point += vec2f(0.618033988750, -0.449027976580);\n }\n\n let translation = vec2f(\n f32(anchor.offset.x) * 0.618033988750 + f32(anchor.offset.y) * 0.618033988750,\n f32(anchor.offset.x) * 0.449027976580 + f32(anchor.offset.y) * -0.449027976580\n );\n point = (point + translation) / f32(1u << hilbertResolution);\n return dggs_a5_rotate_face_point(point, anchor.quintant);\n}\n\n// Transforming the center directly preserves sub-cell precision when individual vertices collapse\n// to the same f32 coordinate at the highest resolutions.\nfn dggs_a5_get_pentagon_center(anchor : DggsA5Anchor, hilbertResolution : u32) -> vec2f {\n let flipSum = anchor.flips.x + anchor.flips.y;\n let reflectsY = ((flipSum == -2 || flipSum == 2) && anchor.q > 1u) ||\n (flipSum == 0 && (anchor.q == 0u || anchor.q == 3u));\n var point = dggs_a5_get_base_pentagon_center();\n\n if (anchor.flips.x == 1 && anchor.flips.y == -1) {\n point = -point;\n }\n if (reflectsY) {\n point.y = -point.y;\n }\n if (anchor.flips.x == -1 && anchor.flips.y == -1) {\n point = -point;\n } else if (anchor.flips.x == -1) {\n point += vec2f(-0.618033988750, 0.449027976580);\n } else if (anchor.flips.y == -1) {\n point += vec2f(0.618033988750, -0.449027976580);\n }\n\n let translation = vec2f(\n f32(anchor.offset.x) * 0.618033988750 + f32(anchor.offset.y) * 0.618033988750,\n f32(anchor.offset.x) * 0.449027976580 + f32(anchor.offset.y) * -0.449027976580\n );\n point = (point + translation) / f32(1u << hilbertResolution);\n return dggs_a5_rotate_face_point(point, anchor.quintant);\n}\n\nfn dggs_a5_get_shape_vertex_count(cell : DggsA5Cell) -> u32 {\n return select(5u, 3u, cell.resolution == 1u);\n}\n\nfn dggs_a5_get_reversed_shape_index(vertexIndex : u32, shapeVertexCount : u32) -> u32 {\n if (vertexIndex == 0u || vertexIndex >= shapeVertexCount) {\n return 0u;\n }\n return shapeVertexCount - vertexIndex;\n}\n\nfn dggs_a5_get_cell_shape_face_point(index : vec2u, cell : DggsA5Cell, shapeIndex : u32) -> vec2f {\n let anchorInfo = dggs_a5_segment_to_anchor_info(cell);\n if (cell.resolution == 0u) {\n return dggs_a5_get_face_vertex(shapeIndex);\n }\n if (cell.resolution == 1u) {\n return dggs_a5_get_triangle_vertex(anchorInfo.quintant, shapeIndex);\n }\n let anchor = dggs_a5_make_anchor(index, cell, anchorInfo);\n return dggs_a5_get_pentagon_vertex(anchor, cell.hilbertResolution, shapeIndex);\n}\n\nfn dggs_a5_get_cell_center_face_point(index : vec2u, cell : DggsA5Cell) -> vec2f {\n if (cell.resolution >= 2u) {\n return dggs_a5_get_pentagon_center(\n dggs_a5_make_anchor(index, cell, dggs_a5_segment_to_anchor_info(cell)),\n cell.hilbertResolution\n );\n }\n let shapeVertexCount = dggs_a5_get_shape_vertex_count(cell);\n var center = vec2f(0.0);\n var vertexIndex = 0u;\n loop {\n if (vertexIndex >= shapeVertexCount) {\n break;\n }\n center += dggs_a5_get_cell_shape_face_point(index, cell, vertexIndex);\n vertexIndex += 1u;\n }\n return center / f32(shapeVertexCount);\n}\n\nfn dggs_a5_to_polar(point : vec2f) -> vec2f {\n var angle = 0.0;\n if (dot(point, point) > 1.0e-12) {\n angle = atan2(point.y, point.x);\n }\n return vec2f(length(point), angle);\n}\n\nfn dggs_a5_to_cartesian(thetaPhi : vec2f) -> vec3f {\n let sinPhi = sin(thetaPhi.y);\n return vec3f(sinPhi * cos(thetaPhi.x), sinPhi * sin(thetaPhi.x), cos(thetaPhi.y));\n}\n\nfn dggs_a5_normalize_vec3(point : vec3f) -> vec3f {\n let magnitude = length(point);\n if (magnitude < 1.0e-12) {\n return vec3f(0.0, 0.0, 1.0);\n }\n return point / magnitude;\n}\n\nfn dggs_a5_get_face_triangle_index(polar : vec2f) -> u32 {\n return u32(dggs_a5_positive_mod_i32(i32(floor(polar.y / DGGS_PI_OVER_5)), 10));\n}\n\nfn dggs_a5_normalize_gamma(gamma : f32) -> f32 {\n let segment = gamma / DGGS_TWO_PI_OVER_5;\n return (segment - round(segment)) * DGGS_TWO_PI_OVER_5;\n}\n\nfn dggs_a5_should_reflect(polar : vec2f) -> bool {\n return polar.x * cos(dggs_a5_normalize_gamma(polar.y)) > DGGS_A5_DISTANCE_TO_EDGE;\n}\n\nfn dggs_a5_get_base_face_triangle(faceTriangleIndex : u32) -> DggsA5FaceTriangle {\n let quintant = ((faceTriangleIndex + 1u) / 2u) % 5u;\n let center = dggs_a5_get_triangle_vertex(quintant, 0u);\n let corner1 = dggs_a5_get_triangle_vertex(quintant, 1u);\n let corner2 = dggs_a5_get_triangle_vertex(quintant, 2u);\n let edgeMidpoint = (corner1 + corner2) * 0.5;\n if ((faceTriangleIndex & 1u) == 0u) {\n return DggsA5FaceTriangle(center, edgeMidpoint, corner1);\n }\n return DggsA5FaceTriangle(center, corner2, edgeMidpoint);\n}\n\nfn dggs_a5_get_face_triangle_vertex(triangle : DggsA5FaceTriangle, vertexIndex : u32) -> vec2f {\n if (vertexIndex == 0u) {\n return triangle.a;\n }\n if (vertexIndex == 1u) {\n return triangle.b;\n }\n return triangle.c;\n}\n\nfn dggs_a5_get_reflected_face_triangle(\n faceTriangleIndex : u32,\n squashed : bool\n) -> DggsA5FaceTriangle {\n let baseTriangle = dggs_a5_get_base_face_triangle(faceTriangleIndex);\n let even = (faceTriangleIndex & 1u) == 0u;\n let midpoint = select(baseTriangle.c, baseTriangle.b, even);\n let scale = select(2.0, DGGS_A5_REFLECTED_TRIANGLE_SCALE, squashed);\n let reflectedA = -baseTriangle.a + midpoint * scale;\n return DggsA5FaceTriangle(reflectedA, baseTriangle.c, baseTriangle.b);\n}\n\nfn dggs_a5_get_face_triangle(\n faceTriangleIndex : u32,\n reflected : bool,\n squashed : bool\n) -> DggsA5FaceTriangle {\n if (reflected) {\n return dggs_a5_get_reflected_face_triangle(faceTriangleIndex, squashed);\n }\n return dggs_a5_get_base_face_triangle(faceTriangleIndex);\n}\n\nfn dggs_a5_face_triangle_to_spherical_triangle(\n faceTriangleIndex : u32,\n origin : u32,\n reflected : bool\n) -> DggsA5SphericalTriangle {\n let faceTriangle = dggs_a5_get_face_triangle(faceTriangleIndex, reflected, true);\n let originAngle = dggs_a5_get_origin_angle(origin);\n let quaternion = dggs_a5_get_origin_quaternion(origin);\n\n let polarA = dggs_a5_to_polar(faceTriangle.a);\n let polarB = dggs_a5_to_polar(faceTriangle.b);\n let polarC = dggs_a5_to_polar(faceTriangle.c);\n let sphericalA = dggs_a5_quaternion_rotate(\n dggs_a5_to_cartesian(vec2f(polarA.y + originAngle, atan(polarA.x))),\n quaternion\n );\n let sphericalB = dggs_a5_quaternion_rotate(\n dggs_a5_to_cartesian(vec2f(polarB.y + originAngle, atan(polarB.x))),\n quaternion\n );\n let sphericalC = dggs_a5_quaternion_rotate(\n dggs_a5_to_cartesian(vec2f(polarC.y + originAngle, atan(polarC.x))),\n quaternion\n );\n return DggsA5SphericalTriangle(\n dggs_a5_normalize_vec3(sphericalA),\n dggs_a5_normalize_vec3(sphericalB),\n dggs_a5_normalize_vec3(sphericalC)\n );\n}\n\nfn dggs_a5_face_to_barycentric(point : vec2f, triangle : DggsA5FaceTriangle) -> vec3f {\n let d31 = triangle.a - triangle.c;\n let d23 = triangle.c - triangle.b;\n let d3p = point - triangle.c;\n let determinant = d23.x * d31.y - d23.y * d31.x;\n if (abs(determinant) < 1.0e-12) {\n return vec3f(1.0, 0.0, 0.0);\n }\n let b0 = (d23.x * d3p.y - d23.y * d3p.x) / determinant;\n let b1 = (d31.x * d3p.y - d31.y * d3p.x) / determinant;\n return vec3f(b0, b1, 1.0 - (b0 + b1));\n}\n\nfn dggs_a5_triangle_area(a : vec3f, b : vec3f, c : vec3f) -> f32 {\n let midA = dggs_a5_normalize_vec3((b + c) * 0.5);\n let midB = dggs_a5_normalize_vec3((c + a) * 0.5);\n let midC = dggs_a5_normalize_vec3((a + b) * 0.5);\n let tripleProduct = dot(midA, cross(midB, midC));\n let clamped = clamp(tripleProduct, -1.0, 1.0);\n if (abs(clamped) < 1.0e-8) {\n return 2.0 * clamped;\n }\n return asin(clamped) * 2.0;\n}\n\nfn dggs_a5_spherical_triangle_area(triangle : DggsA5SphericalTriangle) -> f32 {\n return dggs_a5_triangle_area(triangle.a, triangle.b, triangle.c);\n}\n\nfn dggs_a5_vector_difference(a : vec3f, b : vec3f) -> f32 {\n let midpoint = dggs_a5_normalize_vec3((a + b) * 0.5);\n let crossProduct = cross(a, midpoint);\n let distance = length(crossProduct);\n if (distance < 1.0e-8) {\n return 0.5 * length(a - b);\n }\n return distance;\n}\n\nfn dggs_a5_slerp(a : vec3f, b : vec3f, t : f32) -> vec3f {\n let cosineGamma = clamp(dot(a, b), -1.0, 1.0);\n let gamma = acos(cosineGamma);\n if (gamma < 1.0e-8) {\n return dggs_a5_normalize_vec3(mix(a, b, t));\n }\n let sinGamma = sin(gamma);\n if (abs(sinGamma) < 1.0e-8) {\n return dggs_a5_normalize_vec3(mix(a, b, t));\n }\n let weightA = sin((1.0 - t) * gamma) / sinGamma;\n let weightB = sin(t * gamma) / sinGamma;\n return dggs_a5_normalize_vec3(weightA * a + weightB * b);\n}\n\nfn dggs_a5_safe_acos(value : f32) -> f32 {\n if (value < 1.0e-3) {\n return 2.0 * value + value * value * value / 3.0;\n }\n return acos(clamp(1.0 - 2.0 * value * value, -1.0, 1.0));\n}\n\nfn dggs_a5_polyhedral_inverse_edge(a : vec3f, b : vec3f, edgeHeight : f32) -> vec3f {\n let difference = dggs_a5_vector_difference(a, b);\n let denominator = max(dggs_a5_safe_acos(difference), 1.0e-8);\n let interpolation = dggs_a5_safe_acos(clamp(edgeHeight, 0.0, 1.0) * difference) / denominator;\n return dggs_a5_slerp(a, b, clamp(interpolation, 0.0, 1.0));\n}\n\nfn dggs_a5_polyhedral_inverse(\n facePoint : vec2f,\n faceTriangle : DggsA5FaceTriangle,\n sphericalTriangle : DggsA5SphericalTriangle\n) -> vec3f {\n let barycentric = dggs_a5_face_to_barycentric(facePoint, faceTriangle);\n if (barycentric.x > 0.9999999) {\n return sphericalTriangle.a;\n }\n if (barycentric.y > 0.9999999) {\n return sphericalTriangle.b;\n }\n if (barycentric.z > 0.9999999) {\n return sphericalTriangle.c;\n }\n if (abs(barycentric.z) < 1.0e-6) {\n return dggs_a5_polyhedral_inverse_edge(\n sphericalTriangle.a,\n sphericalTriangle.b,\n 1.0 - barycentric.x\n );\n }\n if (abs(barycentric.y) < 1.0e-6) {\n return dggs_a5_polyhedral_inverse_edge(\n sphericalTriangle.a,\n sphericalTriangle.c,\n 1.0 - barycentric.x\n );\n }\n\n let crossBC = cross(sphericalTriangle.b, sphericalTriangle.c);\n let areaABC = dggs_a5_spherical_triangle_area(sphericalTriangle);\n let h = max(1.0 - barycentric.x, 1.0e-8);\n let areaRatio = barycentric.z / h;\n let alpha = areaRatio * areaABC;\n let sineAlpha = sin(alpha);\n let halfSineAlpha = sin(alpha * 0.5);\n let chordCoefficient = 2.0 * halfSineAlpha * halfSineAlpha;\n let dotAB = dot(sphericalTriangle.a, sphericalTriangle.b);\n let dotBC = dot(sphericalTriangle.b, sphericalTriangle.c);\n let dotCA = dot(sphericalTriangle.c, sphericalTriangle.a);\n let lengthBC = length(crossBC);\n let volume = dot(sphericalTriangle.a, crossBC);\n let f = sineAlpha * volume + chordCoefficient * (dotAB * dotBC - dotCA);\n let g = chordCoefficient * lengthBC * (1.0 + dotAB);\n let edgeAngle = max(acos(clamp(dotBC, -1.0, 1.0)), 1.0e-8);\n let q = 2.0 / edgeAngle * atan2(g, f);\n let pointOnBC = dggs_a5_slerp(sphericalTriangle.b, sphericalTriangle.c, q);\n let k = dggs_a5_vector_difference(sphericalTriangle.a, pointOnBC);\n let denominator = max(dggs_a5_safe_acos(k), 1.0e-8);\n let t = dggs_a5_safe_acos(h * k) / denominator;\n return dggs_a5_slerp(sphericalTriangle.a, pointOnBC, t);\n}\n\nfn dggs_a5_authalic_inverse(phi : f32) -> f32 {\n let sinPhi = sin(phi);\n let cosPhi = cos(phi);\n let coefficientX = 2.0 * (cosPhi - sinPhi) * (cosPhi + sinPhi);\n var u0 = coefficientX * 4.92842354825238055e-17 + 2.19128723067677184e-14;\n var u1 = coefficientX * u0 + 1.02018123778161003e-11;\n u0 = coefficientX * u1 - u0 + 5.08622073997266026e-9;\n u1 = coefficientX * u0 - u1 + 0.00000288319780486075558;\n u0 = coefficientX * u1 - u0 + 0.00223920899635416575;\n return phi + 2.0 * sinPhi * cosPhi * u0;\n}\n\nfn dggs_a5_normalize_longitude(longitude : f32) -> f32 {\n var result = longitude;\n loop {\n if (result < -180.0) {\n result += 360.0;\n } else {\n break;\n }\n }\n loop {\n if (result >= 180.0) {\n result -= 360.0;\n } else {\n break;\n }\n }\n return result;\n}\n\nfn dggs_a5_normalize_longitude_to_center(longitude : f32, centerLongitude : f32) -> f32 {\n var result = longitude;\n loop {\n if (result - centerLongitude > 180.0) {\n result -= 360.0;\n } else {\n break;\n }\n }\n loop {\n if (result - centerLongitude < -180.0) {\n result += 360.0;\n } else {\n break;\n }\n }\n return result;\n}\n\nfn dggs_a5_sphere_to_lnglat(cartesian : vec3f) -> vec2f {\n let point = dggs_a5_normalize_vec3(cartesian);\n var theta = 0.0;\n if (dot(point.xy, point.xy) > 1.0e-12) {\n theta = atan2(point.y, point.x);\n }\n let phi = acos(clamp(point.z, -1.0, 1.0));\n let longitude = dggs_a5_normalize_longitude(theta * DGGS_RADIANS_TO_DEGREES - DGGS_A5_LONGITUDE_OFFSET);\n let authalicLatitude = DGGS_PI_OVER_2 - phi;\n let latitude = dggs_a5_authalic_inverse(authalicLatitude) * DGGS_RADIANS_TO_DEGREES;\n return vec2f(longitude, latitude);\n}\n\nfn dggs_a5_face_to_lnglat(facePoint : vec2f, origin : u32) -> vec2f {\n let polar = dggs_a5_to_polar(facePoint);\n let faceTriangleIndex = dggs_a5_get_face_triangle_index(polar);\n let reflected = dggs_a5_should_reflect(polar);\n let faceTriangle = dggs_a5_get_face_triangle(faceTriangleIndex, reflected, false);\n let sphericalTriangle = dggs_a5_face_triangle_to_spherical_triangle(\n faceTriangleIndex,\n origin,\n reflected\n );\n return dggs_a5_sphere_to_lnglat(\n dggs_a5_polyhedral_inverse(facePoint, faceTriangle, sphericalTriangle)\n );\n}\n\nfn dggs_a5_get_center_lnglat(index : vec2u) -> vec2f {\n let cell = dggs_a5_deserialize(index);\n if (cell.valid == 0u || dggs_u64_is_zero(index)) {\n return vec2f(0.0);\n }\n if (cell.resolution == 0u) {\n return dggs_a5_sphere_to_lnglat(dggs_a5_quaternion_rotate(\n vec3f(0.0, 0.0, 1.0),\n dggs_a5_get_origin_quaternion(cell.origin)\n ));\n }\n return dggs_a5_face_to_lnglat(\n dggs_a5_get_cell_center_face_point(index, cell),\n cell.origin\n );\n}\n\nfn dggs_a5_get_center_unit_vector(index : vec2u) -> vec3f {\n let longitudeLatitude = dggs_a5_get_center_lnglat(index) * DGGS_DEGREES_TO_RADIANS;\n let cosLatitude = cos(longitudeLatitude.y);\n return vec3f(\n cosLatitude * cos(longitudeLatitude.x),\n cosLatitude * sin(longitudeLatitude.x),\n sin(longitudeLatitude.y)\n );\n}\n\nfn dggs_a5_get_boundary_point(index : vec2u, vertexIndex : u32) -> vec2f {\n let cell = dggs_a5_deserialize(index);\n if (cell.valid == 0u || dggs_u64_is_zero(index)) {\n return vec2f(0.0);\n }\n\n let shapeVertexCount = dggs_a5_get_shape_vertex_count(cell);\n let shapeIndex = dggs_a5_get_reversed_shape_index(vertexIndex, shapeVertexCount);\n let facePoint = dggs_a5_get_cell_shape_face_point(index, cell, shapeIndex);\n let centerPoint = dggs_a5_get_cell_center_face_point(index, cell);\n var lngLat = dggs_a5_face_to_lnglat(facePoint, cell.origin);\n let centerLngLat = dggs_a5_face_to_lnglat(centerPoint, cell.origin);\n lngLat.x = dggs_a5_normalize_longitude_to_center(lngLat.x, centerLngLat.x);\n return lngLat;\n}\n\nfn dggs_a5_get_boundary_point_fp64_split(index : vec2u, vertexIndex : u32) -> vec4f {\n return dggs_boundary_point_to_fp64_split(dggs_a5_get_boundary_point(index, vertexIndex));\n}\n\nfn dggs_h3_get_mode(index: vec2u) -> u32 {\n return dggs_u64_extract_bits(index, 59u, 4u);\n}\n\nfn dggs_h3_is_cell_mode(index: vec2u) -> bool {\n return dggs_h3_get_mode(index) == DGGS_H3_CELL_MODE;\n}\n\nfn dggs_h3_get_resolution(index: vec2u) -> u32 {\n return dggs_u64_extract_bits(index, 52u, 4u);\n}\n\nfn dggs_h3_get_base_cell(index: vec2u) -> u32 {\n return dggs_u64_extract_bits(index, 45u, 7u);\n}\n\nfn dggs_h3_digit_bit_offset(resolution: u32) -> u32 {\n return 3u * (DGGS_H3_MAX_RESOLUTION - resolution);\n}\n\nfn dggs_h3_get_digit(index: vec2u, resolution: u32) -> u32 {\n if (resolution == 0u || resolution > DGGS_H3_MAX_RESOLUTION) {\n return DGGS_H3_UNUSED_DIGIT;\n }\n return dggs_u64_extract_bits(index, dggs_h3_digit_bit_offset(resolution), 3u);\n}\n\nfn dggs_h3_is_valid_cell_id(index: vec2u) -> bool {\n let cellResolution = dggs_h3_get_resolution(index);\n if (\n (dggs_u64_high(index) >> 24u) != 8u ||\n !dggs_h3_is_cell_mode(index) ||\n cellResolution > DGGS_H3_MAX_RESOLUTION ||\n dggs_h3_get_base_cell(index) > DGGS_H3_MAX_BASE_CELL\n ) {\n return false;\n }\n\n var digitResolution = 1u;\n loop {\n if (digitResolution > DGGS_H3_MAX_RESOLUTION) {\n break;\n }\n\n let digit = dggs_h3_get_digit(index, digitResolution);\n if (digitResolution <= cellResolution) {\n if (digit == DGGS_H3_UNUSED_DIGIT) {\n return false;\n }\n } else if (digit != DGGS_H3_UNUSED_DIGIT) {\n return false;\n }\n digitResolution += 1u;\n }\n if (\n dggs_h3_is_base_cell_pentagon(dggs_h3_get_base_cell(index)) &&\n dggs_h3_get_leading_non_zero_digit(index) == 1u\n ) {\n return false;\n }\n return true;\n}\n\nfn dggs_h3_get_parent(index: vec2u, parentResolution: u32) -> vec2u {\n let currentResolution = dggs_h3_get_resolution(index);\n let targetResolution = min(parentResolution, currentResolution);\n var parent = dggs_u64_set_bits(index, 52u, 4u, targetResolution);\n var resolution = targetResolution + 1u;\n loop {\n if (resolution > DGGS_H3_MAX_RESOLUTION) {\n break;\n }\n parent = dggs_u64_set_bits(\n parent,\n dggs_h3_digit_bit_offset(resolution),\n 3u,\n DGGS_H3_UNUSED_DIGIT\n );\n resolution += 1u;\n }\n return parent;\n}\n\nfn dggs_h3_positive_angle_rads(angle: f32) -> f32 {\n var result = angle;\n if (result < 0.0) {\n result += DGGS_TWO_PI;\n }\n if (result >= DGGS_TWO_PI) {\n result -= DGGS_TWO_PI;\n }\n return result;\n}\n\nfn dggs_h3_constrain_longitude(longitude: f32) -> f32 {\n var result = longitude;\n loop {\n if (result <= DGGS_PI) {\n break;\n }\n result -= DGGS_TWO_PI;\n }\n loop {\n if (result >= -DGGS_PI) {\n break;\n }\n result += DGGS_TWO_PI;\n }\n return result;\n}\n\nfn dggs_h3_is_resolution_class_iii(resolution: u32) -> bool {\n return (resolution & 1u) != 0u;\n}\n\nfn dggs_h3_is_base_cell_pentagon(baseCell: u32) -> bool {\n return baseCell == 4u ||\n baseCell == 14u ||\n baseCell == 24u ||\n baseCell == 38u ||\n baseCell == 49u ||\n baseCell == 58u ||\n baseCell == 63u ||\n baseCell == 72u ||\n baseCell == 83u ||\n baseCell == 97u ||\n baseCell == 107u ||\n baseCell == 117u;\n}\n\nfn dggs_h3_get_leading_non_zero_digit(index: vec2u) -> u32 {\n let cellResolution = dggs_h3_get_resolution(index);\n var resolution = 1u;\n loop {\n if (resolution > cellResolution) {\n break;\n }\n let digit = dggs_h3_get_digit(index, resolution);\n if (digit != 0u) {\n return digit;\n }\n resolution += 1u;\n }\n return 0u;\n}\n\nfn dggs_h3_get_base_cell_home(baseCell: u32) -> DggsH3FaceIJK {\n let values = array<vec4i, 122>(\n vec4i(1, 1, 0, 0),\n vec4i(2, 1, 1, 0),\n vec4i(1, 0, 0, 0),\n vec4i(2, 1, 0, 0),\n vec4i(0, 2, 0, 0),\n vec4i(1, 1, 1, 0),\n vec4i(1, 0, 0, 1),\n vec4i(2, 0, 0, 0),\n vec4i(0, 1, 0, 0),\n vec4i(2, 0, 1, 0),\n vec4i(1, 0, 1, 0),\n vec4i(1, 0, 1, 1),\n vec4i(3, 1, 0, 0),\n vec4i(3, 1, 1, 0),\n vec4i(11, 2, 0, 0),\n vec4i(4, 1, 0, 0),\n vec4i(0, 0, 0, 0),\n vec4i(6, 0, 1, 0),\n vec4i(0, 0, 0, 1),\n vec4i(2, 0, 1, 1),\n vec4i(7, 0, 0, 1),\n vec4i(2, 0, 0, 1),\n vec4i(0, 1, 1, 0),\n vec4i(6, 0, 0, 1),\n vec4i(10, 2, 0, 0),\n vec4i(6, 0, 0, 0),\n vec4i(3, 0, 0, 0),\n vec4i(11, 1, 0, 0),\n vec4i(4, 1, 1, 0),\n vec4i(3, 0, 1, 0),\n vec4i(0, 0, 1, 1),\n vec4i(4, 0, 0, 0),\n vec4i(5, 0, 1, 0),\n vec4i(0, 0, 1, 0),\n vec4i(7, 0, 1, 0),\n vec4i(11, 1, 1, 0),\n vec4i(7, 0, 0, 0),\n vec4i(10, 1, 0, 0),\n vec4i(12, 2, 0, 0),\n vec4i(6, 1, 0, 1),\n vec4i(7, 1, 0, 1),\n vec4i(4, 0, 0, 1),\n vec4i(3, 0, 0, 1),\n vec4i(3, 0, 1, 1),\n vec4i(4, 0, 1, 0),\n vec4i(6, 1, 0, 0),\n vec4i(11, 0, 0, 0),\n vec4i(8, 0, 0, 1),\n vec4i(5, 0, 0, 1),\n vec4i(14, 2, 0, 0),\n vec4i(5, 0, 0, 0),\n vec4i(12, 1, 0, 0),\n vec4i(10, 1, 1, 0),\n vec4i(4, 0, 1, 1),\n vec4i(12, 1, 1, 0),\n vec4i(7, 1, 0, 0),\n vec4i(11, 0, 1, 0),\n vec4i(10, 0, 0, 0),\n vec4i(13, 2, 0, 0),\n vec4i(10, 0, 0, 1),\n vec4i(11, 0, 0, 1),\n vec4i(9, 0, 1, 0),\n vec4i(8, 0, 1, 0),\n vec4i(6, 2, 0, 0),\n vec4i(8, 0, 0, 0),\n vec4i(9, 0, 0, 1),\n vec4i(14, 1, 0, 0),\n vec4i(5, 1, 0, 1),\n vec4i(16, 0, 1, 1),\n vec4i(8, 1, 0, 1),\n vec4i(5, 1, 0, 0),\n vec4i(12, 0, 0, 0),\n vec4i(7, 2, 0, 0),\n vec4i(12, 0, 1, 0),\n vec4i(10, 0, 1, 0),\n vec4i(9, 0, 0, 0),\n vec4i(13, 1, 0, 0),\n vec4i(16, 0, 0, 1),\n vec4i(15, 0, 1, 1),\n vec4i(15, 0, 1, 0),\n vec4i(16, 0, 1, 0),\n vec4i(14, 1, 1, 0),\n vec4i(13, 1, 1, 0),\n vec4i(5, 2, 0, 0),\n vec4i(8, 1, 0, 0),\n vec4i(14, 0, 0, 0),\n vec4i(9, 1, 0, 1),\n vec4i(14, 0, 0, 1),\n vec4i(17, 0, 0, 1),\n vec4i(12, 0, 0, 1),\n vec4i(16, 0, 0, 0),\n vec4i(17, 0, 1, 1),\n vec4i(15, 0, 0, 1),\n vec4i(16, 1, 0, 1),\n vec4i(9, 1, 0, 0),\n vec4i(15, 0, 0, 0),\n vec4i(13, 0, 0, 0),\n vec4i(8, 2, 0, 0),\n vec4i(13, 0, 1, 0),\n vec4i(17, 1, 0, 1),\n vec4i(19, 0, 1, 0),\n vec4i(14, 0, 1, 0),\n vec4i(19, 0, 1, 1),\n vec4i(17, 0, 1, 0),\n vec4i(13, 0, 0, 1),\n vec4i(17, 0, 0, 0),\n vec4i(16, 1, 0, 0),\n vec4i(9, 2, 0, 0),\n vec4i(15, 1, 0, 1),\n vec4i(15, 1, 0, 0),\n vec4i(18, 0, 1, 1),\n vec4i(18, 0, 0, 1),\n vec4i(19, 0, 0, 1),\n vec4i(17, 1, 0, 0),\n vec4i(19, 0, 0, 0),\n vec4i(18, 0, 1, 0),\n vec4i(18, 1, 0, 1),\n vec4i(19, 2, 0, 0),\n vec4i(19, 1, 0, 0),\n vec4i(18, 0, 0, 0),\n vec4i(19, 1, 0, 1),\n vec4i(18, 1, 0, 0)\n );\n let value = values[min(baseCell, DGGS_H3_MAX_BASE_CELL)];\n return DggsH3FaceIJK(\n u32(value.x),\n value.yzw,\n select(1u, 0u, baseCell > DGGS_H3_MAX_BASE_CELL)\n );\n}\n\nfn dggs_h3_get_face_center_geo(face: u32) -> vec2f {\n let values = array<vec2f, 20>(\n vec2f(0.803582649718989942, 1.248397419617396099),\n vec2f(1.307747883455638156, 2.536945009877921159),\n vec2f(1.054751253523952054, -1.347517358900396623),\n vec2f(0.600191595538186799, -0.450603909469755746),\n vec2f(0.491715428198773866, 0.401988202911306943),\n vec2f(0.172745327415618701, 1.678146885280433686),\n vec2f(0.605929321571350690, 2.953923329812411617),\n vec2f(0.427370518328979641, -1.888876200336285401),\n vec2f(-0.079066118549212831, -0.733429513380867741),\n vec2f(-0.230961644455383637, 0.506495587332349035),\n vec2f(0.079066118549212831, 2.408163140208925497),\n vec2f(0.230961644455383637, -2.635097066257444203),\n vec2f(-0.172745327415618701, -1.463445768309359553),\n vec2f(-0.605929321571350690, -0.187669323777381622),\n vec2f(-0.427370518328979641, 1.252716453253507838),\n vec2f(-0.600191595538186799, 2.690988744120037492),\n vec2f(-0.491715428198773866, -2.739604450678486295),\n vec2f(-0.803582649718989942, -1.893195233972397139),\n vec2f(-1.307747883455638156, -0.604647643711872080),\n vec2f(-1.054751253523952054, 1.794075294689396615)\n );\n return values[min(face, 19u)];\n}\n\nfn dggs_h3_get_face_axis_azimuths(face: u32) -> vec3f {\n let values = array<vec3f, 20>(\n vec3f(5.619958268523939882, 3.525563166130744542, 1.431168063737548730),\n vec3f(5.760339081714187279, 3.665943979320991689, 1.571548876927796127),\n vec3f(0.780213654393430055, 4.969003859179821079, 2.874608756786625655),\n vec3f(0.430469363979999913, 4.619259568766391033, 2.524864466373195467),\n vec3f(6.130269123335111400, 4.035874020941915804, 1.941478918548720291),\n vec3f(2.692877706530642877, 0.598482604137447119, 4.787272808923838195),\n vec3f(2.982963003477243874, 0.888567901084048369, 5.077358105870439581),\n vec3f(3.532912002790141181, 1.438516900396945656, 5.627307105183336758),\n vec3f(3.494305004259568154, 1.399909901866372864, 5.588700106652763840),\n vec3f(3.003214169499538391, 0.908819067106342928, 5.097609271892733906),\n vec3f(5.930472956509811562, 3.836077854116615875, 1.741682751723420374),\n vec3f(0.138378484090254847, 4.327168688876645809, 2.232773586483450311),\n vec3f(0.448714947059150361, 4.637505151845541521, 2.543110049452346120),\n vec3f(0.158629650112549365, 4.347419854898940135, 2.253024752505744869),\n vec3f(5.891865957979238535, 3.797470855586042958, 1.703075753192847583),\n vec3f(2.711123289609793325, 0.616728187216597771, 4.805518392002988683),\n vec3f(3.294508837434268316, 1.200113735041072948, 5.388903939827463911),\n vec3f(3.804819692245439833, 1.710424589852244509, 5.899214794638635174),\n vec3f(3.664438879055192436, 1.570043776661997111, 5.758833981448388027),\n vec3f(2.361378999196363184, 0.266983896803167583, 4.455774101589558636)\n );\n return values[min(face, 19u)];\n}\n\n// Per face: unit-sphere center, Class II hex x axis, and Class II hex y axis.\nconst DGGS_H3_FACE_UNIT_VECTOR_BASES = array<vec4f, 60>(\n vec4f(0.219930779140, 0.658369178027, 0.719847537893, 0.0),\n vec4f(0.404214808693, -0.733089476282, 0.546982822580, 0.0),\n vec4f(0.887829285854, 0.170674676471, -0.427351511044, 0.0),\n vec4f(-0.213923483450, 0.147817182955, 0.965601793521, 0.0),\n vec4f(0.972137411506, -0.064768238220, 0.225286325522, 0.0),\n vec4f(0.095841516985, 0.986891663629, -0.129843166477, 0.0),\n vec4f(0.109262527878, -0.481195157287, 0.869777512129, 0.0),\n vec4f(0.549081430333, 0.758619604829, 0.350721938339, 0.0),\n vec4f(-0.828595970824, 0.439257914866, 0.347104020954, 0.0),\n vec4f(0.742856730159, -0.359394167828, 0.564800593652, 0.0),\n vec4f(-0.280304147989, 0.599180039695, 0.749941907518, 0.0),\n vec4f(-0.607941989895, -0.715415342415, 0.344365249059, 0.0),\n vec4f(0.811253470914, 0.344895323764, 0.472138773641, 0.0),\n vec4f(-0.369836643998, -0.322746873758, 0.871237804641, 0.0),\n vec4f(0.452867157880, -0.881408912551, -0.134274592492, 0.0),\n vec4f(-0.105549814961, 0.979445729641, 0.171887461001, 0.0),\n vec4f(-0.447904449344, 0.107499848833, -0.887595283200, 0.0),\n vec4f(-0.887829285854, -0.170674676471, 0.427351511044, 0.0),\n vec4f(-0.807540757997, 0.153355248590, 0.569526199488, 0.0),\n vec4f(-0.581972789566, -0.050269394156, -0.811653041771, 0.0),\n vec4f(-0.095841516985, -0.986891663629, 0.129843166477, 0.0),\n vec4f(-0.284614806979, -0.864408097265, 0.414479255247, 0.0),\n vec4f(-0.482102819721, -0.244644896962, -0.841264373195, 0.0),\n vec4f(0.828595970824, -0.439257914866, -0.347104020954, 0.0),\n vec4f(0.740562147385, -0.667329956457, -0.078983764633, 0.0),\n vec4f(-0.286311443679, -0.207006321288, -0.935507423896, 0.0),\n vec4f(0.607941989895, 0.715415342415, -0.344365249059, 0.0),\n vec4f(0.851230398647, 0.472234378858, -0.228913738869, 0.0),\n vec4f(-0.265175688426, 0.010631100574, -0.964141501009, 0.0),\n vec4f(-0.452867157880, 0.881408912551, 0.134274592492, 0.0),\n vec4f(-0.740562147385, 0.667329956457, 0.078983764633, 0.0),\n vec4f(0.286311443679, 0.207006321288, 0.935507423896, 0.0),\n vec4f(0.607941989895, 0.715415342415, -0.344365249059, 0.0),\n vec4f(-0.851230398647, -0.472234378858, 0.228913738869, 0.0),\n vec4f(0.265175688426, -0.010631100574, 0.964141501009, 0.0),\n vec4f(-0.452867157880, 0.881408912551, 0.134274592492, 0.0),\n vec4f(0.105549814961, -0.979445729641, -0.171887461001, 0.0),\n vec4f(0.447904449344, -0.107499848833, 0.887595283200, 0.0),\n vec4f(-0.887829285854, -0.170674676471, 0.427351511044, 0.0),\n vec4f(0.807540757997, -0.153355248590, -0.569526199488, 0.0),\n vec4f(0.581972789566, 0.050269394156, 0.811653041771, 0.0),\n vec4f(-0.095841516985, -0.986891663629, 0.129843166477, 0.0),\n vec4f(0.284614806979, 0.864408097265, -0.414479255247, 0.0),\n vec4f(0.482102819721, 0.244644896962, 0.841264373195, 0.0),\n vec4f(0.828595970824, -0.439257914866, -0.347104020954, 0.0),\n vec4f(-0.742856730159, 0.359394167828, -0.564800593652, 0.0),\n vec4f(0.280304147989, -0.599180039695, -0.749941907518, 0.0),\n vec4f(-0.607941989895, -0.715415342415, 0.344365249059, 0.0),\n vec4f(-0.811253470914, -0.344895323764, -0.472138773641, 0.0),\n vec4f(0.369836643998, 0.322746873758, -0.871237804641, 0.0),\n vec4f(0.452867157880, -0.881408912551, -0.134274592492, 0.0),\n vec4f(-0.219930779140, -0.658369178027, -0.719847537893, 0.0),\n vec4f(-0.404214808693, 0.733089476282, -0.546982822580, 0.0),\n vec4f(0.887829285854, 0.170674676471, -0.427351511044, 0.0),\n vec4f(0.213923483450, -0.147817182955, -0.965601793521, 0.0),\n vec4f(-0.972137411506, 0.064768238220, -0.225286325522, 0.0),\n vec4f(0.095841516985, 0.986891663629, -0.129843166477, 0.0),\n vec4f(-0.109262527878, 0.481195157287, -0.869777512129, 0.0),\n vec4f(-0.549081430333, -0.758619604829, -0.350721938339, 0.0),\n vec4f(-0.828595970824, 0.439257914866, 0.347104020954, 0.0)\n);\n\nfn dggs_h3_get_face_unit_vector_basis(face: u32, basisIndex: u32) -> vec3f {\n return DGGS_H3_FACE_UNIT_VECTOR_BASES[min(face, 19u) * 3u + min(basisIndex, 2u)].xyz;\n}\n\nfn dggs_h3_get_max_dim_by_cii_resolution(resolution: u32) -> i32 {\n let values = array<i32, 17>(\n 2, -1, 14, -1, 98, -1, 686, -1, 4802, -1, 33614, -1, 235298, -1, 1647086, -1, 11529602\n );\n return values[min(resolution, 16u)];\n}\n\nfn dggs_h3_get_unit_vector(digit: u32) -> vec3i {\n if (digit == 1u) {\n return vec3i(0, 0, 1);\n }\n if (digit == 2u) {\n return vec3i(0, 1, 0);\n }\n if (digit == 3u) {\n return vec3i(0, 1, 1);\n }\n if (digit == 4u) {\n return vec3i(1, 0, 0);\n }\n if (digit == 5u) {\n return vec3i(1, 0, 1);\n }\n if (digit == 6u) {\n return vec3i(1, 1, 0);\n }\n return vec3i(0);\n}\n\nfn dggs_h3_ijk_normalize(coord: vec3i) -> vec3i {\n var normalized = coord;\n if (normalized.x < 0) {\n normalized.y -= normalized.x;\n normalized.z -= normalized.x;\n normalized.x = 0;\n }\n if (normalized.y < 0) {\n normalized.x -= normalized.y;\n normalized.z -= normalized.y;\n normalized.y = 0;\n }\n if (normalized.z < 0) {\n normalized.x -= normalized.z;\n normalized.y -= normalized.z;\n normalized.z = 0;\n }\n\n let minimumValue = min(normalized.x, min(normalized.y, normalized.z));\n if (minimumValue > 0) {\n normalized -= vec3i(minimumValue);\n }\n return normalized;\n}\n\nfn dggs_h3_neighbor(coord: vec3i, digit: u32) -> vec3i {\n if (digit > 0u && digit < DGGS_H3_UNUSED_DIGIT) {\n return dggs_h3_ijk_normalize(coord + dggs_h3_get_unit_vector(digit));\n }\n return coord;\n}\n\nfn dggs_h3_down_ap3(coord: vec3i) -> vec3i {\n let iVector = vec3i(2, 0, 1) * coord.x;\n let jVector = vec3i(1, 2, 0) * coord.y;\n let kVector = vec3i(0, 1, 2) * coord.z;\n return dggs_h3_ijk_normalize(iVector + jVector + kVector);\n}\n\nfn dggs_h3_down_ap3r(coord: vec3i) -> vec3i {\n let iVector = vec3i(2, 1, 0) * coord.x;\n let jVector = vec3i(0, 2, 1) * coord.y;\n let kVector = vec3i(1, 0, 2) * coord.z;\n return dggs_h3_ijk_normalize(iVector + jVector + kVector);\n}\n\nfn dggs_h3_down_ap7(coord: vec3i) -> vec3i {\n let iVector = vec3i(3, 0, 1) * coord.x;\n let jVector = vec3i(1, 3, 0) * coord.y;\n let kVector = vec3i(0, 1, 3) * coord.z;\n return dggs_h3_ijk_normalize(iVector + jVector + kVector);\n}\n\nfn dggs_h3_down_ap7r(coord: vec3i) -> vec3i {\n let iVector = vec3i(3, 1, 0) * coord.x;\n let jVector = vec3i(0, 3, 1) * coord.y;\n let kVector = vec3i(1, 0, 3) * coord.z;\n return dggs_h3_ijk_normalize(iVector + jVector + kVector);\n}\n\nfn dggs_h3_divide_round_nearest_seven(value: i32) -> i32 {\n return select((value - 3) / 7, (value + 3) / 7, value >= 0);\n}\n\nfn dggs_h3_up_ap7r(coord: vec3i) -> vec3i {\n let i = coord.x - coord.z;\n let j = coord.y - coord.z;\n return dggs_h3_ijk_normalize(vec3i(\n dggs_h3_divide_round_nearest_seven(2 * i + j),\n dggs_h3_divide_round_nearest_seven(3 * j - i),\n 0\n ));\n}\n\nfn dggs_h3_ijk_rotate_60_ccw(coord: vec3i) -> vec3i {\n let iVector = vec3i(1, 1, 0) * coord.x;\n let jVector = vec3i(0, 1, 1) * coord.y;\n let kVector = vec3i(1, 0, 1) * coord.z;\n return dggs_h3_ijk_normalize(iVector + jVector + kVector);\n}\n\nfn dggs_h3_ijk_rotate_60_cw(coord: vec3i) -> vec3i {\n let iVector = vec3i(1, 0, 1) * coord.x;\n let jVector = vec3i(1, 1, 0) * coord.y;\n let kVector = vec3i(0, 1, 1) * coord.z;\n return dggs_h3_ijk_normalize(iVector + jVector + kVector);\n}\n\nfn dggs_h3_rotate_digit_60_cw(digit: u32) -> u32 {\n let values = array<u32, 8>(0u, 3u, 6u, 2u, 5u, 1u, 4u, 7u);\n return values[min(digit, 7u)];\n}\n\nfn dggs_h3_get_face_neighbor(face: u32, quadrant: u32) -> DggsH3FaceIJK {\n let neighborFaces = array<vec3u, 20>(\n vec3u(4u, 1u, 5u),\n vec3u(0u, 2u, 6u),\n vec3u(1u, 3u, 7u),\n vec3u(2u, 4u, 8u),\n vec3u(3u, 0u, 9u),\n vec3u(10u, 14u, 0u),\n vec3u(11u, 10u, 1u),\n vec3u(12u, 11u, 2u),\n vec3u(13u, 12u, 3u),\n vec3u(14u, 13u, 4u),\n vec3u(5u, 6u, 15u),\n vec3u(6u, 7u, 16u),\n vec3u(7u, 8u, 17u),\n vec3u(8u, 9u, 18u),\n vec3u(9u, 5u, 19u),\n vec3u(16u, 19u, 10u),\n vec3u(17u, 15u, 11u),\n vec3u(18u, 16u, 12u),\n vec3u(19u, 17u, 13u),\n vec3u(15u, 18u, 14u)\n );\n let neighborFace = neighborFaces[min(face, 19u)][min(quadrant, 3u) - 1u];\n let isOuterBand = face < 5u || face >= 15u;\n var translate = vec3i(0, 2, 2);\n if (quadrant == 1u) {\n translate = select(vec3i(2, 2, 0), vec3i(2, 0, 2), isOuterBand);\n } else if (quadrant == 2u) {\n translate = select(vec3i(2, 0, 2), vec3i(2, 2, 0), isOuterBand);\n }\n var counterClockwiseRotations = 3u;\n if (isOuterBand && quadrant == 1u) {\n counterClockwiseRotations = 1u;\n } else if (isOuterBand && quadrant == 2u) {\n counterClockwiseRotations = 5u;\n }\n return DggsH3FaceIJK(neighborFace, translate, counterClockwiseRotations);\n}\n\nfn dggs_h3_adjust_overage_class_ii(\n input: DggsH3FaceIJK,\n resolution: u32,\n pentagonLeading4: bool\n) -> DggsH3FaceIJKOverage {\n var faceIJK = input;\n let maximumDimension = dggs_h3_get_max_dim_by_cii_resolution(resolution);\n if (faceIJK.coord.x + faceIJK.coord.y + faceIJK.coord.z <= maximumDimension) {\n return DggsH3FaceIJKOverage(faceIJK, 0u);\n }\n\n var quadrant = 1u;\n if (faceIJK.coord.z > 0) {\n quadrant = select(2u, 3u, faceIJK.coord.y > 0);\n if (quadrant == 2u && pentagonLeading4) {\n let origin = vec3i(maximumDimension, 0, 0);\n faceIJK.coord = dggs_h3_ijk_rotate_60_cw(faceIJK.coord - origin) + origin;\n }\n }\n\n let neighbor = dggs_h3_get_face_neighbor(faceIJK.face, quadrant);\n faceIJK.face = neighbor.face;\n var rotation = 0u;\n loop {\n if (rotation >= neighbor.valid) {\n break;\n }\n faceIJK.coord = dggs_h3_ijk_rotate_60_ccw(faceIJK.coord);\n rotation += 1u;\n }\n faceIJK.coord = dggs_h3_ijk_normalize(\n faceIJK.coord + neighbor.coord * (maximumDimension / 2)\n );\n faceIJK.valid = 1u;\n return DggsH3FaceIJKOverage(faceIJK, 2u);\n}\n\nfn dggs_h3_get_center_face_ijk(index: vec2u) -> DggsH3FaceIJK {\n if (!dggs_h3_is_valid_cell_id(index)) {\n return DggsH3FaceIJK(0u, vec3i(0), 0u);\n }\n\n let baseCell = dggs_h3_get_base_cell(index);\n var faceIJK = dggs_h3_get_base_cell_home(baseCell);\n let cellResolution = dggs_h3_get_resolution(index);\n let isPentagonBaseCell = dggs_h3_is_base_cell_pentagon(baseCell);\n let leadingDigit = dggs_h3_get_leading_non_zero_digit(index);\n let rotateDigits = isPentagonBaseCell && leadingDigit == 5u;\n var resolution = 1u;\n loop {\n if (resolution > cellResolution) {\n break;\n }\n\n if (dggs_h3_is_resolution_class_iii(resolution)) {\n faceIJK.coord = dggs_h3_down_ap7(faceIJK.coord);\n } else {\n faceIJK.coord = dggs_h3_down_ap7r(faceIJK.coord);\n }\n var digit = dggs_h3_get_digit(index, resolution);\n if (rotateDigits) {\n digit = dggs_h3_rotate_digit_60_cw(digit);\n }\n faceIJK.coord = dggs_h3_neighbor(faceIJK.coord, digit);\n resolution += 1u;\n }\n\n let originalCoord = faceIJK.coord;\n var adjustedResolution = cellResolution;\n if (dggs_h3_is_resolution_class_iii(cellResolution)) {\n faceIJK.coord = dggs_h3_down_ap7r(faceIJK.coord);\n adjustedResolution += 1u;\n }\n\n var adjustment = dggs_h3_adjust_overage_class_ii(\n faceIJK,\n adjustedResolution,\n isPentagonBaseCell && leadingDigit == 4u\n );\n if (adjustment.overage != 0u) {\n faceIJK = adjustment.faceIJK;\n if (isPentagonBaseCell) {\n var adjustmentCount = 0u;\n loop {\n if (adjustmentCount >= 4u) {\n break;\n }\n adjustment = dggs_h3_adjust_overage_class_ii(faceIJK, adjustedResolution, false);\n if (adjustment.overage == 0u) {\n break;\n }\n faceIJK = adjustment.faceIJK;\n adjustmentCount += 1u;\n }\n }\n if (adjustedResolution != cellResolution) {\n faceIJK.coord = dggs_h3_up_ap7r(faceIJK.coord);\n }\n } else if (adjustedResolution != cellResolution) {\n faceIJK.coord = originalCoord;\n }\n return faceIJK;\n}\n\nfn dggs_h3_get_vertex_offset(resolution: u32, vertexIndex: u32) -> vec3i {\n let pointIndex = vertexIndex % 6u;\n if (dggs_h3_is_resolution_class_iii(resolution)) {\n let vertices = array<vec3i, 6>(\n vec3i(5, 4, 0),\n vec3i(1, 5, 0),\n vec3i(0, 5, 4),\n vec3i(0, 1, 5),\n vec3i(4, 0, 5),\n vec3i(5, 0, 1)\n );\n return vertices[pointIndex];\n }\n\n let vertices = array<vec3i, 6>(\n vec3i(2, 1, 0),\n vec3i(1, 2, 0),\n vec3i(0, 2, 1),\n vec3i(0, 1, 2),\n vec3i(1, 0, 2),\n vec3i(2, 0, 1)\n );\n return vertices[pointIndex];\n}\n\n// Boundary generation retains the original home-face coordinates. Center projection applies face\n// overage separately because rewriting the center face changes the substrate vertex construction.\nfn dggs_h3_get_boundary_center_face_ijk(index: vec2u) -> DggsH3FaceIJK {\n if (!dggs_h3_is_valid_cell_id(index)) {\n return DggsH3FaceIJK(0u, vec3i(0), 0u);\n }\n let baseCell = dggs_h3_get_base_cell(index);\n if (dggs_h3_is_base_cell_pentagon(baseCell)) {\n return DggsH3FaceIJK(0u, vec3i(0), 0u);\n }\n\n var faceIJK = dggs_h3_get_base_cell_home(baseCell);\n let cellResolution = dggs_h3_get_resolution(index);\n var resolution = 1u;\n loop {\n if (resolution > cellResolution) {\n break;\n }\n if (dggs_h3_is_resolution_class_iii(resolution)) {\n faceIJK.coord = dggs_h3_down_ap7(faceIJK.coord);\n } else {\n faceIJK.coord = dggs_h3_down_ap7r(faceIJK.coord);\n }\n faceIJK.coord = dggs_h3_neighbor(faceIJK.coord, dggs_h3_get_digit(index, resolution));\n resolution += 1u;\n }\n return faceIJK;\n}\n\nfn dggs_h3_get_boundary_vertex(index: vec2u, vertexIndex: u32) -> DggsH3BoundaryVertex {\n let center = dggs_h3_get_boundary_center_face_ijk(index);\n if (center.valid == 0u) {\n return DggsH3BoundaryVertex(0u, vec3i(0), 0u, 0u);\n }\n\n let cellResolution = dggs_h3_get_resolution(index);\n var adjustedResolution = cellResolution;\n var centerCoord = dggs_h3_down_ap3r(dggs_h3_down_ap3(center.coord));\n if (dggs_h3_is_resolution_class_iii(cellResolution)) {\n centerCoord = dggs_h3_down_ap7r(centerCoord);\n adjustedResolution += 1u;\n }\n\n let vertexCoord = dggs_h3_ijk_normalize(\n centerCoord + dggs_h3_get_vertex_offset(cellResolution, vertexIndex)\n );\n let maximumDimension = dggs_h3_get_max_dim_by_cii_resolution(adjustedResolution) * 3;\n let vertexDimension = vertexCoord.x + vertexCoord.y + vertexCoord.z;\n if (vertexDimension >= maximumDimension) {\n return DggsH3BoundaryVertex(center.face, vertexCoord, adjustedResolution, 0u);\n }\n\n return DggsH3BoundaryVertex(center.face, vertexCoord, adjustedResolution, 1u);\n}\n\nfn dggs_h3_is_single_face_boundary(index: vec2u) -> bool {\n var vertexIndex = 0u;\n loop {\n if (vertexIndex >= 6u) {\n break;\n }\n if (dggs_h3_get_boundary_vertex(index, vertexIndex).valid == 0u) {\n return false;\n }\n vertexIndex += 1u;\n }\n return true;\n}\n\nfn dggs_h3_ijk_to_hex2d(coord: vec3i) -> vec2f {\n let i = f32(coord.x - coord.z);\n let j = f32(coord.y - coord.z);\n return vec2f(i - 0.5 * j, j * DGGS_H3_SQRT3_2);\n}\n\nfn dggs_h3_get_center_unit_vector(index: vec2u) -> vec3f {\n let center = dggs_h3_get_center_face_ijk(index);\n if (center.valid == 0u) {\n return vec3f(0.0);\n }\n\n var hexPoint = dggs_h3_ijk_to_hex2d(center.coord);\n let resolution = dggs_h3_get_resolution(index);\n if (dggs_h3_is_resolution_class_iii(resolution)) {\n hexPoint = vec2f(\n 0.944911182523 * hexPoint.x - 0.327326835354 * hexPoint.y,\n 0.327326835354 * hexPoint.x + 0.944911182523 * hexPoint.y\n );\n }\n var scale = DGGS_H3_RES0_U_GNOMONIC;\n var resolutionIndex = 0u;\n loop {\n if (resolutionIndex >= resolution) {\n break;\n }\n scale *= DGGS_H3_RSQRT7;\n resolutionIndex += 1u;\n }\n return normalize(\n dggs_h3_get_face_unit_vector_basis(center.face, 0u) +\n scale * (\n hexPoint.x * dggs_h3_get_face_unit_vector_basis(center.face, 1u) +\n hexPoint.y * dggs_h3_get_face_unit_vector_basis(center.face, 2u)\n )\n );\n}\n\nfn dggs_h3_get_center_lnglat(index: vec2u) -> vec2f {\n let unitVector = dggs_h3_get_center_unit_vector(index);\n if (dot(unitVector, unitVector) < 0.5) {\n return vec2f(0.0);\n }\n return vec2f(\n atan2(unitVector.y, unitVector.x) * DGGS_RADIANS_TO_DEGREES,\n atan2(unitVector.z, length(unitVector.xy)) * DGGS_RADIANS_TO_DEGREES\n );\n}\n\nfn dggs_h3_geo_az_distance_rads(center: vec2f, azimuth: f32, distance: f32) -> vec2f {\n if (distance < 1.0e-7) {\n return center;\n }\n\n let positiveAzimuth = dggs_h3_positive_angle_rads(azimuth);\n let latitude = asin(clamp(\n sin(center.x) * cos(distance) + cos(center.x) * sin(distance) * cos(positiveAzimuth),\n -1.0,\n 1.0\n ));\n var longitude = 0.0;\n if (abs(latitude - DGGS_PI_OVER_2) < 1.0e-7) {\n return vec2f(DGGS_PI_OVER_2, 0.0);\n }\n if (abs(latitude + DGGS_PI_OVER_2) < 1.0e-7) {\n return vec2f(-DGGS_PI_OVER_2, 0.0);\n }\n\n let inverseCosLatitude = 1.0 / max(cos(latitude), 1.0e-8);\n let sinLongitude = clamp(\n sin(positiveAzimuth) * sin(distance) * inverseCosLatitude,\n -1.0,\n 1.0\n );\n let cosLongitude = clamp(\n (cos(distance) - sin(center.x) * sin(latitude)) / max(cos(center.x), 1.0e-8) *\n inverseCosLatitude,\n -1.0,\n 1.0\n );\n longitude = dggs_h3_constrain_longitude(center.y + atan2(sinLongitude, cosLongitude));\n return vec2f(latitude, longitude);\n}\n\nfn dggs_h3_hex2d_to_lnglat(point: vec2f, face: u32, resolution: u32, substrate: bool) -> vec2f {\n var radius = length(point);\n let center = dggs_h3_get_face_center_geo(face);\n if (radius < 1.0e-7) {\n return vec2f(center.y * DGGS_RADIANS_TO_DEGREES, center.x * DGGS_RADIANS_TO_DEGREES);\n }\n\n var theta = atan2(point.y, point.x);\n var resolutionIndex = 0u;\n loop {\n if (resolutionIndex >= resolution) {\n break;\n }\n radius *= DGGS_H3_RSQRT7;\n resolutionIndex += 1u;\n }\n\n if (substrate) {\n radius *= DGGS_H3_ONETHIRD;\n if (dggs_h3_is_resolution_class_iii(resolution)) {\n radius *= DGGS_H3_RSQRT7;\n }\n } else if (dggs_h3_is_resolution_class_iii(resolution)) {\n theta = dggs_h3_positive_angle_rads(theta + DGGS_H3_AP7_ROT_RADS);\n }\n\n radius = atan(radius * DGGS_H3_RES0_U_GNOMONIC);\n theta = dggs_h3_positive_angle_rads(dggs_h3_get_face_axis_azimuths(face).x - theta);\n let latitudeLongitude = dggs_h3_geo_az_distance_rads(center, theta, radius);\n return vec2f(\n latitudeLongitude.y * DGGS_RADIANS_TO_DEGREES,\n latitudeLongitude.x * DGGS_RADIANS_TO_DEGREES\n );\n}\n\nfn dggs_h3_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {\n if (!dggs_h3_is_single_face_boundary(index)) {\n return vec2f(0.0);\n }\n\n let vertex = dggs_h3_get_boundary_vertex(index, vertexIndex % 6u);\n let hexPoint = dggs_h3_ijk_to_hex2d(vertex.coord);\n return dggs_h3_hex2d_to_lnglat(hexPoint, vertex.face, vertex.resolution, true);\n}\n\nfn dggs_h3_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {\n return dggs_boundary_point_to_fp64_split(dggs_h3_get_boundary_point(index, vertexIndex));\n}\n\nfn dggs_s2_get_face(index: vec2u) -> u32 {\n return dggs_u64_extract_bits(index, 61u, 3u);\n}\n\nfn dggs_s2_get_level(index: vec2u) -> u32 {\n let trailingZeros = dggs_u64_count_trailing_zeros(index);\n if (trailingZeros > 60u) {\n return 0u;\n }\n return DGGS_S2_MAX_LEVEL - trailingZeros / 2u;\n}\n\nfn dggs_s2_is_valid_cell_id(index: vec2u) -> bool {\n let trailingZeros = dggs_u64_count_trailing_zeros(index);\n return !dggs_u64_is_zero(index) &&\n dggs_s2_get_face(index) <= 5u &&\n trailingZeros <= 60u &&\n trailingZeros % 2u == 0u;\n}\n\nfn dggs_s2_get_child_position(index: vec2u, level: u32) -> u32 {\n if (level == 0u || level > DGGS_S2_MAX_LEVEL) {\n return 0u;\n }\n return dggs_u64_extract_bits(index, 2u * (DGGS_S2_MAX_LEVEL - level) + 1u, 2u);\n}\n\n// Packed geohash keys store the geohash length in bits 60..63 and right-align\n// the base32 character codes in the lower 60 bits, first character first.\nfn dggs_geohash_get_length(index: vec2u) -> u32 {\n return min(\n dggs_u64_extract_bits(index, DGGS_GEOHASH_LENGTH_BIT_OFFSET, 4u),\n DGGS_GEOHASH_MAX_LENGTH\n );\n}\n\nfn dggs_geohash_get_character(index: vec2u, characterIndex: u32) -> u32 {\n let length = dggs_geohash_get_length(index);\n if (characterIndex >= length) {\n return 0u;\n }\n return dggs_u64_extract_bits(index, 5u * (length - characterIndex - 1u), 5u);\n}\n\n// Returns vec4(west, south, east, north) in longitude/latitude degrees.\nfn dggs_geohash_get_bounds(index: vec2u) -> vec4f {\n let length = dggs_geohash_get_length(index);\n var west = -180.0;\n var east = 180.0;\n var south = -90.0;\n var north = 90.0;\n var isLongitude = true;\n var characterIndex = 0u;\n loop {\n if (characterIndex >= length) {\n break;\n }\n\n let character = dggs_geohash_get_character(index, characterIndex);\n var bitIndex = 0u;\n loop {\n if (bitIndex >= 5u) {\n break;\n }\n\n let bit = (character >> (4u - bitIndex)) & 1u;\n if (isLongitude) {\n let midpoint = (west + east) * 0.5;\n if (bit == 0u) {\n east = midpoint;\n } else {\n west = midpoint;\n }\n } else {\n let midpoint = (south + north) * 0.5;\n if (bit == 0u) {\n north = midpoint;\n } else {\n south = midpoint;\n }\n }\n isLongitude = !isLongitude;\n bitIndex += 1u;\n }\n characterIndex += 1u;\n }\n\n return vec4f(west, south, east, north);\n}\n\nfn dggs_bounds_get_boundary_point(bounds: vec4f, vertexIndex: u32) -> vec2f {\n let pointIndex = vertexIndex % 4u;\n if (pointIndex == 0u) {\n return vec2f(bounds.x, bounds.w);\n }\n if (pointIndex == 1u) {\n return vec2f(bounds.z, bounds.w);\n }\n if (pointIndex == 2u) {\n return vec2f(bounds.z, bounds.y);\n }\n return vec2f(bounds.x, bounds.y);\n}\n\nfn dggs_geohash_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {\n return dggs_bounds_get_boundary_point(dggs_geohash_get_bounds(index), vertexIndex);\n}\n\nfn dggs_geohash_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {\n return dggs_boundary_point_to_fp64_split(dggs_geohash_get_boundary_point(index, vertexIndex));\n}\n\n// Packed quadkey keys store the quadkey length in bits 58..63 and right-align\n// the base4 digits in the lower 58 bits, first digit first.\nfn dggs_quadkey_get_length(index: vec2u) -> u32 {\n return min(\n dggs_u64_extract_bits(index, DGGS_QUADKEY_LENGTH_BIT_OFFSET, 6u),\n DGGS_QUADKEY_MAX_LENGTH\n );\n}\n\nfn dggs_quadkey_get_digit(index: vec2u, digitIndex: u32) -> u32 {\n let length = dggs_quadkey_get_length(index);\n if (digitIndex >= length) {\n return 0u;\n }\n return dggs_u64_extract_bits(index, 2u * (length - digitIndex - 1u), 2u);\n}\n\nfn dggs_quadkey_get_tile(index: vec2u) -> vec3u {\n let length = dggs_quadkey_get_length(index);\n var tileX = 0u;\n var tileY = 0u;\n var digitIndex = 0u;\n loop {\n if (digitIndex >= length) {\n break;\n }\n\n let digit = dggs_quadkey_get_digit(index, digitIndex);\n let mask = 1u << (length - digitIndex - 1u);\n if ((digit & 1u) != 0u) {\n tileX |= mask;\n }\n if ((digit & 2u) != 0u) {\n tileY |= mask;\n }\n digitIndex += 1u;\n }\n return vec3u(tileX, tileY, length);\n}\n\nfn dggs_web_mercator_tile_y_to_latitude(tileY: f32, tileScale: f32) -> f32 {\n let mercatorY = DGGS_PI * (1.0 - 2.0 * tileY / tileScale);\n return (2.0 * atan(exp(mercatorY)) - DGGS_PI * 0.5) * DGGS_RADIANS_TO_DEGREES;\n}\n\n// Returns vec4(west, south, east, north) in longitude/latitude degrees.\nfn dggs_quadkey_get_bounds(index: vec2u) -> vec4f {\n let tile = dggs_quadkey_get_tile(index);\n let tileScale = f32(1u << tile.z);\n let west = f32(tile.x) / tileScale * 360.0 - 180.0;\n let east = f32(tile.x + 1u) / tileScale * 360.0 - 180.0;\n let north = dggs_web_mercator_tile_y_to_latitude(f32(tile.y), tileScale);\n let south = dggs_web_mercator_tile_y_to_latitude(f32(tile.y + 1u), tileScale);\n return vec4f(west, south, east, north);\n}\n\nfn dggs_quadkey_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {\n return dggs_bounds_get_boundary_point(dggs_quadkey_get_bounds(index), vertexIndex);\n}\n\nfn dggs_quadkey_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {\n return dggs_boundary_point_to_fp64_split(dggs_quadkey_get_boundary_point(index, vertexIndex));\n}\n\nfn dggs_s2_digit_to_xy(digit: u32) -> vec2u {\n if (digit == 1u) {\n return vec2u(0u, 1u);\n }\n if (digit == 2u) {\n return vec2u(1u, 1u);\n }\n if (digit == 3u) {\n return vec2u(1u, 0u);\n }\n return vec2u(0u, 0u);\n}\n\nfn dggs_s2_rotate_and_flip_quadrant(size: u32, point: vec2u, digitXY: vec2u) -> vec2u {\n var result = point;\n if (digitXY.y == 0u) {\n if (digitXY.x == 1u) {\n result = vec2u(size - 1u - result.x, size - 1u - result.y);\n }\n result = result.yx;\n }\n return result;\n}\n\nfn dggs_s2_get_ij(index: vec2u) -> vec2u {\n let level = dggs_s2_get_level(index);\n var point = vec2u(0u);\n var hilbertLevel = 1u;\n loop {\n if (hilbertLevel > level) {\n break;\n }\n let digitLevel = level - hilbertLevel + 1u;\n let digitXY = dggs_s2_digit_to_xy(dggs_s2_get_child_position(index, digitLevel));\n let size = 1u << (hilbertLevel - 1u);\n point = dggs_s2_rotate_and_flip_quadrant(size, point, digitXY);\n point += size * digitXY;\n hilbertLevel += 1u;\n }\n if ((dggs_s2_get_face(index) & 1u) != 0u) {\n point = point.yx;\n }\n return point;\n}\n\nfn dggs_s2_single_st_to_uv(st: f32) -> f32 {\n if (st >= 0.5) {\n return (4.0 * st * st - 1.0) / 3.0;\n }\n return (1.0 - 4.0 * (1.0 - st) * (1.0 - st)) / 3.0;\n}\n\nfn dggs_s2_ij_to_st(ij: vec2u, level: u32, offset: vec2f) -> vec2f {\n let maxSize = f32(1u << level);\n return (vec2f(f32(ij.x), f32(ij.y)) + offset) / maxSize;\n}\n\nfn dggs_s2_st_to_uv(st: vec2f) -> vec2f {\n return vec2f(dggs_s2_single_st_to_uv(st.x), dggs_s2_single_st_to_uv(st.y));\n}\n\nfn dggs_s2_face_uv_to_xyz(face: u32, uv: vec2f) -> vec3f {\n if (face == 0u) {\n return vec3f(1.0, uv.x, uv.y);\n }\n if (face == 1u) {\n return vec3f(-uv.x, 1.0, uv.y);\n }\n if (face == 2u) {\n return vec3f(-uv.x, -uv.y, 1.0);\n }\n if (face == 3u) {\n return vec3f(-1.0, -uv.y, -uv.x);\n }\n if (face == 4u) {\n return vec3f(uv.y, -1.0, -uv.x);\n }\n return vec3f(uv.y, uv.x, -1.0);\n}\n\nfn dggs_s2_xyz_to_lnglat(xyz: vec3f) -> vec2f {\n let latitude = atan2(xyz.z, length(xyz.xy)) * DGGS_RADIANS_TO_DEGREES;\n let longitude = atan2(xyz.y, xyz.x) * DGGS_RADIANS_TO_DEGREES;\n return vec2f(longitude, latitude);\n}\n\nfn dggs_s2_get_boundary_offset(vertexIndex: u32) -> vec2f {\n let pointIndex = vertexIndex % 4u;\n if (pointIndex == 1u) {\n return vec2f(0.0, 1.0);\n }\n if (pointIndex == 2u) {\n return vec2f(1.0, 1.0);\n }\n if (pointIndex == 3u) {\n return vec2f(1.0, 0.0);\n }\n return vec2f(0.0, 0.0);\n}\n\nfn dggs_s2_get_boundary_point(index: vec2u, vertexIndex: u32) -> vec2f {\n let level = dggs_s2_get_level(index);\n let ij = dggs_s2_get_ij(index);\n let st = dggs_s2_ij_to_st(ij, level, dggs_s2_get_boundary_offset(vertexIndex));\n let uv = dggs_s2_st_to_uv(st);\n let xyz = dggs_s2_face_uv_to_xyz(dggs_s2_get_face(index), uv);\n return dggs_s2_xyz_to_lnglat(xyz);\n}\n\nfn dggs_s2_get_boundary_point_fp64_split(index: vec2u, vertexIndex: u32) -> vec4f {\n return dggs_boundary_point_to_fp64_split(dggs_s2_get_boundary_point(index, vertexIndex));\n}\n" =dggsWGSL