import { degToRad } from './utils';
import WBVector from './wbVector';
export default class WBVector3 extends WBVector {
    constructor(x, y, z) {
        super();
        this.x = x;
        this.y = y;
        this.z = z;
    }
    static fromMC(vec3) {
        return new WBVector3(vec3.x, vec3.y, vec3.z);
    }
    add(other) {
        return new WBVector3(this.x + other.x, this.y + other.y, this.z + other.z);
    }
    subtract(other) {
        return new WBVector3(this.x - other.x, this.y - other.y, this.z - other.z);
    }
    transform(transformFn) {
        return new WBVector3(transformFn(this.x), transformFn(this.y), transformFn(this.z));
    }
    multiply(value) {
        return new WBVector3(this.x * value, this.y * value, this.z * value);
    }
    divide(value) {
        return new WBVector3(this.x / value, this.y / value, this.z / value);
    }
    length() {
        return Math.sqrt(this.x ** 2 + this.y ** 2 + this.z ** 2);
    }
    normalize() {
        const length = this.length();
        return new WBVector3(this.x / length, this.y / length, this.z / length);
    }
    dot(other) {
        return this.x * other.x + this.y * other.y + this.z * other.z;
    }
    cross(other) {
        return new WBVector3(this.y * other.z - this.z * other.y, this.z * other.x - this.x * other.z, this.x * other.y - this.y * other.x);
    }
    angleDegrees(other) {
        // cos(theta) = (A . B) / (|A| * |B|)
        const dot = this.dot(other);
        const lengthA = this.length();
        const lengthB = other.length();
        const cosTheta = dot / (lengthA * lengthB);
        const angle = Math.acos(cosTheta) * (180 / Math.PI);
        const cross = this.cross(other);
        if (cross.y < 0)
            return -angle;
        return angle;
    }
    getRotation() {
        const xzProjection = new WBVector3(this.x, 0, this.z);
        const y = xzProjection.angleDegrees(new WBVector3(0, 0, 1));
        // cos(theta) = (A . B) / (|A| * |B|)
        const dot = this.dot(xzProjection);
        const lengthA = this.length();
        const lengthB = xzProjection.length();
        const cosTheta = dot / (lengthA * lengthB);
        let x = Math.acos(cosTheta) * (180 / Math.PI);
        if (this.y >= 0) {
            x = -x;
        }
        // x can be NaN
        return { x: x || 0, y };
    }
    /** Rotates around the local(!) X axis, sort of, it's close enough */
    rotateX(degrees) {
        const angle = degToRad(degrees);
        const length = this.length();
        return new WBVector3(this.x * Math.cos(angle), this.y - Math.sin(angle), this.z * Math.cos(angle)).normalize().multiply(length);
    }
    rotateY(degrees) {
        const angle = degToRad(degrees);
        return new WBVector3(this.x * Math.cos(angle) - this.z * Math.sin(angle), this.y, this.x * Math.sin(angle) + this.z * Math.cos(angle));
    }
    rotate(degrees) {
        return this.rotateX(degrees.x).rotateY(degrees.y);
    }
    roundAll() {
        return new WBVector3(Math.round(this.x), Math.round(this.y), Math.round(this.z));
    }
    floorAll() {
        return new WBVector3(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
    }
    ceilAll() {
        return new WBVector3(Math.floor(this.x), Math.floor(this.y), Math.floor(this.z));
    }
    abs() {
        return new WBVector3(Math.abs(this.x), Math.abs(this.y), Math.abs(this.z));
    }
    equals(other) {
        return this.x === other.x && this.y === other.y && this.z === other.z;
    }
    toString() {
        return `Vec3(${this.x}, ${this.y}, ${this.z})`;
    }
    static distanceSquared(a, b) {
        return (a.x - b.x) ** 2 + (a.y - b.y) ** 2 + (a.z - b.z) ** 2;
    }
    static distance(a, b) {
        return Math.sqrt(WBVector3.distanceSquared(a, b));
    }
    toTuple() {
        return [this.x, this.y, this.z];
    }
    *[Symbol.iterator]() {
        yield this.x;
        yield this.y;
        yield this.z;
    }
}
