diff options
| author | s-ol <s+removethis@s-ol.nu> | 2023-05-06 22:07:32 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2023-05-07 09:53:29 +0000 |
| commit | 1c981aea4dbecb3ca77efbafac1be7f12012327d (patch) | |
| tree | 1a02a0f607ca9a095b80760beef5c9529ecd1eb7 /layout.js | |
| parent | restart with v2 (diff) | |
| download | isomorphic-kb-explorer-1c981aea4dbecb3ca77efbafac1be7f12012327d.tar.gz isomorphic-kb-explorer-1c981aea4dbecb3ca77efbafac1be7f12012327d.zip | |
add basic pattern highlighting
Diffstat (limited to 'layout.js')
| -rw-r--r-- | layout.js | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/layout.js b/layout.js new file mode 100644 index 0000000..e4eab79 --- /dev/null +++ b/layout.js @@ -0,0 +1,163 @@ +const panel = document.querySelector('aside.layout'); + +const svg = document.getElementById('diagram'); +const arrows = Array.from(svg.querySelectorAll('.arrow')); + +const preset = panel.querySelector('.control--preset'); +const controls = Array.from(panel.querySelectorAll('.control--axis')); +const steps = Array.from(panel.querySelectorAll('.control--axis > input')); +const dirs = Array.from(panel.querySelectorAll('.control--axis .dir input')); +const turnCCW = document.getElementById('turn-ccw'); +const turnCW = document.getElementById('turn-cw'); + +let state = [ 7, 2, null ]; +let last = 0; +let selected = null; + +let rot = 0; +let targetRot = 30; + +const completeState = ([a, b, c]) => { + if (a == null) { + a = b - c; + } else if (b == null) { + b = c + a; + } else { + c = b - a; + } + return [a, b, c, -a, -b, -c]; +}; + +const updateValues = () => { + const full = completeState(state); + + full.forEach((val, i) => { + arrows[i].querySelector('text').textContent = val; + + if (i < steps.length) steps[i].value = Math.abs(val); + if (i < dirs.length) { + dirs[i].checked = val < 0; + dirs[i].disabled = val == 0; + } + }); + updateFocus(); +}; + +const updateFocus = () => { + const full = completeState(state); + + controls.forEach((control, i) => { + let className = 'control control--axis'; + if (state[i] == null) className += ' driven'; + else if (i == selected) className += ' selected'; + if (state[i] == 0) className += ' dir-disabled'; + control.className = className; + }); + + full.forEach((val, i) => { + const positive = val > 0 || (val == 0 && i < 3); + + let className = 'arrow'; + if (state[i%3] == null) className += ' driven'; + if (!positive) className += ' negative'; + if (i%3 == selected) className += ' selected'; + arrows[i].className.baseVal = className; + }); +}; + +const select = (i) => { + if (state[i] != null) return false; + + const newState = [null, null, null]; + newState[i] = completeState(state)[i]; + newState[last] = state[last]; + state = newState; + last = i; + + return true; +}; + +export const turn = (dir) => { + targetRot += dir * 30; +} + +arrows.forEach((arrow, i) => { + arrow.onclick = () => steps[i%3].focus(); +}); + +controls.forEach((control, i) => { + control.addEventListener('focusin', () => { selected = i; select(i); updateFocus(); }); + control.addEventListener('focusout', () => { selected = null; updateFocus(); }); +}); + +steps.forEach((input, i) => { + input.onchange = () => { + select(i); + state[i] = +input.value; + if (dirs[i].checked) state[i] = -state[i]; + preset.value = 'custom'; + updateValues(); + }; +}); + +dirs.forEach((input, i) => { + input.onchange = () => { + select(i); + state[i] = state[i] * -1; + preset.value = 'custom'; + updateValues(); + }; +}); + +preset.onchange = () => { + switch (preset.value) { + case 'custom': return; + case 'wicki-hayden': state = [ 7, 2, null ]; break; + case 'janko': state = [ 1, 2, null ]; break; + case 'harmonic': state = [ 7, 4, null ]; break; + case 'gerhard': state = [ 4, 3, null ]; break; + } + + last = state.findIndex(s => s != null); + updateValues(); +}; +preset.value = 'wicki-hayden'; +preset.onchange(); + +turnCCW.onclick = () => turn(-1); +turnCW.onclick = () => turn(1); + +export const update = () => { + const delta = targetRot - rot; + if (Math.abs(delta) < 1) rot = targetRot; + else rot += delta * 0.1; + + if (rot < 60 && rot > -60) return; + + const full = completeState(state); + let driven = state.indexOf(null); + + // cw rotation + while (rot >= 60) { + rot -= 60; + targetRot -= 60; + full.unshift(full.pop()); + driven++; + } + + // cw rotation + while (rot <= -60) { + rot += 60; + targetRot += 60; + full.push(full.shift()); + driven = (driven-1+3) % 3; + } + + state = full.slice(0, 3); + state[driven%3] = null; + + updateValues(); +}; + +export const getSteps = () => completeState(state); +export const getRot = () => rot / 180 * Math.PI; |
