Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it optional to show CS, variety and brightness on Analysis #637

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/stores/scale.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Scale } from '@/scale'
import { midiNoteNumberToEnharmonics, type AccidentalStyle } from '@/utils'
import { midiNoteNumberToEnharmonics, type AccidentalStyle, syncValues } from '@/utils'
import { defineStore } from 'pinia'
import { computed, ref, watch } from 'vue'
import { mmod, mtof } from 'xen-dev-utils'
Expand Down Expand Up @@ -272,9 +272,7 @@ export const useScaleStore = defineStore('scale', () => {
})

// Local storage watchers
watch(accidentalPreference, (newValue) => localStorage.setItem('accidentalPreference', newValue))
watch(hasLeftOfZ, (newValue) => window.localStorage.setItem('hasLeftOfZ', newValue.toString()))
watch(gas, (newValue) => window.localStorage.setItem('gas', newValue.toString()))
syncValues({ accidentalPreference, hasLeftOfZ, gas })

function latticeView(this: ExpressionVisitor) {
const scale = this.getCurrentScale()
Expand Down
50 changes: 30 additions & 20 deletions src/stores/state.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { reactive, ref, watch } from 'vue'
import { defineStore } from 'pinia'
import { UNIX_NEWLINE } from '@/constants'
import { syncValues } from '@/utils'

export const useStateStore = defineStore('state', () => {
const isomorphicVertical = ref(5)
Expand All @@ -23,7 +24,15 @@ export const useStateStore = defineStore('state', () => {
const centsFractionDigits = ref(parseInt(storage.getItem('centsFractionDigits') ?? '3', 10))
const decimalFractionDigits = ref(parseInt(storage.getItem('decimalFractionDigits') ?? '5', 10))
const showVirtualQwerty = ref(storage.getItem('showVirtualQwerty') === 'true')

// Analysis preferences
const intervalMatrixIndexing = ref(parseInt(storage.getItem('intervalMatrixIndexing') ?? '0', 10))
const maxMatrixWidth = ref(parseInt(storage.getItem('maxMatrixWidth') ?? '100', 10))
const calculateConstantStructureViolations = ref(
storage.getItem('calculateConstantStructureViolations') === 'true'
)
const calculateVariety = ref(storage.getItem('calculateVariety') === 'true')
const calculateBrightness = ref(storage.getItem('calculateBrightness') === 'true')

// Special keyboard codes also from local storage.
const deactivationCode = ref(storage.getItem('deactivationCode') ?? 'Backquote')
Expand All @@ -33,33 +42,30 @@ export const useStateStore = defineStore('state', () => {
const degreeDownCode = ref(storage.getItem('degreeDownCode') ?? 'NumpadSubtract')

// Local storage watchers
watch(newline, (newValue) => window.localStorage.setItem('newline', newValue))
syncValues({
newline,
centsFractionDigits,
decimalFractionDigits,
showVirtualQwerty,
intervalMatrixIndexing,
maxMatrixWidth,
calculateConstantStructureViolations,
calculateVariety,
calculateBrightness,
deactivationCode,
equaveUpCode,
equaveDownCode,
degreeUpCode,
degreeDownCode
})
watch(
colorScheme,
(newValue) => {
window.localStorage.setItem('colorScheme', newValue)
storage.setItem('colorScheme', newValue)
document.documentElement.setAttribute('data-theme', newValue)
},
{ immediate: true }
)
watch(centsFractionDigits, (newValue) =>
window.localStorage.setItem('centsFractionDigits', newValue.toString())
)
watch(decimalFractionDigits, (newValue) =>
window.localStorage.setItem('decimalFractionDigits', newValue.toString())
)
watch(showVirtualQwerty, (newValue) =>
window.localStorage.setItem('showVirtualQwerty', newValue.toString())
)
watch(intervalMatrixIndexing, (newValue) =>
window.localStorage.setItem('intervalMatrixIndexing', newValue.toString())
)
// Store keymaps
watch(deactivationCode, (newValue) => window.localStorage.setItem('deactivationCode', newValue))
watch(equaveUpCode, (newValue) => window.localStorage.setItem('equaveUpCode', newValue))
watch(equaveDownCode, (newValue) => window.localStorage.setItem('equaveDownCode', newValue))
watch(degreeUpCode, (newValue) => window.localStorage.setItem('degreeUpCode', newValue))
watch(degreeDownCode, (newValue) => window.localStorage.setItem('degreeDownCode', newValue))

return {
// Live state
Expand All @@ -75,6 +81,10 @@ export const useStateStore = defineStore('state', () => {
decimalFractionDigits,
showVirtualQwerty,
intervalMatrixIndexing,
maxMatrixWidth,
calculateConstantStructureViolations,
calculateVariety,
calculateBrightness,
deactivationCode,
equaveUpCode,
equaveDownCode,
Expand Down
12 changes: 11 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computed, type ComputedRef } from 'vue'
import { computed, watch, type ComputedRef, type Ref } from 'vue'
import { gcd, mmod } from 'xen-dev-utils'
import {
evaluateExpression,
Expand Down Expand Up @@ -445,3 +445,13 @@ export function annotateColors(sourceLines: string[], keyColors: string[]) {
sourceLines[i] += ' ' + keyColors[mmod(i + 1, keyColors.length)].replace(/%/g, '')
}
}

/**
* Synchronize local storage with the values of Vue refs.
* @param values Vue refs to watch in a `{ref1, ref2}` record.
*/
export function syncValues(values: Record<string, Ref>) {
for (const [key, value] of Object.entries(values)) {
watch(value, (newValue) => window.localStorage.setItem(key, String(newValue)))
}
}
56 changes: 47 additions & 9 deletions src/views/AnalysisView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@ import { useScaleStore } from '@/stores/scale'
import { mmod } from 'xen-dev-utils'
import { OCTAVE } from '@/constants'

const MAX_SCALE_SIZE = 100

const audio = useAudioStore()
const state = useStateStore()
const scale = useScaleStore()

const cellFormat = ref<'best' | 'cents' | 'decimal'>('best')
const showOptions = ref(false)
const trailLongevity = ref(70)
const maxOtonalRoot = ref(16)
const maxUtonalRoot = ref(23)
Expand Down Expand Up @@ -80,7 +79,9 @@ function formatMatrixCell(interval: Interval) {

const highlights = reactive<boolean[][]>([])

const matrix = computed(() => intervalMatrix(scale.relativeIntervals))
const matrix = computed(() =>
intervalMatrix(scale.relativeIntervals.slice(0, state.maxMatrixWidth))
)

const matrixRows = computed(() => matrix.value.map((row) => row.map(formatMatrixCell)))

Expand Down Expand Up @@ -124,6 +125,9 @@ const nedjiProjector = computed(() => {
})

function highlight(y?: number, x?: number) {
if (!state.calculateConstantStructureViolations) {
return
}
if (highlights.length !== matrix.value.length) {
highlights.length = 0
for (let i = 0; i < matrix.value.length; ++i) {
Expand Down Expand Up @@ -170,11 +174,11 @@ function highlight(y?: number, x?: number) {
<table @mouseleave="highlight()">
<tr>
<th></th>
<th v-for="i of Math.min(scale.scale.size, MAX_SCALE_SIZE)" :key="i">
<th v-for="i of Math.min(scale.scale.size, state.maxMatrixWidth)" :key="i">
{{ i - 1 + state.intervalMatrixIndexing }}
</th>
<th>({{ scale.scale.size + state.intervalMatrixIndexing }})</th>
<th class="brightness">Bright %</th>
<th class="brightness" v-if="state.calculateBrightness">Bright %</th>
</tr>
<tr v-for="(row, i) of matrixRows" :key="i">
<th :class="{ held: heldScaleDegrees.has(i) }">
Expand All @@ -183,17 +187,20 @@ function highlight(y?: number, x?: number) {
<td
v-for="(name, j) of row"
:key="j"
:class="{ violator: violations[i][j], highlight: (highlights[i] ?? [])[j] }"
:class="{
violator: state.calculateConstantStructureViolations && violations[i][j],
highlight: (highlights[i] ?? [])[j]
}"
@mouseover="highlight(i, j)"
>
{{ name }}
</td>
<td class="brightness">{{ brightness[i] }}</td>
<td class="brightness" v-if="state.calculateBrightness">{{ brightness[i] }}</td>
</tr>
<tr class="variety">
<tr class="variety" v-if="state.calculateVariety">
<th>Var</th>
<td v-for="(v, i) of variety" :key="i">{{ v }}</td>
<td class="brightness"></td>
<td class="brightness" v-if="state.calculateBrightness"></td>
</tr>
</table>
</div>
Expand All @@ -215,6 +222,11 @@ function highlight(y?: number, x?: number) {
<label for="format-decimal"> Decimal ratio </label>
</span>
</div>
</div>
<p class="section" :class="{ open: showOptions }" @click="showOptions = !showOptions">
More options
</p>
<div class="control-group" v-show="showOptions">
<div class="control radio-group">
<label>Interval indexing</label>
<span>
Expand All @@ -227,6 +239,32 @@ function highlight(y?: number, x?: number) {
<label for="indexing-one"> 1-indexing </label>
</span>
</div>
<div class="control">
<label for="max-matrix-width">Maximum matrix width</label>
<input
id="max-matrix-width"
type="number"
min="1"
step="1"
v-model="state.maxMatrixWidth"
/>
</div>
<div class="control checkbox-container">
<input
id="calculate-violators"
type="checkbox"
v-model="state.calculateConstantStructureViolations"
/>
<label for="calculate-violators"> Show constant structure violations</label>
</div>
<div class="control checkbox-container">
<input id="calculate-variety" type="checkbox" v-model="state.calculateVariety" />
<label for="calculate-variety"> Show variety signature</label>
</div>
<div class="control checkbox-container">
<input id="calculate-brightness" type="checkbox" v-model="state.calculateBrightness" />
<label for="calculate-brightness"> Show mode brightness</label>
</div>
</div>
<div class="columns-container">
<div class="column">
Expand Down
2 changes: 1 addition & 1 deletion src/views/PreferencesView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ const scale = useScaleStore()
<label for="virtual-qwerty"> Virtual QWERTY in top menu</label>
</div>
<div class="control">
<input id="gas" type="number" min="1" v-model="scale.gas" />
<label for="gas">Computational budget (gas)</label>
<input id="gas" type="number" min="1" v-model="scale.gas" />
</div>
</div>
</div>
Expand Down
Loading