aboutsummaryrefslogtreecommitdiffstats
path: root/layout.js
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2023-05-06 22:07:32 +0000
committers-ol <s+removethis@s-ol.nu>2023-05-07 09:53:29 +0000
commit1c981aea4dbecb3ca77efbafac1be7f12012327d (patch)
tree1a02a0f607ca9a095b80760beef5c9529ecd1eb7 /layout.js
parentrestart with v2 (diff)
downloadisomorphic-kb-explorer-1c981aea4dbecb3ca77efbafac1be7f12012327d.tar.gz
isomorphic-kb-explorer-1c981aea4dbecb3ca77efbafac1be7f12012327d.zip
add basic pattern highlighting
Diffstat (limited to 'layout.js')
-rw-r--r--layout.js163
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;