-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcanvasCircleOfFifths.js
104 lines (89 loc) · 3.26 KB
/
canvasCircleOfFifths.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import {
NUM_NOTES_IN_OCTAVE,
normalize_octave,
circle_of_fifths_notes,
circle_of_fifths_text
} from './music.js'
const TEMP_circle_of_fifths_notes = [...circle_of_fifths_notes()];
const TEMP_circle_of_fifths_text = [...circle_of_fifths_text()];
export function drawCircleOfFifths(context, notes, options={
lineWidth: 0.015,
borderSize: 0.1,
fontFace: 'serif',
}) {
context = context instanceof String ? document.getElementById(canvas).getContext("2d") : context;
console.assert(context instanceof CanvasRenderingContext2D, `Unable to draw on ${context}`);
notes = notes instanceof Set ? [...notes.keys()].map(normalize_octave) : notes;
notes = notes ? notes : [];
console.assert(notes instanceof Array, 'notes must be an Array');
const _options = {
lineWidth: options.lineWidth * context.canvas.height,
borderSize: options.borderSize * context.canvas.height,
}
const radius = context.canvas.height/2 - _options.borderSize;
context.font = `${_options.borderSize}px ${options.fontFace}`;
// Background
context.setTransform(1,0,0,1,0,0); // Reset translations (why is there not a convenience call for this?)
//context.fillStyle = "#FFFFFF";
//context.fillRect(0, 0, context.canvas.width, context.canvas.height);
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.fillStyle = "#000000";
context.strokeStyle = context.fillStyle;
// Circle
context.beginPath();
context.lineWidth = _options.lineWidth
context.arc(
context.canvas.width/2,
context.canvas.height/2,
Math.min(context.canvas.width, context.canvas.height)/2 - _options.lineWidth - _options.borderSize,
(Math.PI/180)*0,
(Math.PI/180)*360,
false
);
context.stroke();
context.closePath();
// Ticks
//context.strokeStyle = "black";
//context.lineWidth = 10;
context.lineJoin = 'bevel';
context.lineCap = 'round';
function* tickGenerator(ticks=NUM_NOTES_IN_OCTAVE) {
for (let index=0 ; index < ticks ; index++) {
yield [
index,
TEMP_circle_of_fifths_notes[index],
TEMP_circle_of_fifths_text[index],
(index/ticks) * Math.PI * 2,
];
}
}
for (let [index, note, text, angle] of tickGenerator()) {
context.setTransform(1,0,0,1,0,0); // Reset translations (why is there not a convenience call for this?)
context.translate(context.canvas.width/2, context.canvas.height/2);
context.rotate(angle);
context.fillStyle = notes.lastIndexOf(note) >= 0 ? "#FF0000":"#000000";
context.strokeStyle = context.fillStyle;
context.beginPath();
context.moveTo(0, -radius);
context.lineTo(0, -radius + _options.lineWidth * 3);
context.stroke();
context.closePath();
context.fillText(text, -context.measureText(text).width/2, -context.canvas.height/2 + _options.borderSize);
}
// Chord path
context.fillStyle = '#f00'
context.beginPath();
for (let [index, note, text, angle] of tickGenerator()) {
context.setTransform(1,0,0,1,0,0); // Reset translations (why is there not a convenience call for this?)
context.translate(context.canvas.width/2, context.canvas.height/2);
context.rotate(angle);
if (notes.lastIndexOf(note) >= 0) {
context.lineTo(0, -radius);
}
}
context.closePath();
context.fill();
}
export default {
drawCircleOfFifths,
}