fix preset selects
s-ol
7 days ago
2 | 2 | const svg = document.getElementById('diagram'); |
3 | 3 | const arrows = Array.from(svg.querySelectorAll('.arrow')); |
4 | 4 | |
5 | const preset = panel.querySelector('.control--preset'); | |
5 | const preset = panel.querySelector('.control--preset select'); | |
6 | 6 | const controls = Array.from(panel.querySelectorAll('.control--axis')); |
7 | 7 | const steps = Array.from(panel.querySelectorAll('.control--axis > input')); |
8 | 8 | const dirs = Array.from(panel.querySelectorAll('.control--axis .dir input')); |
27 | 27 | return [a, b, c, -a, -b, -c]; |
28 | 28 | }; |
29 | 29 | |
30 | const PRESETS = { | |
31 | 'wicki-hayden': [ 7, 2, null ], | |
32 | 'janko': [ 1, 2, null ], | |
33 | 'harmonic': [ 7, 4, null ], | |
34 | 'gerhard': [ 4, 3, null ], | |
35 | }; | |
36 | ||
30 | 37 | const updateValues = () => { |
31 | 38 | const full = completeState(state); |
32 | 39 | |
39 | 46 | dirs[i].disabled = val == 0; |
40 | 47 | } |
41 | 48 | }); |
49 | ||
50 | const presetName = Object.keys(PRESETS).find(k => completeState(PRESETS[k]).join(',') === full.join(',')); | |
51 | preset.value = presetName ?? 'custom'; | |
52 | ||
42 | 53 | updateFocus(); |
43 | 54 | }; |
44 | 55 | |
94 | 105 | select(i); |
95 | 106 | state[i] = +input.value; |
96 | 107 | if (dirs[i].checked) state[i] = -state[i]; |
97 | preset.value = 'custom'; | |
98 | 108 | updateValues(); |
99 | 109 | }; |
100 | 110 | }); |
103 | 113 | input.onchange = () => { |
104 | 114 | select(i); |
105 | 115 | state[i] = state[i] * -1; |
106 | preset.value = 'custom'; | |
107 | 116 | updateValues(); |
108 | 117 | }; |
109 | 118 | }); |
110 | 119 | |
111 | 120 | preset.onchange = () => { |
112 | switch (preset.value) { | |
113 | case 'custom': return; | |
114 | case 'wicki-hayden': state = [ 7, 2, null ]; break; | |
115 | case 'janko': state = [ 1, 2, null ]; break; | |
116 | case 'harmonic': state = [ 7, 4, null ]; break; | |
117 | case 'gerhard': state = [ 4, 3, null ]; break; | |
121 | const nextState = PRESETS[preset.value]; | |
122 | if (nextState) { | |
123 | state = nextState.slice(); | |
124 | last = state.findIndex(s => s != null); | |
125 | updateValues(); | |
118 | 126 | } |
119 | ||
120 | last = state.findIndex(s => s != null); | |
121 | updateValues(); | |
122 | 127 | }; |
123 | 128 | preset.value = 'wicki-hayden'; |
124 | 129 | preset.onchange(); |
56 | 56 | |
57 | 57 | let lastCanvasSize = 0; |
58 | 58 | |
59 | const updateBackground = (canvasSize) => { | |
60 | bg.strokeStyle = '#b9bdc1'; | |
61 | bg.strokeWidth = 1.5; | |
59 | const updateForeground = (canvasSize) => { | |
60 | fg.strokeStyle = '#b9bdc1'; | |
61 | fg.strokeWidth = 1.5; | |
62 | 62 | |
63 | bg.canvas.width = bg.canvas.width; | |
64 | bg.translate(canvasSize/2, canvasSize/2); | |
63 | fg.canvas.width = fg.canvas.width; | |
64 | fg.translate(canvasSize/2, canvasSize/2); | |
65 | 65 | |
66 | 66 | const rad = Math.ceil(canvasSize / size / 3); |
67 | 67 | for (let q = -rad; q <= rad; q++) { |
68 | 68 | const rMin = Math.max(-rad, -q-rad); |
69 | 69 | const rMax = Math.min(rad, rad-q); |
70 | 70 | for (let r = rMin; r <= rMax; r++) { |
71 | bg.save(); | |
72 | bg.translate(...hex2px([q, r])); | |
71 | fg.save(); | |
72 | fg.translate(...hex2px([q, r])); | |
73 | 73 | |
74 | hexagon(bg); | |
75 | bg.stroke(); | |
74 | hexagon(fg); | |
75 | fg.stroke(); | |
76 | 76 | |
77 | bg.restore(); | |
77 | fg.restore(); | |
78 | 78 | } |
79 | 79 | } |
80 | 80 | }; |
90 | 90 | fg.canvas.width = fg.canvas.height = canvasSize; |
91 | 91 | lastCanvasSize = canvasSize; |
92 | 92 | |
93 | updateBackground(canvasSize); | |
93 | updateForeground(canvasSize); | |
94 | 94 | } |
95 | 95 | |
96 | fg.canvas.width = fg.canvas.width; | |
96 | bg.canvas.width = bg.canvas.width; | |
97 | 97 | |
98 | 98 | layout.update(); |
99 | 99 | |
100 | fg.translate(canvasSize/2, canvasSize/2); | |
100 | bg.translate(canvasSize/2, canvasSize/2); | |
101 | 101 | |
102 | fg.font = `${size*0.5}px sans-serif`; | |
103 | fg.textAlign = 'center'; | |
102 | bg.font = `${size*0.5}px sans-serif`; | |
103 | bg.textAlign = 'center'; | |
104 | bg.textBaseline = 'middle'; | |
104 | 105 | |
105 | fg.strokeStyle = '#ff0000'; | |
106 | fg.strokeWidth = 5; | |
107 | ||
106 | bg.strokeStyle = '#ff0000'; | |
107 | bg.strokeWidth = 5; | |
108 | 108 | |
109 | 109 | const rot = layout.getRot(); |
110 | 110 | const [qq, rr] = layout.getSteps(); |
117 | 117 | const rMin = Math.max(-rad, -q-rad); |
118 | 118 | const rMax = Math.min(rad, rad-q); |
119 | 119 | for (let r = rMin; r <= rMax; r++) { |
120 | fg.save(); | |
121 | fg.translate(...hex2px([q, r])); | |
120 | bg.save(); | |
121 | bg.translate(...hex2px([q, r])); | |
122 | 122 | |
123 | 123 | const note = q*qq + r*rr; |
124 | 124 | let step = note % length; |
125 | 125 | step = (step + length) % length; |
126 | 126 | |
127 | 127 | if (steps.includes(note)) { |
128 | fg.fillStyle = '#eeeeee'; | |
129 | hexagon(fg); | |
130 | fg.fill(); | |
128 | bg.fillStyle = '#eeeeee'; | |
129 | hexagon(bg); | |
130 | bg.fill(); | |
131 | 131 | } |
132 | 132 | |
133 | fg.rotate(-rot); | |
134 | fg.fillStyle = '#303336'; | |
135 | fg.fillText(step, 0, size/3); | |
133 | bg.rotate(-rot); | |
134 | bg.fillStyle = '#303336'; | |
135 | bg.fillText(step, 0, 0); | |
136 | 136 | |
137 | fg.restore(); | |
137 | bg.restore(); | |
138 | 138 | } |
139 | 139 | } |
140 | 140 | |
141 | 141 | // const mouse = px2hex(rotate(mousePos, rot)); |
142 | // fg.arc(...hex2px(mouse), 10, 0, 2*Math.PI); | |
142 | // bg.arc(...hex2px(mouse), 10, 0, 2*Math.PI); | |
143 | 143 | |
144 | 144 | document.body.style.setProperty('--global-rot', `${rot}rad`); |
145 | 145 | requestAnimationFrame(draw); |
0 | 0 | const panel = document.querySelector('aside.pattern'); |
1 | 1 | |
2 | const preset = panel.querySelector('.control--preset'); | |
2 | const preset = panel.querySelector('.control--preset select'); | |
3 | 3 | const patternLength = document.getElementById('pattern-len'); |
4 | 4 | const steps = panel.querySelector('.steps'); |
5 | 5 | |
6 | 6 | let length = 12; |
7 | 7 | let pattern = [0, 2, 4, 5, 7, 9, 11]; |
8 | ||
9 | const PRESETS = { | |
10 | 'major-7': [0, 2, 4, 5, 7, 9, 11], | |
11 | 'minor-7': [0, 2, 3, 5, 7, 8, 10], | |
12 | 'minor-harm-7': [0, 2, 3, 5, 7, 8, 11], | |
13 | 'minor-mel-7': [0, 2, 3, 5, 7, 9, 11], | |
14 | 'minor-hung-7': [0, 2, 3, 6, 7, 8, 11], | |
15 | 'penta': [0, 2, 4, 7, 9], | |
16 | 'major-3': [0, 4, 7], | |
17 | 'major-3+': [0, 4, 8], | |
18 | 'minor-3': [0, 3, 7], | |
19 | 'minor-3-': [0, 3, 6], | |
20 | }; | |
8 | 21 | |
9 | 22 | const update = () => { |
10 | 23 | while (steps.childElementCount > length) { |
20 | 33 | Array.from(steps.children).forEach((toggle, i) => { |
21 | 34 | toggle.checked = pattern.includes(i); |
22 | 35 | }); |
36 | ||
37 | updatePreset(); | |
38 | }; | |
39 | ||
40 | const updatePreset = () => { | |
41 | const presetName = Object.keys(PRESETS).find(k => PRESETS[k].join(',') === pattern.join(',')); | |
42 | preset.value = presetName ?? 'custom'; | |
23 | 43 | }; |
24 | 44 | |
25 | 45 | patternLength.onchange = () => { |
33 | 53 | Array.from(steps.children).forEach((toggle, i) => { |
34 | 54 | if (toggle.checked) pattern.push(i); |
35 | 55 | }); |
56 | updatePreset(); | |
36 | 57 | }; |
37 | 58 | |
38 | 59 | preset.onchange = (e) => { |
39 | const value = e.target.value; | |
40 | switch (value) { | |
41 | case 'custom': return; | |
42 | case 'major-7': | |
43 | pattern = [0, 2, 4, 5, 7, 9, 11]; | |
44 | break; | |
45 | case 'minor-7': | |
46 | pattern = [0, 2, 3, 5, 7, 8, 10]; | |
47 | break; | |
48 | case 'minor-harm-7': | |
49 | pattern = [0, 2, 3, 5, 7, 8, 11]; | |
50 | break; | |
51 | case 'minor-mel-7': | |
52 | pattern = [0, 2, 3, 5, 7, 9, 11]; | |
53 | break; | |
54 | case 'minor-hung-7': | |
55 | pattern = [0, 2, 3, 6, 7, 8, 11]; | |
56 | break; | |
57 | case 'penta': | |
58 | pattern = [0, 2, 4, 7, 9]; | |
59 | break; | |
60 | case 'major-3': | |
61 | pattern = [0, 4, 7]; | |
62 | break; | |
63 | case 'major-3+': | |
64 | pattern = [0, 4, 8]; | |
65 | break; | |
66 | case 'minor-3': | |
67 | pattern = [0, 3, 7]; | |
68 | break; | |
69 | case 'minor-3-': | |
70 | pattern = [0, 3, 6]; | |
71 | break; | |
60 | const nextPattern = PRESETS[preset.value]; | |
61 | if (nextPattern) { | |
62 | pattern = nextPattern.slice(); | |
63 | update(); | |
72 | 64 | } |
73 | update(); | |
74 | preset.value = value; | |
75 | 65 | }; |
76 | 66 | preset.value = 'major-7'; |
77 | preset.onchange({ target: preset }); | |
67 | preset.onchange(); | |
78 | 68 | |
79 | 69 | export const getLength = () => length; |
80 | 70 | export const getSteps = () => pattern; |