OpenHPS/openhps-core

View on GitHub
src/data/position/AbsolutePosition.ts

Summary

Maintainability
C
1 day
Test Coverage
import { Position } from './Position';
import { LengthUnit, Unit } from '../../utils/unit';
import { Vector3 } from '../../utils/math';
import { Velocity } from '../values/Velocity';
import { LinearVelocity } from '../values/LinearVelocity';
import { AngularVelocity } from '../values/AngularVelocity';
import { Orientation } from './Orientation';
import { NumberType, SerializableMember, SerializableObject } from '../decorators';
import { TimeService } from '../../service/TimeService';
import { Accuracy } from '../values/Accuracy';
import { Accuracy1D } from '../values/Accuracy1D';
import { DistanceFn, EUCLIDEAN } from '../../utils';

/**
 * An absolute position of a {@link DataObject}.
 * @category Position
 */
@SerializableObject()
export abstract class AbsolutePosition implements Position<LengthUnit> {
    /**
     * Position recording timestamp
     */
    @SerializableMember({
        index: true,
        numberType: NumberType.LONG,
    })
    timestamp: number = TimeService.now();
    /**
     * Velocity at recorded position
     */
    @SerializableMember()
    velocity: Velocity = new Velocity();
    /**
     * Orientation at recorded position
     */
    @SerializableMember()
    orientation: Orientation;
    /**
     * Position unit
     */
    @SerializableMember()
    unit: LengthUnit = LengthUnit.METER;
    /**
     * Position reference space UID
     */
    @SerializableMember({
        index: true,
    })
    referenceSpaceUID: string;
    @SerializableMember({
        name: 'accuracy',
    })
    private _accuracy: Accuracy<LengthUnit, any>;
    @SerializableMember({
        name: 'probability',
        numberType: NumberType.DECIMAL,
    })
    private _probability: number;

    /**
     * Get the position probability
     * @returns {number} Probability between 0 and 1
     */
    get probability(): number {
        if (!this._probability) {
            return 1 / this.accuracy.valueOf();
        }
        return this._probability;
    }

    set probability(value: number) {
        if (value > 1 || value < 0) {
            throw new Error(`${this.constructor.name} should be between 0 and 1.`);
        }
        this._probability = value;
    }

    /**
     * Position accuracy
     * @returns {Accuracy} Position accuracy
     */
    get accuracy(): Accuracy<LengthUnit, any> {
        if (!this._accuracy) {
            this._accuracy = new Accuracy1D(1, this.unit);
        }
        return this._accuracy;
    }

    set accuracy(value: Accuracy<LengthUnit, any>) {
        if (!value) {
            throw new Error(`Accuracy can not be undefined!`);
        }
        this._accuracy = value;
    }

    /**
     * Get the linear velocity
     * @returns {LinearVelocity} Linear velocity
     */
    get linearVelocity(): LinearVelocity {
        if (!this.velocity) {
            return undefined;
        }
        return this.velocity.linear;
    }

    /**
     * Set the linear velocity
     */
    set linearVelocity(value: LinearVelocity) {
        if (!this.velocity) {
            this.velocity = new Velocity();
        }
        this.velocity.linear = value;
    }

    /**
     * Get the angular velocity
     * @returns {AngularVelocity} Angular velocity
     */
    get angularVelocity(): AngularVelocity {
        if (!this.velocity) {
            return undefined;
        }
        return this.velocity.angular;
    }

    /**
     * Set the angular velocity
     */
    set angularVelocity(value: AngularVelocity) {
        if (!this.velocity) {
            this.velocity = new Velocity();
        }
        this.velocity.angular = value;
    }

    /**
     * Set the orientation of the position
     * @param {Orientation} orientation orientation
     * @returns {AbsolutePosition} instance
     */
    setOrientation(orientation: Orientation): this {
        this.orientation = orientation;
        return this;
    }

    /**
     * Set the accuracy of the absolute position
     * @param {number | Accuracy} accuracy Accuracy object or number
     * @param {Unit} [unit] Optional unit
     * @returns {AbsolutePosition} instance
     */
    setAccuracy(accuracy: number | Accuracy<LengthUnit, any>, unit?: Unit): this {
        if (typeof accuracy === 'number') {
            this.accuracy = new Accuracy1D(accuracy, unit || this.unit);
        } else {
            this.accuracy = accuracy;
        }
        return this;
    }

    abstract fromVector(vector: Vector3, unit?: LengthUnit): this;

    abstract toVector3(unit?: LengthUnit): Vector3;

    /**
     * Get the angle in radians from this position to a destination
     * @param {AbsolutePosition} destination Destination position
     * @returns {number} Bearing in radians from this position to destination
     */
    abstract angleTo(destination: this): number;

    /**
     * Get the distance from this location to a destination
     * @param {AbsolutePosition} destination Destination location
     * @param {DistanceFn} [distanceFunction] Distance function to use (default EUCLIDEAN distance)
     * @returns {number} Distance between this point and destination
     */
    distanceTo(destination: this, distanceFunction: DistanceFn = EUCLIDEAN): number {
        return distanceFunction(this.toVector3().toArray(), destination.toVector3().toArray());
    }

    equals(position: this): boolean {
        return this.toVector3(this.unit).equals(position.toVector3(this.unit));
    }

    /**
     * Clone the position
     * @returns {AbsolutePosition} Cloned position
     */
    clone(): this {
        const position = new (this.constructor as new () => this)();
        position.unit = this.unit;
        position._accuracy = this._accuracy ? this._accuracy.clone() : undefined;
        position.orientation = this.orientation ? this.orientation.clone() : undefined;
        position.velocity = this.velocity ? this.velocity.clone() : undefined;
        position.timestamp = this.timestamp;
        position.referenceSpaceUID = this.referenceSpaceUID;
        return position as this;
    }
}