export function random(minIncl = 0, maxIncl = 1) {
    return Math.random() * (maxIncl - minIncl) + minIncl;
}
export function randomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1)) + min;
}
export function randomBool() {
    return Math.random() < 0.5;
}
export function intToRGB(i) {
    const r = (i >> 16) & 0xff;
    const g = (i >> 8) & 0xff;
    const b = i & 0xff;
    const a = (i >> 24) & 0xff;
    return { red: r / 255, green: g / 255, blue: b / 255, alpha: a / 255 };
}
export function RGBToInt(r, g, b) {
    return (r << 16) | (g << 8) | b;
}
/**
 * Rotates vector `v` around axis `axis` by `angle` radians.
 * Uses Rodrigues' rotation formula.
 */
export function rotateAroundAxis(v, axis, angle) {
    const cosA = Math.cos(angle);
    const sinA = Math.sin(angle);
    const dot = v.x * axis.x + v.y * axis.y + v.z * axis.z;
    const cross = vecCross(axis, v);
    const term1 = vecScale(v, cosA);
    const term2 = vecScale(cross, sinA);
    const term3 = vecScale(axis, dot * (1 - cosA));
    return vecSum(vecSum(term1, term2), term3);
}
export function clamp(value, min, max) {
    return Math.min(Math.max(value, min), max);
}
//mega dumb
export function vecToString(vec) {
    return "[" + vec.x + " , " + vec.y + " , " + vec.z + "]";
}
export function vecEqual(a, b) {
    return a.x === b.x && a.y === b.y && a.z === b.z;
}
export function vecScale(v, a) {
    return { x: v.x * a, y: v.y * a, z: v.z * a };
}
export function vecSum(a, b) {
    return { x: a.x + b.x, y: a.y + b.y, z: a.z + b.z };
}
export function firstNonZero(a) {
    if (a.x != 0)
        return a.x;
    if (a.y != 0)
        return a.y;
    if (a.z != 0)
        return a.z;
    return 0;
}
export function vecMul(a, b) {
    return { x: a.x * b.x, y: a.y * b.y, z: a.z * b.z };
}
export function vecDiff(a, b) {
    return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
}
export function vecAdd(a, x, y, z) {
    return { x: a.x + x, y: a.y + y, z: a.z + z };
}
export function lengthSquared(vec) {
    return vec.x * vec.x + vec.y * vec.y + vec.z * vec.z;
}
export function printVec(vec, message = "vec") {
    console.error(message + ": " + vec.x + " " + vec.y + " " + vec.z);
}
export const reflectVec = (v, n) => {
    const dot = v.x * n.x + v.y * n.y + v.z * n.z;
    return {
        x: v.x - 2 * dot * n.x,
        y: v.y - 2 * dot * n.y,
        z: v.z - 2 * dot * n.z
    };
};
export function cardinalMagnitude(vec) {
    if (vec.x !== 0)
        return Math.abs(vec.x);
    if (vec.y !== 0)
        return Math.abs(vec.y);
    if (vec.z !== 0)
        return Math.abs(vec.z);
    return 0;
}
export function wrapDegrees(f) {
    let g = f % 360;
    if (g >= 180) {
        g -= 360;
    }
    if (g < -180) {
        g += 360;
    }
    return g;
}
export function degreesDifferenceAbs(f, g) {
    return Math.abs(wrapDegrees(g - f));
}
export function vecCross(a, b) {
    return {
        x: a.y * b.z - a.z * b.y,
        y: a.z * b.x - a.x * b.z,
        z: a.x * b.y - a.y * b.x
    };
}
export function vecNormalize(v) {
    const lenSq = lengthSquared(v);
    if (lenSq === 0) {
        return [0, { x: 0, y: 0, z: 0 }];
    }
    const len = Math.sqrt(lenSq);
    return [len, { x: v.x / len, y: v.y / len, z: v.z / len }];
}
export function toDegrees(radians) {
    return radians * (180 / Math.PI);
}
export function toRadians(degrees) {
    return degrees * (Math.PI / 180);
}
export function map(value, in_min, in_max, out_min, out_max) {
    return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
// Rotate vector 'v' by 90° around the axis given by 'axis'.
// Using the right-hand rule, we return cross(axis, v) for +90° and its negative for –90°.
export function rotateVector(v, axis, clockwise = true) {
    // axis must be a unit vector pointing in a cardinal direction
    const cross = {
        x: axis.y * v.z - axis.z * v.y,
        y: axis.z * v.x - axis.x * v.z,
        z: axis.x * v.y - axis.y * v.x,
    };
    return clockwise ? cross : { x: -cross.x, y: -cross.y, z: -cross.z };
}
export function* traverseGrid(start, direction) {
    // Current voxel coords
    let x = Math.floor(start.x);
    let y = Math.floor(start.y);
    let z = Math.floor(start.z);
    // Step direction (±1)
    const stepX = Math.sign(direction.x) || 1;
    const stepY = Math.sign(direction.y) || 1;
    const stepZ = Math.sign(direction.z) || 1;
    // Avoid division by zero—set large delta if direction is 0
    const tDeltaX = direction.x !== 0 ? Math.abs(1 / direction.x) : Number.POSITIVE_INFINITY;
    const tDeltaY = direction.y !== 0 ? Math.abs(1 / direction.y) : Number.POSITIVE_INFINITY;
    const tDeltaZ = direction.z !== 0 ? Math.abs(1 / direction.z) : Number.POSITIVE_INFINITY;
    // Fractional part of start within its voxel
    const fracX = start.x - Math.floor(start.x);
    const fracY = start.y - Math.floor(start.y);
    const fracZ = start.z - Math.floor(start.z);
    // Initial tMax: distance along ray to the first voxel boundary
    const tMaxX = direction.x !== 0
        ? tDeltaX * (stepX > 0 ? (1 - fracX) : fracX)
        : Number.POSITIVE_INFINITY;
    const tMaxY = direction.y !== 0
        ? tDeltaY * (stepY > 0 ? (1 - fracY) : fracY)
        : Number.POSITIVE_INFINITY;
    const tMaxZ = direction.z !== 0
        ? tDeltaZ * (stepZ > 0 ? (1 - fracZ) : fracZ)
        : Number.POSITIVE_INFINITY;
    // The normalized length of the ray (so t goes from 0 to 1)
    const maxT = 1.0;
    // Current t parameter along the ray
    let t = 0.0;
    // Yield the starting voxel
    yield { x, y, z };
    // Traverse until we pass the end point
    let currTMaxX = tMaxX;
    let currTMaxY = tMaxY;
    let currTMaxZ = tMaxZ;
    while (t <= maxT) {
        // Step to the next voxel boundary
        if (currTMaxX < currTMaxY) {
            if (currTMaxX < currTMaxZ) {
                // X is smallest
                x += stepX;
                t = currTMaxX;
                currTMaxX += tDeltaX;
            }
            else {
                // Z is smallest
                z += stepZ;
                t = currTMaxZ;
                currTMaxZ += tDeltaZ;
            }
        }
        else {
            if (currTMaxY < currTMaxZ) {
                // Y is smallest
                y += stepY;
                t = currTMaxY;
                currTMaxY += tDeltaY;
            }
            else {
                // Z is smallest
                z += stepZ;
                t = currTMaxZ;
                currTMaxZ += tDeltaZ;
            }
        }
        // If t > 1, we've gone past the end point
        if (t > maxT)
            break;
        yield { x, y, z };
    }
}
