Source: model/Point.js

/**
 * @file Class Point
 * @version March 12, 2017
 *
 * @author Olivier Pirson --- http://www.opimedia.be/
 * @license GPLv3 --- Copyright (C) 2017 Olivier Pirson
 */

/**
 * A point,
 * represented by its cartesian coordinates.
 */
class Point {
    /**
     * Constructs a point of cartesian coordinates (x=a, y=b).
     *
     * @param {number} a
     * @param {number} b
     */
    constructor(a, b) {
        assert(typeof a === "number", a);
        assert(typeof b === "number", b);

        this._x = a;
        this._y = b;
    }


    /**
     * Returns the horizontal coordinate of the cartesian coordinates.
     *
     * @returns {number}
     */
    get x() {
        if (this._x === null) {
            this._x = this.r*Math.cos(this.phi);
        }

        return this._x;
    }


    /**
     * Returns the vertical coordinate of the cartesian coordinates.
     *
     * @returns {number}
     */
    get y() {
        if (this._y === null) {
            this._y = this.r*Math.sin(this.phi);
        }

        return this._y;
    }



    /**
     * Returns a new point that is the sum.
     *
     * @param {Point} other
     *
     * @returns {Point}
     */
    add(other) {
        assert(other instanceof Point, other);

        return new Point(this.x + other.x, this.y + other.y);
    }


    /**
     * Compare two points before with x coordinate
     * and maybe after with y coordinate.
     *
     * @param {Point} other
     *
     * @returns {number} -1, 0 or 1
     */
    compare(other) {
        assert(other instanceof Point, other);

        const compX = compare(this.x, other.x);

        return (compX !== 0
                ? compX
                : compare(this.y, other.y));
    }


    /**
     * Returns the distance between the two points.
     *
     * @param {Point} other
     *
     * @returns {number} >= 0
     */
    distance(other) {
        assert(other instanceof Point, other);

        return Math.sqrt(this.distanceSqr(other));
    }


    /**
     * Returns the square of the distance between the two points.
     *
     * @param {Point} other
     *
     * @returns {number} >= 0
     */
    distanceSqr(other) {
        assert(other instanceof Point, other);

        const diffX = this._x - other._x;
        const diffY = this._y - other._y;

        return diffX*diffX + diffY*diffY;
    }


    /**
     * Returns the distance to (0, 0).
     *
     * @returns {number} >= 0
     */
    distanceTo0() {
        return Math.sqrt(this.distanceTo0Sqr());
    }


    /**
     * Returns the square of the distance to (0, 0).
     *
     * @returns {number} >= 0
     */
    distanceTo0Sqr() {
        return this._x*this._x + this._y*this._y;
    }


    /**
     * Returns true iff the point is equals to the point other,
     * namely iff they are same cartesian coordinates.
     *
     * @param {Point} other
     *
     * @returns {boolean}
     */
    isEquals(other) {
        assert(other instanceof Point, other);

        return (this.x === other.x) && (this.y === other.y);
    }


    /**
     * Returns a new point multiply by the scalar n.
     *
     * @returns {Point}
     */
    mulScalar(n) {
        assert(typeof n === "number", n);

        return new Point(this.x*n, this.y*n);
    }


    /**
     * Returns a new point that is the difference.
     *
     * @param {Point} other
     *
     * @returns {Point}
     */
    sub(other) {
        assert(other instanceof Point, other);

        return new Point(this.x - other.x, this.y - other.y);
    }


    /**
     * Returns a string cartesian representation of the point.
     *
     * Each number is represented with at most precision figures after the decimal point.
     *
     * @returns {String}
     */
    toString(precision=2) {
        assert(Number.isInteger(precision), precision);
        assert(precision >= 0, precision);

        function format(n) {
            return (Number.isInteger(n)
                    ? n.toString()
                    : n.toFixed(precision));
        }

        return "(" + format(this.x) + ", " + format(this.y) + ")";
    }
}