// Source: https://github.com/nylen/d3-process-map
(function () {

    if (typeof geo == 'undefined') {
        geo = {};
    }

    var tolerance = 1e-10;

    function eq(a, b) {
        return (Math.abs(a - b) < tolerance);
    }

    function gt(a, b) {
        return (a - b > -tolerance);
    }

    function lt(a, b) {
        return gt(b, a);
    }

    geo.eq = eq;
    geo.gt = gt;
    geo.lt = lt;

    geo.LineSegment = function (x1, y1, x2, y2) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;

        // Ax + By = C
        this.a = y2 - y1;
        this.b = x1 - x2;
        this.c = x1 * this.a + y1 * this.b;

        if (eq(this.a, 0) && eq(this.b, 0)) {
            throw new Error(
                'Cannot construct a LineSegment with two equal endpoints.');
        }
    };

    geo.LineSegment.prototype.intersect = function (that) {
        var d = (this.x1 - this.x2) * (that.y1 - that.y2) -
            (this.y1 - this.y2) * (that.x1 - that.x2);

        if (eq(d, 0)) {
            // The two lines are parallel or very close.
            return {
                x: NaN,
                y: NaN
            };
        }

        var t1 = this.x1 * this.y2 - this.y1 * this.x2,
            t2 = that.x1 * that.y2 - that.y1 * that.x2,
            x = (t1 * (that.x1 - that.x2) - t2 * (this.x1 - this.x2)) / d,
            y = (t1 * (that.y1 - that.y2) - t2 * (this.y1 - this.y2)) / d,
            in1 = (gt(x, Math.min(this.x1, this.x2)) && lt(x, Math.max(this.x1, this.x2)) &&
                gt(y, Math.min(this.y1, this.y2)) && lt(y, Math.max(this.y1, this.y2))),
            in2 = (gt(x, Math.min(that.x1, that.x2)) && lt(x, Math.max(that.x1, that.x2)) &&
                gt(y, Math.min(that.y1, that.y2)) && lt(y, Math.max(that.y1, that.y2)));

        return {
            x: x,
            y: y,
            in1: in1,
            in2: in2
        };
    };

    geo.LineSegment.prototype.x = function (y) {
        // x = (C - By) / a;
        if (this.a) {
            return (this.c - this.b * y) / this.a;
        } else {
            // a == 0 -> horizontal line
            return NaN;
        }
    };

    geo.LineSegment.prototype.y = function (x) {
        // y = (C - Ax) / b;
        if (this.b) {
            return (this.c - this.a * x) / this.b;
        } else {
            // b == 0 -> vertical line
            return NaN;
        }
    };

    geo.LineSegment.prototype.length = function () {
        return Math.sqrt(
            (this.y2 - this.y1) * (this.y2 - this.y1) +
            (this.x2 - this.x1) * (this.x2 - this.x1));
    };

    geo.LineSegment.prototype.offset = function (x, y) {
        return new geo.LineSegment(
            this.x1 + x, this.y1 + y,
            this.x2 + x, this.y2 + y);
    };

})();

