瀏覽代碼

Move old code into new module, precompute inverse sizes

Kirk Trombley 2 年之前
父節點
當前提交
76d97ea889
共有 3 個文件被更改,包括 195 次插入8 次删除
  1. 7 5
      form.js
  2. 11 3
      index.html
  3. 177 0
      math.js

+ 7 - 5
form.js

@@ -124,8 +124,9 @@ const calcScores = (data, target) => {
     chroma: data.mu.chroma,
     importance: data.importance,
 
+    inverseSize: data.inverseSize,
     proportion: data.proportion,
-    inverseProportion: 1 / data.proportion,
+    inverseProportion: data.inverseProportion,
   };
 };
 
@@ -139,10 +140,11 @@ const scaleOptions = {
   direct: (scores) => scores.clusters.map((c) => c.proportion),
   inverse: (scores) => scores.clusters.map((c) => c.inverseProportion),
   size: (scores) => [scores.total.size, scores.total.size, scores.total.size],
-  inverseSize: (scores) => {
-    const c = 1 / scores.total.size;
-    return [c, c, c];
-  },
+  inverseSize: (scores) => [
+    scores.total.inverseSize,
+    scores.total.inverseSize,
+    scores.total.inverseSize,
+  ],
 };
 
 const currentScores = {};

+ 11 - 3
index.html

@@ -7,9 +7,7 @@
     <script src="https://unpkg.com/d3-color@3.0.1/dist/d3-color.min.js"></script>
     <script src="https://unpkg.com/d3-cam02@0.1.5/build/d3-cam02.min.js"></script>
     <script src="database-v3.js"></script>
-    <!-- TODO move these files -->
-    <script src="web/math.js"></script>
-    <script src="web/convert.js"></script>
+    <script src="math.js"></script>
     <script src="form.js"></script>
   </head>
   <body>
@@ -99,6 +97,16 @@
       </div>
     </template>
 
+    <div>
+      <img
+        src="https://img.pokemondb.net/sprites/sword-shield/icon/breloom.png"
+        alt="breloom"
+      />
+      <div>Breloom</div>
+      <div></div>
+      <div></div>
+    </div>
+
     <form action="javascript:void(0);" id="sortControl" autocomplete="off">
       <div>
         <label>

+ 177 - 0
math.js

