Source: view/GlobalView.js

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

/**
 * View for two matchings
 * and global informations.
 */
class GlobalView {
    /**
     * Construct a view for draw two matchings
     * and print global informations.
     *
     * @param {Matching} leftMatchingView
     * @param {Matching} rightMatchingView
     * @param {HTMLElement} infosHtmlElement that wil contains global informations
     * @param {HTMLElement} listMatchingsHtmlElement that wil contains list of matchings
     */
    constructor(leftMatchingView, rightMatchingView, infosHtmlElement, listMatchingsHtmlElement, infosListMatchingsHtmlElement) {
        assert(leftMatchingView instanceof MatchingView, leftMatchingView);
        assert(rightMatchingView instanceof MatchingView, rightMatchingView);
        assert(infosHtmlElement instanceof HTMLElement, infosHtmlElement);
        assert(listMatchingsHtmlElement instanceof HTMLElement, listMatchingsHtmlElement);
        assert(infosListMatchingsHtmlElement instanceof HTMLElement, infosListMatchingsHtmlElement);

        this._leftView = leftMatchingView;
        this._rightView = rightMatchingView;
        this._infosHtmlElement = infosHtmlElement;
        this._listMatchingsHtmlElement = listMatchingsHtmlElement;
        this._infosListMatchingsHtmlElement = infosListMatchingsHtmlElement;

        infosHtmlElement.innerHTML = "<div></div><div></div>";

        this.update();
    }



    /**
     * Resize the container of list matchings.
     */
    resizeListMatchings() {
        const width = (420*2/5 + 2 + 6)*this._leftView.matching.linkedMatchings.length - 6;
        const parent = this._listMatchingsHtmlElement.parentNode;

        if (width >= parent.offsetWidth) {
            const margin = (6 - parent.offsetLeft) + "px";

            this._listMatchingsHtmlElement.style.marginLeft = margin;
            this._listMatchingsHtmlElement.style.marginRight = margin;
        }
        else {
            this._listMatchingsHtmlElement.style.marginLeft = "0px";
            this._listMatchingsHtmlElement.style.marginRight = "0px";
        }
    }


