Source: view/GlobalView.js

  1. /**
  2. * @file Class GlobalView
  3. * @version April 21, 2017
  4. *
  5. * @author Olivier Pirson --- http://www.opimedia.be/
  6. * @license GPLv3 --- Copyright (C) 2017 Olivier Pirson
  7. */
  8. /**
  9. * View for two matchings
  10. * and global informations.
  11. */
  12. class GlobalView {
  13. /**
  14. * Construct a view for draw two matchings
  15. * and print global informations.
  16. *
  17. * @param {Matching} leftMatchingView
  18. * @param {Matching} rightMatchingView
  19. * @param {HTMLElement} infosHtmlElement that wil contains global informations
  20. * @param {HTMLElement} listMatchingsHtmlElement that wil contains list of matchings
  21. */
  22. constructor(leftMatchingView, rightMatchingView, infosHtmlElement, listMatchingsHtmlElement, infosListMatchingsHtmlElement) {
  23. assert(leftMatchingView instanceof MatchingView, leftMatchingView);
  24. assert(rightMatchingView instanceof MatchingView, rightMatchingView);
  25. assert(infosHtmlElement instanceof HTMLElement, infosHtmlElement);
  26. assert(listMatchingsHtmlElement instanceof HTMLElement, listMatchingsHtmlElement);
  27. assert(infosListMatchingsHtmlElement instanceof HTMLElement, infosListMatchingsHtmlElement);
  28. this._leftView = leftMatchingView;
  29. this._rightView = rightMatchingView;
  30. this._infosHtmlElement = infosHtmlElement;
  31. this._listMatchingsHtmlElement = listMatchingsHtmlElement;
  32. this._infosListMatchingsHtmlElement = infosListMatchingsHtmlElement;
  33. infosHtmlElement.innerHTML = "<div></div><div></div>";
  34. this.update();
  35. }
  36. /**
  37. * Resize the container of list matchings.
  38. */
  39. resizeListMatchings() {
  40. const width = (420*2/5 + 2 + 6)*this._leftView.matching.linkedMatchings.length - 6;
  41. const parent = this._listMatchingsHtmlElement.parentNode;
  42. if (width >= parent.offsetWidth) {
  43. const margin = (6 - parent.offsetLeft) + "px";
  44. this._listMatchingsHtmlElement.style.marginLeft = margin;
  45. this._listMatchingsHtmlElement.style.marginRight = margin;
  46. }
  47. else {
  48. this._listMatchingsHtmlElement.style.marginLeft = "0px";
  49. this._listMatchingsHtmlElement.style.marginRight = "0px";
  50. }
  51. }
  52. /**
  53. * Update the two matching views
  54. * and the global informations.
  55. *
  56. * If updateMatchings
  57. * then update also matchings.
  58. *
  59. * @param {currentPoint} Point
  60. * @param {boolean} updateMatchings
  61. */
  62. update(currentPoint=null, updateMatchings=true) {
  63. assert((currentPoint === null) || (currentPoint instanceof Point), currentPoint);
  64. assert(typeof updateMatchings === "boolean", updateMatchings);
  65. const different = !this._leftView.matching.isEquals(this._rightView.matching);
  66. const disjoint = this._leftView.matching.isDisjoint(this._rightView.matching);
  67. const compatible = this._leftView.matching.isCompatible(this._rightView.matching);
  68. // Enable or disable some buttons
  69. var onOffButton = function (buttonItem, bool) {
  70. if (bool) {
  71. buttonItem.removeAttribute("disabled");
  72. }
  73. else {
  74. buttonItem.setAttribute("disabled", "disabled");
  75. }
  76. };
  77. onOffButton(document.getElementById("button-build-list-perfect-matchings"),
  78. (this._leftView.matching.points.length % 2 === 0)
  79. && (this._leftView.matching.points.length >= 2));
  80. onOffButton(document.getElementById("button-build-list-disjoint-perfect-matchings"),
  81. (this._leftView.matching.points.length % 2 === 0)
  82. && (this._leftView.matching.points.length >= 2)
  83. && this._leftView.matching.isPerfect());
  84. onOffButton(document.getElementById("button-build-list-compatible-perfect-matchings"),
  85. (this._leftView.matching.points.length % 2 === 0)
  86. && (this._leftView.matching.points.length >= 2)
  87. && this._leftView.matching.isPerfect());
  88. onOffButton(document.getElementById("button-build-list-disjoint-compatible-perfect-matchings"),
  89. (this._leftView.matching.points.length % 2 === 0)
  90. && (this._leftView.matching.points.length >= 2)
  91. && this._leftView.matching.isPerfect());
  92. onOffButton(document.getElementById("button-build-list-transformation"),
  93. (this._leftView.matching.points.length % 2 === 0)
  94. && (this._leftView.matching.points.length >= 4)
  95. && different
  96. && !compatible
  97. && this._leftView.matching.isPerfect()
  98. && this._rightView.matching.isPerfect());
  99. onOffButton(document.getElementById("button-build-list-disjoint-transformation"),
  100. (this._leftView.matching.points.length % 2 === 0)
  101. && (this._leftView.matching.points.length >= 4)
  102. && different
  103. && (!disjoint || !compatible)
  104. && this._leftView.matching.isPerfect()
  105. && this._rightView.matching.isPerfect());
  106. onOffButton(document.getElementById("button-canonical-left"),
  107. (this._leftView.matching.points.length % 2 === 0)
  108. && (this._leftView.matching.points.length >= 2));
  109. onOffButton(document.getElementById("button-canonical-right"),
  110. (this._rightView.matching.points.length % 2 === 0)
  111. && (this._rightView.matching.points.length >= 2));
  112. onOffButton(document.getElementById("button-shuffle-left"),
  113. (this._leftView.matching.segments.length >= 2));
  114. onOffButton(document.getElementById("button-shuffle-right"),
  115. (this._rightView.matching.segments.length >= 2));
  116. onOffButton(document.getElementById("button-clear"),
  117. this._leftView.matching.points.length > 0);
  118. onOffButton(document.getElementById("button-clear-list-matchings"),
  119. this._leftView.matching.linkedMatchings.length > 2);
  120. onOffButton(document.getElementById("button-save"),
  121. this._leftView.matching.points.length > 0);
  122. onOffButton(document.getElementById("button-swap"), different);
  123. // Update infos
  124. this.updateInfosCurrentPoint(currentPoint);
  125. this._infosHtmlElement.children[1].innerHTML
  126. = ("<span><span>Different? " + classHtmlTrueFalse(different) + "</span>"
  127. + '<span class="margin-left-2m">Disjoint? ' + htmlTrueFalse(disjoint) + '<span class="margin-left-m '
  128. + (disjoint
  129. ? "hidden"
  130. : "color-not-disjoint") + '">(common segments)</span></span></span>'
  131. + '<span>Compatible? ' + htmlTrueFalse(compatible)
  132. + '<span class="margin-left-m '
  133. + (compatible
  134. ? "hidden"
  135. : "color-not-compatible") + '">(intersect segments)</span></span>');
  136. if (updateMatchings) {
  137. // Update left and right matchings
  138. this._leftView.update();
  139. this._rightView.update();
  140. }
  141. // Update list of linked matchings
  142. const number = ((this._leftView.matching.linkedMatchings.length == 2)
  143. && (this._leftView.matching.isEquals(this._leftView.matching.linkedMatchings[1]))
  144. ? 1
  145. : this._leftView.matching.linkedMatchings.length);
  146. this._infosListMatchingsHtmlElement.innerHTML = number + " matching" + s(number) + ":";
  147. this._listMatchingsHtmlElement.innerHTML = null;
  148. var skipped = false;
  149. for (let i = 0; i < number; ++i) {
  150. const matching = this._leftView.matching.linkedMatchings[i];
  151. const divZoom = document.createElement("div");
  152. divZoom.className = "zoom";
  153. const divMatching = document.createElement("div");
  154. divMatching.className = "matching";
  155. divZoom.appendChild(divMatching);
  156. const canvas_container = document.createElement("div");
  157. canvas_container.className = "canvas-container";
  158. divMatching.appendChild(canvas_container);
  159. this._listMatchingsHtmlElement.appendChild(divZoom);
  160. const view = new MatchingView(matching, divMatching, null, true);
  161. view._drawSegments = this._leftView._drawSegments;
  162. view.update();
  163. if (!skipped && (i > 50)) {
  164. // Skip linked matchings except the two lasts
  165. skipped = true;
  166. i = number - 3;
  167. const div = document.createElement("div");
  168. div.innerHTML = "&hellip;";
  169. this._listMatchingsHtmlElement.appendChild(div);
  170. }
  171. if ((number > 1)
  172. && ((i === 0) || (i === number - 2))) {
  173. // Add a separator after the first and before the last matchings
  174. const divSep = document.createElement("div");
  175. divSep.className = "sep";
  176. divSep.innerHTML = "&nbsp;";
  177. this._listMatchingsHtmlElement.appendChild(divSep);
  178. }
  179. }
  180. this.resizeListMatchings();
  181. }
  182. /**
  183. * Update the coordinates of the current point
  184. * or remove it,
  185. * and update the number of points.
  186. *
  187. * @param {Point} currentPoint
  188. */
  189. updateInfosCurrentPoint(currentPoint=null) {
  190. assert((currentPoint === null) || (currentPoint instanceof Point), currentPoint);
  191. const leftMatching = this._leftView.matching;
  192. const numberPerfectMatchingsUppedBound = leftMatching.allPerfectMatchingsUppedBound();
  193. const n = leftMatching.points.length;
  194. this._infosHtmlElement.children[0].innerHTML
  195. = ("<span>"
  196. + (currentPoint === null
  197. ? ""
  198. : "(" + strPad(currentPoint.x, 3) + ", " + strPad(currentPoint.y, 3) + ")") + "</span><span>"
  199. + n + " point" + s(n)
  200. + " " + classHtmlTrueFalse(n % 2 === 0)
  201. + '</span><span class="margin-left-2m">'
  202. + (leftMatching.numberPerfectMatchingsIfCalculated === null
  203. ? '?'
  204. : leftMatching.numberPerfectMatchingsIfCalculated)
  205. + ' possible perfect matching(s) <span title="Rough upper bound of the number of possible perfect matchings with these points.">&le; ' + numberPerfectMatchingsUppedBound
  206. + '</span></span><span class="margin-left-2m">'
  207. + ((n > 0) && (n%2 === 0)
  208. ? "Minimal length transformation &leq; " + Math.ceil(Math.log2(n))*2
  209. : '') + "</span>");
  210. }
  211. }