@@ -0,0 +1,177 @@
+// Vector Math
+const vectorDot = (u, v) => u.map((x, i) => x * v[i]).reduce((x, y) => x + y);
+const vectorMag = (v) => Math.sqrt(vectorDot(v, v));
+
+// Angle Math
+const rad2deg = 180 / Math.PI;
+
+// Contrast
+const getContrastingTextColor = (hex) => {
+  const { r, g, b } = d3.color(hex);
+  return vectorDot([r, g, b], [0.3, 0.6, 0.1]) >= 128 ? "#222" : "#ddd";
+};
+
+// "Visual Importance"
+const calcImportance = (chroma, lightness, proportion) =>
+  chroma +
+  Math.tanh(100 * (chroma - 0.25)) + // penalty for being <25%
+  Math.tanh(100 * (chroma - 0.4)) + // penalty for being <40%
+  lightness +
+  Math.tanh(100 * (lightness - 0.5)) + // penalty for being <50%
+  proportion +
+  Math.tanh(100 * (proportion - 0.05)) + // penalty for being <5%
+  Math.tanh(100 * (proportion - 0.1)) + // penalty for being <15%
+  Math.tanh(100 * (proportion - 0.15)) + // penalty for being <15%
+  Math.tanh(100 * (proportion - 0.25)) + // penalty for being <25%
+  Math.tanh(100 * (proportion - 0.8)); // penalty for being <50%
+
+// Conversions
+const jab2hex = (jab) => d3.jab(...jab).formatHex();
+const rgb2hex = (rgb) => d3.rgb(...rgb).formatHex();
+const jab2hue = (jab) => d3.jch(d3.jab(...jab)).h || 0;
+const rgb2hue = (rgb) => d3.hsl(d3.rgb(...rgb)).h || 0;
+const jab2lit = ([j]) => j / 100;
+const rgb2lit = (rgb) => d3.hsl(d3.rgb(...rgb)).l || 0;
+const jab2chroma = (jab) => d3.jch(d3.jab(...jab)).C / 100;
+const rgb2chroma = (rgb) => d3.jch(d3.rgb(...rgb)).C / 100;
+
+// Pre-computation
+const buildVectorData = (vector, toHue, toLightness, toChroma, toHex) => {
+  const sqMag = vectorDot(vector, vector);
+  const mag = Math.sqrt(sqMag);
+  const unit = vector.map((c) => c / mag);
+  const hue = toHue(vector);
+  const lightness = toLightness(vector);
+  const chroma = toChroma(vector);
+  const hex = toHex(vector);
+  return { vector, sqMag, mag, unit, hue, lightness, chroma, hex };
+};
+
+const buildClusterData = (
+  size,
+  inertia,
+  mu1,
+  mu2,
+  mu3,
+  nu1,
+  nu2,
+  nu3,
+  totalSize,
+  toHue,
+  toLightness,
+  toChroma,
+  toHex
+) => {
+  const mu = buildVectorData([mu1, mu2, mu3], toHue, toLightness, toChroma, toHex);
+  const nu = [nu1, nu2, nu3];
+  const muNuAngle = rad2deg * Math.acos(vectorDot(mu.unit, nu) / vectorMag(nu));
+  const proportion = size / totalSize;
+  const importance = calcImportance(mu.chroma, mu.lightness, proportion);
+  return {
+    size,
+    inverseSize: 1 / size,
+    inertia,
+    mu,
+    nu,
+    muNuAngle,
+    proportion,
+    inverseProportion: 1 / proportion,
+    importance,
+  };
+};
+
+const buildPokemonData = ([name, size, ...values]) => ({
+  name,
+  jab: {
+    total: buildClusterData(
+      size,
+      ...values.slice(0, 7),
+      size,
+      jab2hue,
+      jab2lit,
+      jab2chroma,
+      jab2hex
+    ),
+    clusters: [
+      buildClusterData(
+        ...values.slice(7, 15),
+        size,
+        jab2hue,
+        jab2lit,
+        jab2chroma,
+        jab2hex
+      ),
+      buildClusterData(
+        ...values.slice(15, 23),
+        size,
+        jab2hue,
+        jab2lit,
+        jab2chroma,
+        jab2hex
+      ),
+      buildClusterData(
+        ...values.slice(23, 31),
+        size,
+        jab2hue,
+        jab2lit,
+        jab2chroma,
+        jab2hex
+      ),
+      buildClusterData(
+        ...values.slice(31, 39),
+        size,
+        jab2hue,
+        jab2lit,
+        jab2chroma,
+        jab2hex
+      ),
+    ].filter((c) => c.size !== 0),
+  },
+  rgb: {
+    total: buildClusterData(
+      size,
+      ...values.slice(39, 46),
+      size,
+      rgb2hue,
+      rgb2lit,
+      rgb2chroma,
+      rgb2hex
+    ),
+    clusters: [
+      buildClusterData(
+        ...values.slice(46, 54),
+        size,
+        rgb2hue,
+        rgb2lit,
+        rgb2chroma,
+        rgb2hex
+      ),
+      buildClusterData(
+        ...values.slice(54, 62),
+        size,
+        rgb2hue,
+        rgb2lit,
+        rgb2chroma,
+        rgb2hex
+      ),
+      buildClusterData(
+        ...values.slice(62, 70),
+        size,
+        rgb2hue,
+        rgb2lit,
+        rgb2chroma,
+        rgb2hex
+      ),
+      buildClusterData(
+        ...values.slice(70, 78),
+        size,
+        rgb2hue,
+        rgb2lit,
+        rgb2chroma,
+        rgb2hex
+      ),
+    ].filter((c) => c.size !== 0),
+  },
+});
+
+const pokemonData = databaseV3.map((row) => buildPokemonData(row));