form.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. const selectors = {
  2. get sortControl() {
  3. return document.forms.sortControl.elements;
  4. },
  5. get clusterControl() {
  6. return document.forms.clusterControl.elements;
  7. },
  8. get colorSpace() {
  9. return selectors.sortControl.colorSpace.value;
  10. },
  11. get useClusters() {
  12. return selectors.sortControl.useClusters.value;
  13. },
  14. get prevColors() {
  15. return document.getElementById("prev-colors");
  16. },
  17. set background(hex) {
  18. document.querySelector(":root").style.setProperty("--background", hex);
  19. },
  20. set highlight(hex) {
  21. document.querySelector(":root").style.setProperty("--highlight", hex);
  22. },
  23. };
  24. const onMetricChange = (elements, skipUpdates = false) => {
  25. elements.sortOrderLabel.value = elements.sortOrder.checked
  26. ? "Maximizing"
  27. : "Minimizing";
  28. const kind = elements.metricKind.value;
  29. elements.whole.disabled = kind !== "whole";
  30. elements.mean.disabled = kind !== "mean";
  31. elements.statistic.disabled = kind !== "statistic";
  32. elements.sortMetric.value = elements[kind].value;
  33. if (!skipUpdates) {
  34. updateSort();
  35. showResults(selectors.sortControl.resultsToDisplay.value);
  36. }
  37. };
  38. const onColorChange = (inputValue, skipUpdates = false) => {
  39. const colorInput = "#" + (inputValue?.replace("#", "") ?? "FFFFFF");
  40. if (colorInput.length !== 7) {
  41. return;
  42. }
  43. const rgb = d3.color(colorInput);
  44. if (!rgb) {
  45. return;
  46. }
  47. const hex = rgb.formatHex();
  48. selectors.sortControl.colorText.value = hex;
  49. selectors.sortControl.colorPicker.value = hex;
  50. const contrast = getContrastingTextColor(hex);
  51. const newColor = document.createElement("div");
  52. newColor.innerHTML = hex;
  53. newColor.style = `
  54. color: ${contrast};
  55. background-color: ${hex};
  56. `;
  57. selectors.prevColors.prepend(newColor);
  58. selectors.background = hex;
  59. selectors.highlight = contrast;
  60. if (!skipUpdates) {
  61. updateScores(rgb);
  62. updateSort();
  63. showResults(selectors.sortControl.resultsToDisplay.value);
  64. }
  65. };
  66. const randomColor = () =>
  67. d3.hsl(Math.random() * 360, Math.random(), Math.random()).formatHex();
  68. const calcScores = (data, target) => {
  69. const sigma = Math.sqrt(
  70. data.inertia - 2 * vectorDot(data.mu.vector, target.vector) + target.sqMag
  71. );
  72. const theta = rad2deg * Math.acos(vectorDot(data.mu.unit, target.unit));
  73. const rawPhi = Math.abs(data.mu.hue - target.hue);
  74. return {
  75. sigma,
  76. bigTheta: 1 - vectorDot(data.nu, target.unit),
  77. alpha: sigma * Math.pow(bigTheta, target.chroma + target.lightness),
  78. theta,
  79. phi: Math.min(rawPhi, 360 - rawPhi),
  80. delta: vectorMag(data.mu.vector.map((x, i) => x - target.vector[i])),
  81. manhattan: data.mu.vector
  82. .map((x, i) => Math.abs(x - target.vector[i]))
  83. .reduce((x, y) => x + y),
  84. ch: Math.max(...data.mu.vector.map((x, i) => Math.abs(x - target.vector[i]))),
  85. lightnessDiff: Math.abs(data.mu.lightness - target.lightness),
  86. inertia: data.inertia,
  87. variance: data.inertia - data.mu.sqMag,
  88. muNuAngle: data.muNuAngle,
  89. size: data.size,
  90. lightness: data.mu.lightness,
  91. chroma: data.mu.chroma,
  92. importance: data.importance,
  93. };
  94. };
  95. const currentScores = {};
  96. const currentBestClusterIndices = {};
  97. let sortedData = pokemonData;
  98. const updateScores = (rgb) => {
  99. const { J, a, b } = d3.jab(rgb);
  100. const targetJab = buildVectorData([J, a, b], jab2hue, jab2lit, jab2chroma, jab2hex);
  101. const targetRgb = buildVectorData(
  102. [rgb.r, rgb.g, rgb.b],
  103. rgb2hue,
  104. rgb2lit,
  105. rgb2chroma,
  106. rgb2hex
  107. );
  108. pokemonData.forEach(({ name, jab, rgb }) => {
  109. currentScores[name] = {
  110. jab: {
  111. total: calcScores(jab.total, targetJab),
  112. clusters: jab.clusters.map((c) => calcScores(c, targetJab)),
  113. },
  114. rgb: {
  115. total: calcScores(rgb.total, targetRgb),
  116. clusters: rgb.clusters.map((c) => calcScores(c, targetRgb)),
  117. },
  118. };
  119. });
  120. };
  121. const updateSort = () => {
  122. pokemonData.forEach(({ name, jab, rgb }) => {
  123. currentBestClusterIndices[name] = {
  124. // TODO
  125. jab: 0,
  126. rgb: 0,
  127. };
  128. });
  129. // TODO
  130. };
  131. const showResults = (resultsToDisplay) => {
  132. // TODO
  133. };
  134. window.addEventListener("load", () => {
  135. const metricTemplate = document.getElementById("metric-form-template").content;
  136. selectors.sortControl.metric.appendChild(metricTemplate.cloneNode(true));
  137. selectors.sortControl.metricKind.value = "whole";
  138. selectors.sortControl.whole.value = "alpha";
  139. onMetricChange(selectors.sortControl, true);
  140. selectors.clusterControl.metric.appendChild(metricTemplate.cloneNode(true));
  141. selectors.clusterControl.sortOrder.checked = true;
  142. selectors.clusterControl.metricKind.value = "statistic";
  143. selectors.clusterControl.statistic.value = "importance";
  144. onMetricChange(selectors.clusterControl, true);
  145. const scaleTemplate = document.getElementById("scale-form-template").content;
  146. selectors.sortControl.rescaleSection.appendChild(scaleTemplate.cloneNode(true));
  147. selectors.sortControl.rescaleFactor.value = "none";
  148. selectors.clusterControl.rescaleSection.appendChild(scaleTemplate.cloneNode(true));
  149. selectors.clusterControl.rescaleFactor.value = "inverse";
  150. });