335 lines
8.1 KiB
TypeScript
335 lines
8.1 KiB
TypeScript
import { Quaternion } from "./Quaternion";
|
|
import type { XMLNode } from 'xmlbuilder';
|
|
|
|
export class Vector4
|
|
{
|
|
public x: number;
|
|
public y: number;
|
|
public z: number;
|
|
public w: number;
|
|
|
|
public constructor(buf?: Buffer | number[] | Vector4 | number, pos?: number, double?: boolean | number, w?: number)
|
|
{
|
|
if (typeof buf === 'number' && typeof pos === 'number' && typeof double === 'number' && typeof w === 'number')
|
|
{
|
|
this.x = buf;
|
|
this.y = pos;
|
|
this.z = double;
|
|
this.w = w;
|
|
}
|
|
else if (buf instanceof Vector4)
|
|
{
|
|
this.x = buf.x;
|
|
this.y = buf.y;
|
|
this.z = buf.z;
|
|
this.w = buf.w;
|
|
}
|
|
else
|
|
{
|
|
if (double === undefined)
|
|
{
|
|
double = false;
|
|
}
|
|
if (buf instanceof Buffer)
|
|
{
|
|
if (pos === undefined)
|
|
{
|
|
pos = 0;
|
|
}
|
|
if (double === true)
|
|
{
|
|
this.x = buf.readDoubleLE(pos);
|
|
this.y = buf.readDoubleLE(pos + 8);
|
|
this.z = buf.readDoubleLE(pos + 16);
|
|
this.w = buf.readDoubleLE(pos + 24);
|
|
}
|
|
else
|
|
{
|
|
this.x = buf.readFloatLE(pos);
|
|
this.y = buf.readFloatLE(pos + 4);
|
|
this.z = buf.readFloatLE(pos + 8);
|
|
this.w = buf.readFloatLE(pos + 12);
|
|
}
|
|
}
|
|
else if (buf !== undefined && Array.isArray(buf) && buf.length > 3)
|
|
{
|
|
if (typeof buf[0] !== 'number' || typeof buf[1] !== 'number' || typeof buf[2] !== 'number' || typeof buf[3] !== 'number')
|
|
{
|
|
throw new Error('Array contains non-numbers');
|
|
}
|
|
[this.x, this.y, this.z, this.w] = buf;
|
|
}
|
|
else
|
|
{
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.z = 0;
|
|
this.w = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static getXML(doc: XMLNode, v?: Vector4): void
|
|
{
|
|
if (v === undefined)
|
|
{
|
|
v = Vector4.getZero();
|
|
}
|
|
doc.ele('X', v.x);
|
|
doc.ele('Y', v.y);
|
|
doc.ele('Z', v.z);
|
|
doc.ele('W', v.w);
|
|
}
|
|
|
|
|
|
public static getZero(): Vector4
|
|
{
|
|
return new Vector4();
|
|
}
|
|
|
|
public writeToBuffer(buf: Buffer, pos: number, double = false): void
|
|
{
|
|
if (double)
|
|
{
|
|
buf.writeDoubleLE(this.x, pos);
|
|
buf.writeDoubleLE(this.y, pos + 8);
|
|
buf.writeDoubleLE(this.z, pos + 16);
|
|
buf.writeDoubleLE(this.w, pos + 24);
|
|
}
|
|
else
|
|
{
|
|
buf.writeFloatLE(this.x, pos);
|
|
buf.writeFloatLE(this.y, pos + 4);
|
|
buf.writeFloatLE(this.z, pos + 8);
|
|
buf.writeFloatLE(this.w, pos + 12);
|
|
}
|
|
}
|
|
|
|
public toString(): string
|
|
{
|
|
return `<${this.x}, ${this.y}, ${this.z}, ${this.w}>`;
|
|
}
|
|
|
|
public getBuffer(double = false): Buffer
|
|
{
|
|
const buf = Buffer.allocUnsafe(double ? 32 : 16);
|
|
this.writeToBuffer(buf, 0, double);
|
|
return buf;
|
|
}
|
|
|
|
public compareApprox(vec: Vector4): boolean
|
|
{
|
|
return this.equals(vec, 0.00001);
|
|
}
|
|
|
|
public toArray(): number[]
|
|
{
|
|
return [this.x, this.y, this.z, this.w];
|
|
}
|
|
|
|
public equals(vec: Vector4, epsilon = Number.EPSILON): boolean
|
|
{
|
|
return (
|
|
Math.abs(this.x - vec.x) < epsilon &&
|
|
Math.abs(this.y - vec.y) < epsilon &&
|
|
Math.abs(this.z - vec.z) < epsilon &&
|
|
Math.abs(this.w - vec.w) < epsilon
|
|
);
|
|
}
|
|
|
|
public dot(vec: Vector4): number
|
|
{
|
|
return this.x * vec.x + this.y * vec.y + this.z * vec.z + this.w * vec.w;
|
|
}
|
|
|
|
public distance(vec: Vector4): number
|
|
{
|
|
return Math.sqrt(this.squaredDistance(vec));
|
|
}
|
|
|
|
public squaredDistance(vec: Vector4): number
|
|
{
|
|
const dx = this.x - vec.x;
|
|
const dy = this.y - vec.y;
|
|
const dz = this.z - vec.z;
|
|
const dw = this.w - vec.w;
|
|
return dx * dx + dy * dy + dz * dz + dw * dw;
|
|
}
|
|
|
|
public direction(vec: Vector4): Vector4
|
|
{
|
|
return vec.difference(this).normalize();
|
|
}
|
|
|
|
public mix(vec: Vector4, t: number): Vector4
|
|
{
|
|
return new Vector4([
|
|
this.x + t * (vec.x - this.x),
|
|
this.y + t * (vec.y - this.y),
|
|
this.z + t * (vec.z - this.z),
|
|
this.w + t * (vec.w - this.w),
|
|
]);
|
|
}
|
|
|
|
public sum(vec: Vector4): Vector4
|
|
{
|
|
return new Vector4([
|
|
this.x + vec.x,
|
|
this.y + vec.y,
|
|
this.z + vec.z,
|
|
this.w + vec.w,
|
|
]);
|
|
}
|
|
|
|
public difference(vec: Vector4): Vector4
|
|
{
|
|
return new Vector4([
|
|
this.x - vec.x,
|
|
this.y - vec.y,
|
|
this.z - vec.z,
|
|
this.w - vec.w,
|
|
]);
|
|
}
|
|
|
|
public product(vec: Vector4): Vector4
|
|
{
|
|
return new Vector4([
|
|
this.x * vec.x,
|
|
this.y * vec.y,
|
|
this.z * vec.z,
|
|
this.w * vec.w,
|
|
]);
|
|
}
|
|
|
|
public quotient(vec: Vector4): Vector4
|
|
{
|
|
return new Vector4([
|
|
this.x / vec.x,
|
|
this.y / vec.y,
|
|
this.z / vec.z,
|
|
this.w / vec.w,
|
|
]);
|
|
}
|
|
|
|
public reset(): void
|
|
{
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.z = 0;
|
|
this.w = 0;
|
|
}
|
|
|
|
public copy(): Vector4
|
|
{
|
|
return new Vector4(this);
|
|
}
|
|
|
|
public toJSON(): object
|
|
{
|
|
return {
|
|
values: {
|
|
'0': this.x,
|
|
'1': this.y,
|
|
'2': this.z,
|
|
'3': this.w
|
|
}
|
|
}
|
|
}
|
|
|
|
public negate(): Vector4
|
|
{
|
|
return new Vector4([
|
|
-this.x,
|
|
-this.y,
|
|
-this.z,
|
|
-this.w,
|
|
]);
|
|
}
|
|
|
|
public length(): number
|
|
{
|
|
return Math.sqrt(this.squaredLength());
|
|
}
|
|
|
|
public squaredLength(): number
|
|
{
|
|
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;
|
|
}
|
|
|
|
public add(vec: Vector4): this
|
|
{
|
|
this.x += vec.x;
|
|
this.y += vec.y;
|
|
this.z += vec.z;
|
|
this.w += vec.w;
|
|
return this;
|
|
}
|
|
|
|
public subtract(vec: Vector4): this
|
|
{
|
|
this.x -= vec.x;
|
|
this.y -= vec.y;
|
|
this.z -= vec.z;
|
|
this.w -= vec.w;
|
|
return this;
|
|
}
|
|
|
|
public multiply(value: number | Vector4): this
|
|
{
|
|
if (typeof value === 'number')
|
|
{
|
|
this.x *= value;
|
|
this.y *= value;
|
|
this.z *= value;
|
|
this.w *= value;
|
|
}
|
|
else
|
|
{
|
|
this.x *= value.x;
|
|
this.y *= value.y;
|
|
this.z *= value.z;
|
|
this.w *= value.w;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public divide(value: number | Vector4): this
|
|
{
|
|
if (typeof value === 'number')
|
|
{
|
|
this.x /= value;
|
|
this.y /= value;
|
|
this.z /= value;
|
|
this.w /= value;
|
|
}
|
|
else
|
|
{
|
|
this.x /= value.x;
|
|
this.y /= value.y;
|
|
this.z /= value.z;
|
|
this.w /= value.w;
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public scale(scalar: number): this
|
|
{
|
|
return this.multiply(scalar);
|
|
}
|
|
|
|
public normalize(): this
|
|
{
|
|
const len = this.length();
|
|
if (len > 0)
|
|
{
|
|
this.scale(1 / len);
|
|
}
|
|
return this;
|
|
}
|
|
|
|
public toQuaternion(): Quaternion
|
|
{
|
|
return new Quaternion(this.x, this.y, this.z, this.w);
|
|
}
|
|
}
|