Source: model/Triplet.js

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

/**
 * A triplet of points.
 */
class Triplet {
    /**
     * Constructs a triplet (a, b, c) of points.
     *
     * @param {Point} a
     * @param {Point} b
     * @param {Point} c
     */
    constructor(a, b, c) {
        assert(a instanceof Point, a);
        assert(b instanceof Point, b);
        assert(c instanceof Point, c);

        this._a = a;
        this._b = b;
        this._c = c;
    }



    /**
     * Returns the first point.
     *
     * @returns {Point}
     */
    get a() { return this._a; }


    /**
     * Returns the second point.
     *
     * @returns {Point}
     */
    get b() { return this._b; }


    /**
     * Returns the third point.
     *
     * @returns {Point}
     */
    get c() { return this._c; }



    /**
     * Returns a new triplet, copied of this.
     *
     * @returns {Triplet}
     */
    copy() {
        return new Triplet(this.a, this.b, this.c);
    }


    /**
     * Returns the cross product of the vector ab and bc.
     *
     * @returns {number}
     */
    crossProductMagnitude() {
        const a = this.a;
        const b = this.b;
        const c = this.c;

        return (b.x - a.x)*(c.y - a.y) - (c.x - a.x)*(b.y - a.y);
    }


    /**
     * Returns true iff a, b and c are on the same line.
     *
     * @returns {boolean}
     */
    isCollinear() {
        return isFloat0(this.orientation());
    }


    /**
     * Returns true iff p is in the triangle formed by the triplet.
     *
     * Cf. http://stackoverflow.com/a/2049593/1333666
     *
     * @param {Point} p
     *
     * @returns {boolean}
     */
    isIn(p) {
        assert(p instanceof Point, p);

        const ps = [this.a, this.b, this.c];

        sortRadiallyAroundFirst(ps);

        const a = ps[0];
        const b = ps[1];
        const c = ps[2];

        const orient1 = (new Triplet(p, a, b)).isLeftOn();
        const orient2 = (new Triplet(p, b, c)).isLeftOn();
        const orient3 = (new Triplet(p, c, a)).isLeftOn();

        return (orient1 === orient2) && (orient2 === orient3);
    }


    /**
     * Returns true iff c is left to ab.
     *
     * @returns {boolean}
     */
    isLeft() {
        return (this.orientation() > 0);
    }


    /**
     * Returns true iff c is left to ab or collinear.
     *
     * @returns {boolean}
     */
    isLeftOn() {
        return (this.orientation() >= 0);
    }


    /**
     * Returns true iff c is right to ab.
     *
     * @returns {boolean}
     */
    isRight() {
        return (this.orientation() < 0);
    }


    /**
     * Returns true iff c is right to ab or collinear.
     *
     * @returns {boolean}
     */
    isRightOn() {
        return (this.orientation() <= 0);
    }


    /**
     * Returns the orientation of the point c compared to ab.
     *
     * If     -1 then c is on the right,
     * else if 0 then c is on the same line that ab,
     * else if 1 then c is on the left.
     *
     * @returns {number} -1, 0 or 1
     */
    orientation() {
        const magnitude = this.crossProductMagnitude();

        return Math.sign(magnitude);
    }


    /**
     * Returns a string representation of the triplet.
     *
     * 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);

        return (this.a.toString(precision)
                + ":" + this.b.toString(precision)
                + ":" + this.c.toString(precision));
    }
}