From 44c24799306ad34744b81e5e1792a4f2d34d051d Mon Sep 17 00:00:00 2001
From: inthar-raven <36112167+inthar-raven@users.noreply.github.com>
Date: Mon, 27 Nov 2023 16:21:59 -0500
Subject: [PATCH] Issue #411, add radio buttons to change the indexing of the
 top row of the interval matrix in Analysis view.

---
 src/App.vue                | 12 +++++++++++-
 src/analysis.ts            |  4 ++--
 src/views/AnalysisView.vue | 35 +++++++++++++++++++++++++++++++++--
 3 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/src/App.vue b/src/App.vue
index 5186d777..fc6fc652 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -120,6 +120,7 @@ const centsFractionDigits = ref(3);
 const decimalFractionDigits = ref(5);
 const showVirtualQwerty = ref(false);
 const midiOctaveOffset = ref(-1);
+const intervalMatrixIndexing = ref(0);
 
 // Special keyboard codes also from local storage.
 const deactivationCode = ref("Backquote");
@@ -783,7 +784,11 @@ onMounted(() => {
   if ("midiOctaveOffset" in storage) {
     midiOctaveOffset.value = parseInt(storage.getItem("midiOctaveOffset")!);
   }
-
+  if ("intervalMatrixIndexing" in storage) {
+    intervalMatrixIndexing.value = parseInt(
+      storage.getItem("intervalMatrixIndexing")!
+    );
+  }
   // Fetch special key map
   if ("deactivationCode" in storage) {
     deactivationCode.value = storage.getItem("deactivationCode")!;
@@ -968,6 +973,9 @@ watch(showVirtualQwerty, (newValue) =>
 watch(midiOctaveOffset, (newValue) =>
   window.localStorage.setItem("midiOctaveOffset", newValue.toString())
 );
+watch(intervalMatrixIndexing, (newValue) =>
+  window.localStorage.setItem("intervalMatrixIndexing", newValue.toString())
+);
 // Store keymaps
 watch(deactivationCode, (newValue) =>
   window.localStorage.setItem("deactivationCode", newValue)
@@ -1069,6 +1077,7 @@ watch(degreeDownCode, (newValue) =>
     :keyboardMapping="keyboardMapping"
     :showVirtualQwerty="showVirtualQwerty"
     :midiOctaveOffset="midiOctaveOffset"
+    :intervalMatrixIndexing="intervalMatrixIndexing"
     @update:audioDelay="audioDelay = $event"
     @update:mainVolume="mainVolume = $event"
     @update:scaleName="scaleName = $event"
@@ -1095,6 +1104,7 @@ watch(degreeDownCode, (newValue) =>
     @update:decimalFractionDigits="decimalFractionDigits = $event"
     @update:showVirtualQwerty="showVirtualQwerty = $event"
     @update:midiOctaveOffset="midiOctaveOffset = $event"
+    @update:intervalMatrixIndexing="intervalMatrixIndexing = $event"
     @update:deactivationCode="deactivationCode = $event"
     @update:equaveUpCode="equaveUpCode = $event"
     @update:equaveDownCode="equaveDownCode = $event"
diff --git a/src/analysis.ts b/src/analysis.ts
index 6f82dfb9..f2129568 100644
--- a/src/analysis.ts
+++ b/src/analysis.ts
@@ -105,10 +105,10 @@ export function utonalFundamental(frequencies: number[], maxDivisor = 23) {
 // Interval matrix a.k.a the modes of a scale
 export function intervalMatrix(scale: Scale) {
   const result = [];
-  const degrees = [...Array(scale.size + 1).keys()];
+  const columns = [...Array(scale.size + 1).keys()];
   for (let i = 0; i < scale.size; ++i) {
     const mode = scale.rotate(i);
-    result.push(degrees.map((j) => mode.getInterval(j)));
+    result.push(columns.map((j) => mode.getInterval(j)));
   }
   return result;
 }
diff --git a/src/views/AnalysisView.vue b/src/views/AnalysisView.vue
index d756d9c3..b8a204b9 100644
--- a/src/views/AnalysisView.vue
+++ b/src/views/AnalysisView.vue
@@ -16,8 +16,17 @@ const props = defineProps<{
   scale: Scale;
   virtualSynth: VirtualSynth;
   colorScheme: "light" | "dark";
+  intervalMatrixIndexing: number;
 }>();
 
+const emit = defineEmits(["update:intervalMatrixIndexing"]);
+
+const intervalMatrixIndexing = computed({
+  get: () => props.intervalMatrixIndexing,
+  set: (newValue: string) =>
+    emit("update:intervalMatrixIndexing", parseInt(newValue, 10)),
+});
+
 const cellFormat = ref<"best" | "cents" | "decimal">("best");
 const trailLongevity = ref(70);
 const maxOtonalRoot = ref(16);
@@ -100,9 +109,9 @@ const matrix = computed(() => {
         <tr>
           <th></th>
           <th v-for="i of Math.min(scale.size, MAX_SCALE_SIZE)" :key="i">
-            {{ i }}
+            {{ i - 1 + intervalMatrixIndexing }}
           </th>
-          <th>({{ scale.size + 1 }})</th>
+          <th>({{ scale.size + intervalMatrixIndexing }})</th>
         </tr>
         <tr v-for="(row, i) of matrix" :key="i">
           <th>{{ formatMatrixCell(scale.getInterval(i)) }}</th>
@@ -143,6 +152,28 @@ const matrix = computed(() => {
           <label for="format-decimal"> Decimal ratio </label>
         </span>
       </div>
+      <div class="control radio-group">
+        <label>Interval indexing</label>
+        <span>
+          <input
+            type="radio"
+            id="indexing-zero"
+            value="0"
+            v-model="intervalMatrixIndexing"
+          />
+          <label for="indexing-zero"> 0-indexing (default) </label>
+        </span>
+
+        <span>
+          <input
+            type="radio"
+            id="indexing-one"
+            value="1"
+            v-model="intervalMatrixIndexing"
+          />
+          <label for="indexing-one"> 1-indexing </label>
+        </span>
+      </div>
     </div>
     <div class="columns-container">
       <div class="column">