    /**
     * Update the two matching views
     * and the global informations.
     *
     * If updateMatchings
     * then update also matchings.
     *
     * @param {currentPoint} Point
     * @param {boolean} updateMatchings
     */
    update(currentPoint=null, updateMatchings=true) {
        assert((currentPoint === null) || (currentPoint instanceof Point), currentPoint);
        assert(typeof updateMatchings === "boolean", updateMatchings);

        const different = !this._leftView.matching.isEquals(this._rightView.matching);
        const disjoint = this._leftView.matching.isDisjoint(this._rightView.matching);
        const compatible = this._leftView.matching.isCompatible(this._rightView.matching);


        // Enable or disable some buttons
        var onOffButton = function (buttonItem, bool) {
            if (bool) {
                buttonItem.removeAttribute("disabled");
            }
            else {
                buttonItem.setAttribute("disabled", "disabled");
            }
        };

        onOffButton(document.getElementById("button-build-list-perfect-matchings"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 2));

        onOffButton(document.getElementById("button-build-list-disjoint-perfect-matchings"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 2)
                    && this._leftView.matching.isPerfect());

        onOffButton(document.getElementById("button-build-list-compatible-perfect-matchings"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 2)
                    && this._leftView.matching.isPerfect());

        onOffButton(document.getElementById("button-build-list-disjoint-compatible-perfect-matchings"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 2)
                    && this._leftView.matching.isPerfect());


        onOffButton(document.getElementById("button-build-list-transformation"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 4)
                    && different
                    && !compatible
                    && this._leftView.matching.isPerfect()
                    && this._rightView.matching.isPerfect());

        onOffButton(document.getElementById("button-build-list-disjoint-transformation"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 4)
                    && different
                    && (!disjoint || !compatible)
                    && this._leftView.matching.isPerfect()
                    && this._rightView.matching.isPerfect());


        onOffButton(document.getElementById("button-canonical-left"),
                    (this._leftView.matching.points.length % 2 === 0)
                    && (this._leftView.matching.points.length >= 2));

        onOffButton(document.getElementById("button-canonical-right"),
                    (this._rightView.matching.points.length % 2 === 0)
                    && (this._rightView.matching.points.length >= 2));


        onOffButton(document.getElementById("button-shuffle-left"),
                    (this._leftView.matching.segments.length >= 2));

        onOffButton(document.getElementById("button-shuffle-right"),
                    (this._rightView.matching.segments.length >= 2));


        onOffButton(document.getElementById("button-clear"),
                    this._leftView.matching.points.length > 0);

        onOffButton(document.getElementById("button-clear-list-matchings"),
                    this._leftView.matching.linkedMatchings.length > 2);

        onOffButton(document.getElementById("button-save"),
                    this._leftView.matching.points.length > 0);

        onOffButton(document.getElementById("button-swap"), different);


        // Update infos
        this.updateInfosCurrentPoint(currentPoint);

        this._infosHtmlElement.children[1].innerHTML
            = ("<span><span>Different? " + classHtmlTrueFalse(different) + "</span>"
               + '<span class="margin-left-2m">Disjoint? ' + htmlTrueFalse(disjoint) + '<span class="margin-left-m '
               + (disjoint
                  ? "hidden"
                  : "color-not-disjoint") + '">(common segments)</span></span></span>'
               + '<span>Compatible? ' + htmlTrueFalse(compatible)
               + '<span class="margin-left-m '
               + (compatible
                  ? "hidden"
                  : "color-not-compatible") + '">(intersect segments)</span></span>');


        if (updateMatchings) {
            // Update left and right matchings
            this._leftView.update();
            this._rightView.update();
        }


        // Update list of linked matchings
        const number = ((this._leftView.matching.linkedMatchings.length == 2)
                        && (this._leftView.matching.isEquals(this._leftView.matching.linkedMatchings[1]))
                        ? 1
                        : this._leftView.matching.linkedMatchings.length);

        this._infosListMatchingsHtmlElement.innerHTML = number + " matching" + s(number) + ":";

        this._listMatchingsHtmlElement.innerHTML = null;

        var skipped = false;

        for (let i = 0; i < number; ++i) {
            const matching = this._leftView.matching.linkedMatchings[i];
            const divZoom = document.createElement("div");

            divZoom.className = "zoom";

            const divMatching = document.createElement("div");

            divMatching.className = "matching";
            divZoom.appendChild(divMatching);

            const canvas_container = document.createElement("div");

            canvas_container.className = "canvas-container";
            divMatching.appendChild(canvas_container);

            this._listMatchingsHtmlElement.appendChild(divZoom);

            const view = new MatchingView(matching, divMatching, null, true);

            view._drawSegments = this._leftView._drawSegments;

            view.update();

            if (!skipped && (i > 50)) {
                // Skip linked matchings except the two lasts
                skipped = true;
                i = number - 3;

                const div = document.createElement("div");

                div.innerHTML = "&hellip;";
                this._listMatchingsHtmlElement.appendChild(div);
            }

            if ((number > 1)
                && ((i === 0) || (i === number - 2))) {
                // Add a separator after the first and before the last matchings
                const divSep = document.createElement("div");

                divSep.className = "sep";
                divSep.innerHTML = "&nbsp;";
                this._listMatchingsHtmlElement.appendChild(divSep);
            }
        }

        this.resizeListMatchings();
    }


    /**
     * Update the coordinates of the current point
     * or remove it,
     * and update the number of points.
     *
     * @param {Point} currentPoint
     */
    updateInfosCurrentPoint(currentPoint=null) {
        assert((currentPoint === null) || (currentPoint instanceof Point), currentPoint);

        const leftMatching = this._leftView.matching;
        const numberPerfectMatchingsUppedBound = leftMatching.allPerfectMatchingsUppedBound();
        const n = leftMatching.points.length;

        this._infosHtmlElement.children[0].innerHTML
            = ("<span>"
               + (currentPoint === null
                  ? ""
                  : "(" + strPad(currentPoint.x, 3) + ", " + strPad(currentPoint.y, 3) + ")") + "</span><span>"
               + n + " point" + s(n)
               + " " + classHtmlTrueFalse(n % 2 === 0)
               + '</span><span class="margin-left-2m">'
               + (leftMatching.numberPerfectMatchingsIfCalculated === null
                  ? '?'
                  : leftMatching.numberPerfectMatchingsIfCalculated)
               + ' possible perfect matching(s) <span title="Rough upper bound of the number of possible perfect matchings with these points.">&le; ' + numberPerfectMatchingsUppedBound
               + '</span></span><span class="margin-left-2m">'
               + ((n > 0) && (n%2 === 0)
                  ? "Minimal length transformation &leq; " + Math.ceil(Math.log2(n))*2
                  : '') + "</span>");
    }
}