2025-01-17 23:37:54 +00:00
|
|
|
import type { XMLNode } from 'xmlbuilder';
|
2018-10-12 14:34:43 +01:00
|
|
|
|
2025-01-17 23:37:54 +00:00
|
|
|
export class Vector2
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
public x: number;
|
|
|
|
|
public y: number;
|
|
|
|
|
|
|
|
|
|
public constructor(buf?: Buffer | number[] | Vector2 | number, pos?: number, double?: boolean | number)
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
if (typeof buf === 'number' && typeof pos === 'number')
|
|
|
|
|
{
|
|
|
|
|
this.x = buf;
|
|
|
|
|
this.y = pos;
|
|
|
|
|
}
|
|
|
|
|
else if (buf instanceof Vector2)
|
|
|
|
|
{
|
|
|
|
|
this.x = buf.x;
|
|
|
|
|
this.y = buf.y;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (double === undefined)
|
|
|
|
|
{
|
|
|
|
|
double = false;
|
|
|
|
|
}
|
|
|
|
|
if (buf !== undefined && pos !== undefined && buf instanceof Buffer)
|
|
|
|
|
{
|
|
|
|
|
if (double === false)
|
|
|
|
|
{
|
|
|
|
|
this.x = buf.readFloatLE(pos);
|
|
|
|
|
this.y = buf.readFloatLE(pos + 4);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.x = buf.readDoubleLE(pos);
|
|
|
|
|
this.y = buf.readDoubleLE(pos + 8);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (buf !== undefined && Array.isArray(buf) && buf.length >= 2)
|
|
|
|
|
{
|
|
|
|
|
if (typeof buf[0] !== 'number' || typeof buf[1] !== 'number')
|
|
|
|
|
{
|
|
|
|
|
throw new Error('Array contains non-numbers');
|
|
|
|
|
}
|
|
|
|
|
[this.x, this.y] = buf;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.x = 0;
|
|
|
|
|
this.y = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
2025-01-17 23:37:54 +00:00
|
|
|
public static getXML(doc: XMLNode, v?: Vector2): void
|
- Add "GET" method to Caps
- New events: ObjectPhysicsDataEvent, ParcelPropertiesEvent, NewObjectEvent, ObjectUpdateEvent, ObjectKilledEvent
- Added getXML function to Color4, Vector2, Vector3, Vector4, GameObject, Region, Quaternion, UUID for opensim-compatible XML export
- Added TextureAnim and ParticleSystem decoding to the "full" ObjectStore
- Object store will automatically request missing "parent" prims
- "setPersist" - When persist is TRUE, the ObjectStore will not forget about "killed" prims - useful for region scanning
- Support for Flexible params, Light params, LightImage params, Mesh data, Sculpt maps
- Fixed object scale being incorrectly calculated
- Add terrain decoding (this was a ballache)
- Add parcel map decoding
- Add support for region windlight settings (region.environment)
- Add support for materials (normal / specular maps)
- Add getBuffer, getLong and bitwiseOr to UUID
- Added a circular-reference-safe JSONStringify to Utils
- Add XferFile capability to Circuit
PUBLIC API:
AssetCommands:
- Rework "downloadAsset" to detect failures
- NEW: downloadInventoryAsset() - uses TransferRequest for prim inventory items
- NEW: getMaterials() - resolves material UUIDs
RegionCommands:
- NEW: getTerrainTextures()
- NEW: exportSettings() - OpenSim XML export of region settings
- NEW: async getTerrain() - Get binary terrain heightmap, 256x256 float32
- resolveObjects() - now fetches task inventory contents too.
- resolveObjects() - fix calculation of land impact
- NEW: getObjectByLocalID(localID: number, timeout: number)
- NEW: getObjectByUUID(uuid: UUID, timeout: number)
- NEW: getParcels();
- NEW: pruneObjects - removes missing GameObjects from a list
- NEW: setPersist - prevent objectstore from forgetting about killed gameobjects
2018-10-31 11:28:24 +00:00
|
|
|
{
|
|
|
|
|
if (v === undefined)
|
|
|
|
|
{
|
|
|
|
|
v = Vector2.getZero();
|
|
|
|
|
}
|
|
|
|
|
doc.ele('X', v.x);
|
|
|
|
|
doc.ele('Y', v.y);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-17 23:37:54 +00:00
|
|
|
public static getZero(): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2(0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public writeToBuffer(buf: Buffer, pos: number, double = false): void
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
if (double)
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
buf.writeDoubleLE(this.x, pos);
|
|
|
|
|
buf.writeDoubleLE(this.y, pos + 8);
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
2025-01-17 23:37:54 +00:00
|
|
|
else
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
buf.writeFloatLE(this.x, pos);
|
|
|
|
|
buf.writeFloatLE(this.y, pos + 4);
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
2025-01-17 23:37:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public toString(): string
|
|
|
|
|
{
|
|
|
|
|
return `<${this.x}, ${this.y}>`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public getBuffer(double = false): Buffer
|
|
|
|
|
{
|
|
|
|
|
const buf = Buffer.allocUnsafe(double ? 16 : 8);
|
|
|
|
|
this.writeToBuffer(buf, 0, double);
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public compareApprox(vec: Vector2): boolean
|
|
|
|
|
{
|
|
|
|
|
return this.equals(vec, 0.00001);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public toArray(): number[]
|
|
|
|
|
{
|
|
|
|
|
return [this.x, this.y];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public equals(vec: Vector2, epsilon = Number.EPSILON): boolean
|
|
|
|
|
{
|
|
|
|
|
return (
|
|
|
|
|
Math.abs(this.x - vec.x) < epsilon &&
|
|
|
|
|
Math.abs(this.y - vec.y) < epsilon
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public dot(vec: Vector2): number
|
|
|
|
|
{
|
|
|
|
|
return this.x * vec.x + this.y * vec.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public cross(vec: Vector2): number
|
|
|
|
|
{
|
|
|
|
|
// In 2D, the cross product is a scalar representing the magnitude of the perpendicular vector
|
|
|
|
|
return this.x * vec.y - this.y * vec.x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public distance(vec: Vector2): number
|
|
|
|
|
{
|
|
|
|
|
return Math.sqrt(this.squaredDistance(vec));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public squaredDistance(vec: Vector2): number
|
|
|
|
|
{
|
|
|
|
|
const dx = this.x - vec.x;
|
|
|
|
|
const dy = this.y - vec.y;
|
|
|
|
|
return dx * dx + dy * dy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public direction(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
const diff = vec.subtract(this).normalize();
|
|
|
|
|
if (diff.x === 0.0 && diff.y === 0.0)
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
diff.x = NaN;
|
|
|
|
|
diff.y = NaN;
|
|
|
|
|
}
|
|
|
|
|
return diff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public mix(vec: Vector2, t: number): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x + t * (vec.x - this.x),
|
|
|
|
|
this.y + t * (vec.y - this.y),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sum(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x + vec.x,
|
|
|
|
|
this.y + vec.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public difference(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x - vec.x,
|
|
|
|
|
this.y - vec.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public product(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x * vec.x,
|
|
|
|
|
this.y * vec.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public quotient(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x / vec.x,
|
|
|
|
|
this.y / vec.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public reset(): void
|
|
|
|
|
{
|
|
|
|
|
this.x = 0;
|
|
|
|
|
this.y = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public copy(): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public negate(): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
-this.x,
|
|
|
|
|
-this.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public length(): number
|
|
|
|
|
{
|
|
|
|
|
return Math.sqrt(this.squaredLength());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public squaredLength(): number
|
|
|
|
|
{
|
|
|
|
|
return this.x * this.x + this.y * this.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public add(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x + vec.x,
|
|
|
|
|
this.y + vec.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public subtract(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x - vec.x,
|
|
|
|
|
this.y - vec.y,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public multiply(value: number | Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
if (typeof value === 'number')
|
|
|
|
|
{
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x * value,
|
|
|
|
|
this.y * value,
|
|
|
|
|
]);
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
return new Vector2([
|
|
|
|
|
this.x * value.x,
|
|
|
|
|
this.y * value.y,
|
|
|
|
|
]);
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-12-03 13:55:02 +00:00
|
|
|
|
2025-01-17 23:37:54 +00:00
|
|
|
public divide(value: number | Vector2): Vector2
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
if (typeof value === 'number')
|
2018-10-12 14:34:43 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
return new Vector2([
|
|
|
|
|
this.x / value,
|
|
|
|
|
this.y / value,
|
|
|
|
|
]);
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
return new Vector2([
|
|
|
|
|
this.x / value.x,
|
|
|
|
|
this.y / value.y,
|
|
|
|
|
]);
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-17 23:37:54 +00:00
|
|
|
|
|
|
|
|
public scale(scalar: number): Vector2
|
2018-10-20 14:32:33 +01:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
return this.multiply(scalar);
|
2018-10-20 14:32:33 +01:00
|
|
|
}
|
2020-12-03 13:55:02 +00:00
|
|
|
|
2025-01-17 23:37:54 +00:00
|
|
|
public normalize(): Vector2
|
2018-11-15 03:10:14 +00:00
|
|
|
{
|
2025-01-17 23:37:54 +00:00
|
|
|
const len = this.length();
|
|
|
|
|
if (len > 0)
|
|
|
|
|
{
|
|
|
|
|
return this.scale(1 / len);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return this.copy();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public angle(): number
|
|
|
|
|
{
|
|
|
|
|
// Returns the angle in radians between this vector and the positive x-axis
|
|
|
|
|
return Math.atan2(this.y, this.x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public rotate(angle: number): Vector2
|
|
|
|
|
{
|
|
|
|
|
const cos = Math.cos(angle);
|
|
|
|
|
const sin = Math.sin(angle);
|
|
|
|
|
return new Vector2([
|
|
|
|
|
this.x * cos - this.y * sin,
|
|
|
|
|
this.x * sin + this.y * cos,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public perpendicular(): Vector2
|
|
|
|
|
{
|
|
|
|
|
// Returns a vector perpendicular to this vector (rotated 90 degrees counter-clockwise)
|
|
|
|
|
return new Vector2([-this.y, this.x]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public projectOnto(vec: Vector2): Vector2
|
|
|
|
|
{
|
|
|
|
|
const scalar = this.dot(vec) / vec.squaredLength();
|
|
|
|
|
return vec.scale(scalar);
|
2018-11-15 03:10:14 +00:00
|
|
|
}
|
2018-10-12 14:34:43 +01:00
|
|
|
}
|