nearest.html 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>Pokemon By Color</title>
  6. <link rel="stylesheet" href="web/nearest.css" />
  7. <script src="https://unpkg.com/d3-color@3.0.1/dist/d3-color.min.js"></script>
  8. <script src="https://unpkg.com/d3-cam02@0.1.5/build/d3-cam02.min.js"></script>
  9. <script src="https://unpkg.com/fuse.js@6.5.3/dist/fuse.min.js"></script>
  10. <script src="https://unpkg.com/texzilla@1.0.2/TeXZilla.js"></script>
  11. <script src="database-v3-afd.js"></script>
  12. <script src="web/math.js"></script>
  13. <script src="web/metrics.js"></script>
  14. <script src="web/convert.js"></script>
  15. <script src="web/score.js"></script>
  16. <script src="web/render.js"></script>
  17. <script src="web/listeners.js"></script>
  18. <script lang="javascript">
  19. const state = {
  20. target: null,
  21. number: 10,
  22. useCluster: true,
  23. clusterSettings: {
  24. sortMetric: "importance",
  25. scaleOption: "none",
  26. sortOrder: "max",
  27. multWithTotal: true,
  28. },
  29. space: "jab",
  30. sortMetric: "alpha",
  31. scaleOption: "inverse",
  32. sortOrder: "min",
  33. };
  34. window.onload = () => {
  35. document.getElementById("mu-def").innerHTML = TeXZilla.toMathMLString(String.raw`
  36. \vec{\mu}\left(P\right) = \frac{1}{\left|P\right|}\sum_{p\in P}{\vec{p}}
  37. `);
  38. document.getElementById("nu-def").innerHTML = TeXZilla.toMathMLString(String.raw`
  39. \vec{\nu}\left(P\right) = \frac{1}{\left|P\right|}\sum_{p\in P}{\hat{p}}
  40. `);
  41. document.body.addEventListener("click", event => {
  42. if (event.detail === 2 && event.target.innerText.includes("#")) {
  43. const clickedHex = event.target.innerText?.match(/.*(#[0-9a-fA-F]{6}).*/)?.[1] ?? "";
  44. if (clickedHex) {
  45. document.getElementById("color-input").value = clickedHex;
  46. onColorChanged(state, clickedHex);
  47. }
  48. }
  49. });
  50. const metricSelect = document.getElementById("sort-metric");
  51. const clusterMetricSelect = document.getElementById("cluster-sort-metric");
  52. const addOption = (name, value) => {
  53. const opt = document.createElement("option");
  54. if (value) {
  55. opt.setAttribute("value", value);
  56. } else {
  57. opt.disabled = true;
  58. }
  59. metricSelect.appendChild(opt);
  60. opt.innerHTML = name;
  61. const clone = opt.cloneNode();
  62. clusterMetricSelect.appendChild(clone);
  63. clone.innerHTML = name;
  64. }
  65. addOption("--- Compare to whole ---");
  66. Object.entries(metrics).forEach(([value, metric]) => {
  67. if (value === "theta") {
  68. addOption("--- Angle to mean ---");
  69. } else if (value === "delta") {
  70. addOption("--- Distance to mean ---");
  71. } else if (value === "inertia") {
  72. addOption("--- Statistics ---");
  73. }
  74. addOption(metric.option, value);
  75. });
  76. document.getElementById("sort-metric").firstChild.nextSibling.nextSibling.selected = true;
  77. document.getElementById("cluster-sort-metric").lastChild.selected = true;
  78. onRandomColor(state);
  79. }
  80. </script>
  81. </head>
  82. <body>
  83. <noscript>Requires javascript</noscript>
  84. <div
  85. style="
  86. display: flex;
  87. flex-flow: row nowrap;
  88. justify-content: flex-start;
  89. align-items: stretch;
  90. "
  91. >
  92. <div style="display: inline-block" class="toggle-box">
  93. <input
  94. autocomplete="off"
  95. type="checkbox"
  96. id="all-control-toggle"
  97. class="toggle-button"
  98. role="button"
  99. />
  100. <label
  101. for="all-control-toggle"
  102. style="min-width: 1.1em; display: inline-block"
  103. >
  104. <div class="toggle-off" style="writing-mode: vertical-rl">
  105. ► Controls
  106. </div>
  107. <hr
  108. class="toggle-off"
  109. style="color: var(--highlight); margin-top: 1em"
  110. />
  111. <div
  112. class="toggle-off"
  113. style="margin-top: 16px; text-align: center"
  114. id="collapsed-sort"
  115. ></div>
  116. <div
  117. class="toggle-off"
  118. style="margin-top: 16px; text-align: center"
  119. id="collapsed-fn-name"
  120. ></div>
  121. <hr
  122. class="toggle-off"
  123. style="color: var(--highlight); margin-top: 1em"
  124. />
  125. <div
  126. class="toggle-off"
  127. style="margin-top: 16px; text-align: center"
  128. id="collapsed-cluster-sort"
  129. ></div>
  130. <div
  131. class="toggle-off"
  132. style="margin-top: 16px; text-align: center"
  133. id="collapsed-cluster"
  134. ></div>
  135. <div
  136. class="toggle-off"
  137. style="margin-top: 16px; text-align: center"
  138. id="collapsed-cluster-scale"
  139. ></div>
  140. <div
  141. class="toggle-off"
  142. style="margin-top: 16px; text-align: center"
  143. id="collapsed-cluster-mult-total"
  144. ></div>
  145. <hr
  146. class="toggle-off"
  147. style="color: var(--highlight); margin-top: 1em"
  148. />
  149. <div
  150. class="toggle-off"
  151. style="margin-top: 16px; text-align: center"
  152. id="collapsed-scale"
  153. ></div>
  154. <div class="toggle-on">▼ Controls</div>
  155. </label>
  156. <div class="toggle-on">
  157. <div class="control-grid">
  158. <img
  159. style="grid-area: bulb; justify-self: center"
  160. src="https://img.pokemondb.net/sprites/sword-shield/icon/bulbasaur.png"
  161. />
  162. <div style="grid-area: qvec" id="qvec"></div>
  163. <div class="toggle-box" style="grid-area: cspc">
  164. <input
  165. autocomplete="off"
  166. type="checkbox"
  167. class="toggle-button"
  168. id="space-toggle"
  169. role="button"
  170. onchange="state.space = event.target.checked ? 'rgb' : 'jab'; onControlsChanged(state); rerenderSearch(state)"
  171. />
  172. <label
  173. for="space-toggle"
  174. style="
  175. display: flex;
  176. flex-flow: row nowrap;
  177. justify-content: flex-start;
  178. align-items: flex-start;
  179. padding-bottom: 0.5em;
  180. "
  181. >
  182. Color space:&nbsp;
  183. <span class="toggle-off">CIECAM02-UCS (Jab)</span>
  184. <span class="toggle-on">sRGB</span>
  185. </label>
  186. </div>
  187. <label for="num-poke" style="grid-area: liml">
  188. Results:&nbsp;<span id="num-poke-display">10</span>
  189. </label>
  190. <input
  191. id="num-poke"
  192. style="grid-area: limt"
  193. autocomplete="off"
  194. type="range"
  195. min="1"
  196. max="100"
  197. value="10"
  198. oninput="document.getElementById('num-poke-display').textContent = event.target.value"
  199. onchange="state.number = event.target.value; onControlsChanged(state)"
  200. />
  201. <div class="toggle-box" style="grid-area: sort">
  202. <input
  203. autocomplete="off"
  204. type="checkbox"
  205. class="toggle-button"
  206. id="sort-order"
  207. role="button"
  208. onchange="state.sortOrder = event.target.checked ? 'max' : 'min'; onControlsChanged(state)"
  209. />
  210. <label for="sort-order">
  211. <span class="toggle-off">Minimize</span>
  212. <span class="toggle-on">Maximize</span>
  213. </label>
  214. </div>
  215. <select
  216. style="grid-area: metr"
  217. autocomplete="off"
  218. id="sort-metric"
  219. onchange="state.sortMetric = event.target.value; onControlsChanged(state)"
  220. ></select>
  221. <div
  222. style="
  223. grid-area: disp;
  224. padding-bottom: 0.5em;
  225. justify-self: center;
  226. "
  227. id="metric-display"
  228. ></div>
  229. <div
  230. style="
  231. grid-area: munu;
  232. padding-top: 0.5em;
  233. justify-self: center;
  234. display: flex;
  235. flex-flow: row nowrap;
  236. justify-content: space-between;
  237. width: 90%;
  238. "
  239. >
  240. <div id="mu-def"></div>
  241. <div id="nu-def"></div>
  242. </div>
  243. </div>
  244. <div class="toggle-box cluster-grid">
  245. <input
  246. autocomplete="off"
  247. type="checkbox"
  248. class="toggle-button"
  249. id="cluster-toggle"
  250. role="button"
  251. checked
  252. onchange="state.useCluster = event.target.checked; onControlsChanged(state)"
  253. />
  254. <label
  255. for="cluster-toggle"
  256. style="
  257. grid-area: albl;
  258. display: flex;
  259. flex-flow: row nowrap;
  260. justify-content: flex-start;
  261. align-items: flex-start;
  262. padding-bottom: 0.5em;
  263. "
  264. >
  265. Across&nbsp;
  266. <span class="toggle-off">whole image</span>
  267. <span class="toggle-on">best cluster by...</span>
  268. </label>
  269. <div
  270. style="grid-area: sort; justify-self: end"
  271. class="toggle-on toggle-box"
  272. >
  273. <input
  274. autocomplete="off"
  275. checked
  276. type="checkbox"
  277. class="toggle-button"
  278. id="cluster-sort-order"
  279. role="button"
  280. onchange="state.clusterSettings.sortOrder = event.target.checked ? 'max' : 'min'; onControlsChanged(state)"
  281. />
  282. <label for="cluster-sort-order">
  283. <span class="toggle-off">Minimizing</span>
  284. <span class="toggle-on">Maximizing</span>
  285. </label>
  286. </div>
  287. <select
  288. style="grid-area: metr"
  289. autocomplete="off"
  290. id="cluster-sort-metric"
  291. class="toggle-on"
  292. onchange="state.clusterSettings.sortMetric = event.target.value; onControlsChanged(state)"
  293. ></select>
  294. <span style="grid-area: blbl; justify-self: end" class="toggle-on"
  295. >Rescaled by</span
  296. >
  297. <select
  298. style="grid-area: clus"
  299. autocomplete="off"
  300. id="cluster-scale-option"
  301. class="toggle-on"
  302. onchange="state.clusterSettings.scaleOption = event.target.value; onControlsChanged(state)"
  303. >
  304. <option value="none" selected>None</option>
  305. <option value="direct">Cluster size</option>
  306. <option value="inverse">Inverse cluster size</option>
  307. <option value="size">Image size</option>
  308. <option value="inverseSize">Inverse image size</option>
  309. </select>
  310. <span style="grid-area: slbl" class="toggle-on"
  311. >And scale result by</span
  312. >
  313. <select
  314. style="grid-area: scal"
  315. autocomplete="off"
  316. id="scale-option"
  317. class="toggle-on"
  318. onchange="state.scaleOption = event.target.value; onControlsChanged(state)"
  319. >
  320. <option value="none">None</option>
  321. <option value="direct">Cluster size</option>
  322. <option value="inverse" selected>Inverse cluster size</option>
  323. <option value="size">Image size</option>
  324. <option value="inverseSize">Inverse image size</option>
  325. </select>
  326. <div class="toggle-on toggle-box" style="grid-area: mult">
  327. <input
  328. autocomplete="off"
  329. type="checkbox"
  330. class="toggle-button"
  331. id="multiply-toggle"
  332. role="button"
  333. checked
  334. onchange="state.clusterSettings.multWithTotal = event.target.checked; onControlsChanged(state)"
  335. />
  336. <label
  337. for="multiply-toggle"
  338. style="
  339. display: flex;
  340. flex-flow: row nowrap;
  341. justify-content: flex-start;
  342. align-items: flex-start;
  343. padding-bottom: 0.5em;
  344. "
  345. >
  346. And
  347. <span class="toggle-off">&nbsp;do not&nbsp;</span>
  348. multiply by whole image score
  349. </label>
  350. </div>
  351. <div
  352. style="
  353. grid-area: disp;
  354. padding-top: 0.5em;
  355. padding-bottom: 0.5em;
  356. padding-top: 0.5em;
  357. padding-bottom: 0.5em;
  358. justify-self: center;
  359. overflow: hidden;
  360. "
  361. id="cluster-metric-display"
  362. class="toggle-on"
  363. ></div>
  364. </div>
  365. <div
  366. style="
  367. width: 100%;
  368. display: flex;
  369. flex-flow: column nowrap;
  370. justify-content: flex-start;
  371. align-items: center;
  372. "
  373. >
  374. <span style="align-self: flex-start">Final metric:</span>
  375. <div
  376. id="final-metric-display"
  377. style="padding-top: 0.5em; padding-bottom: 0.5em"
  378. ></div>
  379. </div>
  380. </div>
  381. </div>
  382. <div class="divider"></div>
  383. <div class="list-container">
  384. <div class="search-container">
  385. <button type="button" onclick="onRandomColor(state)">
  386. Random Color
  387. </button>
  388. <input
  389. autocomplete="off"
  390. maxlength="7"
  391. id="color-input"
  392. value="#222222"
  393. oninput="onColorChanged(state, event.target.value)"
  394. />
  395. </div>
  396. <ul id="result-list" class="pkmn-list"></ul>
  397. </div>
  398. <div class="divider"></div>
  399. <div class="list-container">
  400. <div class="search-container">
  401. <button type="button" onclick="onRandomName(state)">
  402. Random Pokemon
  403. </button>
  404. <input
  405. autocomplete="off"
  406. size="15"
  407. oninput="onSearch(state, event.target.value)"
  408. />
  409. </div>
  410. <ul id="search-list" class="pkmn-list"></ul>
  411. </div>
  412. <div class="divider"></div>
  413. <div>
  414. Previous colors:
  415. <div id="prev-colors"></div>
  416. </div>
  417. </div>
  418. </body>
  419. </html>