/**
* @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 = "…";
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 = " ";
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.">≤ ' + numberPerfectMatchingsUppedBound
+ '</span></span><span class="margin-left-2m">'
+ ((n > 0) && (n%2 === 0)
? "Minimal length transformation ≤ " + Math.ceil(Math.log2(n))*2
: '') + "</span>");
}
}