aboutsummaryrefslogtreecommitdiffstats
path: root/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'main.js')
-rw-r--r--main.js265
1 files changed, 69 insertions, 196 deletions
diff --git a/main.js b/main.js
index 85c34b9..4aa2009 100644
--- a/main.js
+++ b/main.js
@@ -1,182 +1,62 @@
-const svg = document.getElementById('diagram');
-const arrows = Array.from(svg.querySelectorAll('.arrow'));
-const preset = document.getElementById('layout-preset');
-const controls = Array.from(document.querySelectorAll('.axis-control'));
-const steps = Array.from(document.querySelectorAll('.axis-control > input'));
-const dirs = Array.from(document.querySelectorAll('.axis-control .dir input'));
-
-let state = [ 7, 2, null ];
-let last = 0;
-let selected = null;
-
-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 = 'axis-control';
- 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;
-};
-
-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();
- };
-});
-
-let rot = 0;
-let targetRot = 0;
-
-document.getElementById('turn-ccw').onclick = () => {
- targetRot -= 30;
-};
-document.getElementById('turn-cw').onclick = () => {
- targetRot += 30;
-};
-
-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();
-
-const bg = document.getElementById('canvas-bg').getContext('2d');
-const fg = document.getElementById('canvas-fg').getContext('2d');
+import * as layout from './layout.js';
+import * as pattern from './pattern.js';
const sqrt3 = Math.sqrt(3);
const sqrt32 = sqrt3 / 2;
-const size = 80;
+const size = 50;
const hs = size / 2;
const hh = sqrt32 * size;
-const hex2px = (q, r) => {
+const hex2px = ([q, r]) => {
const x = size * (3/2 * r);
const y = -size * (sqrt32 * r + sqrt3 * q);
return [x, y];
};
+const px2hex = ([x, y]) => {
+ const t1 = x / size;
+ const t2 = -y / hh / 2;
-const mul = (x, y, mat) => {
+ const q = Math.floor( (Math.floor(-y / hh) + Math.floor(t2 - t1) + 2 ) / 3);
+ const r = Math.floor( (Math.floor(t1 - t2) + Math.floor(t1 + t2) + 2 ) / 3);
+
+ return [q, r];
+}
+const rotate = ([x, y], a) => {
return [
- x * mat[0] + y * mat[1] + mat[2],
- y * mat[3] + y * mat[4] + mat[5],
+ Math.cos(a) * x + Math.sin(a) * y,
+ -Math.sin(a) * x + Math.cos(a) * y,
];
};
+const hexagon = (ctx) => {
+ ctx.beginPath();
+ ctx.moveTo(-hs, hh);
+ ctx.lineTo(hs, hh);
+ ctx.lineTo(size, 0);
+ ctx.lineTo(hs, -hh);
+ ctx.lineTo(-hs, -hh);
+ ctx.lineTo(-size, 0);
+ ctx.closePath();
+};
-const updateRotation = () => {
- 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);
+const main = document.querySelector('main');
+const bg = document.getElementById('canvas-bg').getContext('2d');
+const fg = document.getElementById('canvas-fg').getContext('2d');
- // cw rotation
- while (rot >= 60) {
- rot -= 60;
- targetRot -= 60;
- full.unshift(full.pop());
- driven++;
- }
+let mousePos = [0, 0];
+main.onmousemove = (e) => {
+ mousePos = [e.clientX - main.clientWidth/2, e.clientY - main.clientHeight/2];
+};
- // cw rotation
- while (rot <= -60) {
- rot += 60;
- targetRot += 60;
- full.push(full.shift());
- driven = (driven-1+3) % 3;
+document.body.onkeydown = (e) => {
+ switch (e.key) {
+ case 'q': layout.turn(-1); break;
+ case 'e': layout.turn(1); break;
}
-
- state = full.slice(0, 3);
- state[driven%3] = null;
-
- updateValues();
};
+let lastCanvasSize = 0;
+
const updateBackground = (canvasSize) => {
bg.strokeStyle = '#b9bdc1';
bg.strokeWidth = 1.5;
@@ -190,16 +70,9 @@ const updateBackground = (canvasSize) => {
const rMax = Math.min(rad, rad-q);
for (let r = rMin; r <= rMax; r++) {
bg.save();
- bg.translate(...hex2px(q, r));
+ bg.translate(...hex2px([q, r]));
- bg.beginPath();
- bg.moveTo(-hs, hh);
- bg.lineTo(hs, hh);
- bg.lineTo(size, 0);
- bg.lineTo(hs, -hh);
- bg.lineTo(-hs, -hh);
- bg.lineTo(-size, 0);
- bg.closePath();
+ hexagon(bg);
bg.stroke();
bg.restore();
@@ -207,17 +80,6 @@ const updateBackground = (canvasSize) => {
}
};
-const main = document.querySelector('main');
-
-let mousePos = [0, 0];
-main.onmousemove = (e) => {
- mousePos = [
- e.clientX - main.clientWidth/2,
- e.clientY - main.clientHeight/2,
- ];
-};
-
-let lastCanvasSize = 0;
const draw = () => {
const width = window.innerWidth;
const height = window.innerHeight;
@@ -234,42 +96,53 @@ const draw = () => {
fg.canvas.width = fg.canvas.width;
- updateRotation();
+ layout.update();
fg.translate(canvasSize/2, canvasSize/2);
- fg.fillStyle = '#303336';
- fg.font = `${size*0.8}px sans-serif`;
+ fg.font = `${size*0.5}px sans-serif`;
fg.textAlign = 'center';
+
+ fg.strokeStyle = '#ff0000';
+ fg.strokeWidth = 5;
+
+
+ const rot = layout.getRot();
+ const [qq, rr] = layout.getSteps();
- const rotRad = rot / 180 * Math.PI;
+ const steps = pattern.getSteps();
+ const length = pattern.getLength();
- const [qq, rr] = completeState(state);
const rad = Math.ceil(canvasSize / size / 3);
for (let q = -rad; q <= rad; q++) {
const rMin = Math.max(-rad, -q-rad);
const rMax = Math.min(rad, rad-q);
for (let r = rMin; r <= rMax; r++) {
fg.save();
- fg.translate(...hex2px(q, r));
-
- const val = q*qq + r*rr;
- fg.rotate(-rotRad);
- fg.fillText(val, 0, size/3);
+ fg.translate(...hex2px([q, r]));
+
+ const note = q*qq + r*rr;
+ let step = note % length;
+ step = (step + length) % length;
+
+ if (steps.includes(note)) {
+ fg.fillStyle = '#eeeeee';
+ hexagon(fg);
+ fg.fill();
+ }
+
+ fg.rotate(-rot);
+ fg.fillStyle = '#303336';
+ fg.fillText(step, 0, size/3);
fg.restore();
}
}
- const mouse = [
- Math.cos(rotRad) * mousePos[0] + Math.sin(rotRad) * mousePos[1],
- -Math.sin(rotRad) * mousePos[0] + Math.cos(rotRad) * mousePos[1],
- ];
-
- fg.arc(mouse[0], mouse[1], 5, 0, 2*Math.PI);
- fg.fill()
+ // const mouse = px2hex(rotate(mousePos, rot));
+ // fg.arc(...hex2px(mouse), 10, 0, 2*Math.PI);
- document.body.style.setProperty('--global-rot', `${rot}deg`);
+ document.body.style.setProperty('--global-rot', `${rot}rad`);
requestAnimationFrame(draw);
};