
function reduce_even_wrap(x) {
    // reduces to the range [-1, 3] by removing *even* windings:
    // ... -3, -1, 1, 3, 5 ... → cycle every 2
    return x - 2 * Math.floor((x + 1) * 0.5);
}

/**
 * Octahedral UV wrapping.
 * Full winding support without breaking octahedral seam rules.
 *
 * ```
 *    +-----+-----+
 *    | 6  /|\  5 |
 *    |   / | \   |
 *    |  /  |  \  |
 *    | / 2 | 1 \ |
 *    |/    |    \|
 *    +-----+-----+
 *    |\  3 | 4  /|
 *    | \   |   / |
 *    |  \  |  /  |
 *    |   \ | /   |
 *    | 7  \|/  8 |
 *    +-----+-----+
 *```
 *
 * @param {number[]} out_uv - Array to write the wrapped UVs into
 * @param {number} out_offset - Index to start writing at
 * @param {number[]} in_uv - Input UV array (can be outside 0..1)
 * @param {number} in_offset - Index to start reading from
 *
 * @author Alex Goldring
 * @copyright Company Named Limited (c) 2026
 */
export function octahedral_uv_wrap(
    out_uv, out_offset,
    in_uv, in_offset
) {
    let u = in_uv[in_offset];
    let v = in_uv[in_offset + 1];

    // First collapse infinite windings into [-1, 1] range.
    u = reduce_even_wrap(u);
    v = reduce_even_wrap(v);

    // Left side (u < 0)
    if (u < 0) {
        u = -u;
        v = 1 - v;
    }
    // Right side (u > 1)
    else if (u > 1) {
        u = 2 - u;
        v = 1 - v;
    }

    // Top (v < 0)
    if (v < 0) {
        v = -v;
        u = 1 - u;
    }
    // Bottom (v > 1)
    else if (v > 1) {
        v = 2 - v;
        u = 1 - u;
    }

    out_uv[out_offset]     = u;
    out_uv[out_offset + 1] = v;
}