diff --git a/circleOfFifths.js b/circleOfFifths.js new file mode 100644 index 0000000..ea6f08a --- /dev/null +++ b/circleOfFifths.js @@ -0,0 +1,261 @@ +let outRatio = 0.8; +let outC1; +let outC2; +let outCH; +let outCT; +let outCSP; // 分隔 +let outText = ["沒有升降", "1 個 #", "2 個 #", "3 個 #", "4 個 #", "5 個 # / 7 個 b", "6 個 # / 6 個 b", "7 個 # / 5 個 b", "4 個 b", "3 個 b", "2 個 b", "1 個 b"] +let outTextSize = 15; + +let majorRatio = 0.74; +let majorC1; +let majorC2; +let majorCT; +let majorText = ["C", "G", "D", "A", "E", "B/Cb", "F#/Gb", "C#/Db", "Ab", "Eb", "Bb", "F"] +let majorTextSize = 22; + +let minorRatio = 0.65; +let minorC1; +let minorC2; +let minorCT; +let minorText = ["Am", "Em", "Bm", "F#m", "C#m", "G#m/Abm", "D#m/Ebm", "A#m/Bbm", "Fm", "Cm", "Gm", "Dm"] +let minorTextSize = 17; + +let detailText1 = [ + "C 大調 / A 小調", + "G 大調 / E 小調", + "D 大調 / B 小調", + "A 大調 / F# 小調", + "E 大調 / C# 小調", + "B 或 Cb 大調 / G# 或 Ab 小調", + "F# 或 Gb 大調 / D# 或 Eb 小調", + "C# 或 Db 大調 / A# 或 Bb 小調", + "Ab 大調 / F 小調", + "Eb 大調 / C 小調", + "Bb 大調 / G 小調", + "F 大調 / D 小調" +] + +let coreRatio = 0.57; +let coreType = "Detail"; + +let dbText = "This is the default debug text." + +let mouseDir; + +let acc = 0; +let vel = 0; +let angle = -3.5; // 1 = 1/12 TAU, global angle + +let locked = true; + +function setup() { + createCanvas(800, 800); + ellipseMode(CENTER); + colorMode(HSB, 100); + //colors + outC1 = color(60, 30, 70); + outC2 = color(60, 30, 80); + outCH = color(60, 30, 100); + outCT = color(60, 0, 95); + outCSP = color(60, 20, 95); + majorC1 = color(80, 30, 80); + majorC2 = color(80, 30, 70); + majorCT = color(80, 30, 20); + minorC1 = color(80, 30, 75); + minorC2 = color(80, 30, 65); + minorCT = color(80, 10, 90); + + lockButton = createButton('[Locked]'); + lockButton.position(19, 19); + lockButton.mousePressed(toggleLocked); + +} + +function toggleLocked() { + if (locked) { + lockButton.html("Lock"); + locked = false; + } else { + lockButton.html("[Locked]"); + locked = true; + } + +} + +function highlighted() { + let x = mouseDir - angle; + while (x < 0) { + x += 12; + } + x = ((x * 10000) % 120000) / 10000; + + return floor(x); +} + +function drawOuter(ang) { + stroke(outCSP); + for (i = 0; i < 12; i++) { + fill((i % 2 == 0) ? outC1 : outC2); + + if (i == highlighted()) { + fill(outCH); + + } + let j = i + ang; + arc(0, 0, width * outRatio, height * outRatio, TAU * (j / 12), TAU * ((j + 1) / 12) - 0.000001, PIE); + } + stroke(20); + noFill(); + ellipse(0, 0, width * outRatio); +} + +function drawOuterText(ang) { + for (i = 0; i < 12; i++) { + let j = i + ang + 3.5; // 把第一個字畫到右方 + push(); + rotate(TAU * (j / 12)); + textAlign(CENTER); + textSize(outTextSize); + noStroke(); + fill(outCT); + text(outText[i], 0, -(height * outRatio / 2 * 0.945)); + pop(); + } + +} + +function drawMajor(ang) { + stroke(90); + for (i = 0; i < 12; i++) { + fill((i % 2 == 0) ? majorC1 : majorC2); + let j = i + ang; + arc(0, 0, width * majorRatio, height * majorRatio, TAU * (j / 12), TAU * ((j + 1) / 12) - 0.000001); + } +} + +function drawMajorText(ang) { + for (i = 0; i < 12; i++) { + let j = i + ang + 3.5; + push(); + rotate(TAU * (j / 12)); + textAlign(CENTER); + textSize(majorTextSize); + noStroke(); + fill(majorCT); + text(majorText[i], 0, -(height * majorRatio / 2 * 0.91)); + pop(); + } + +} + +function drawMinor(ang) { + noStroke(); + for (i = 0; i < 12; i++) { + fill((i % 2 == 0) ? minorC1 : minorC2); + let j = i + ang; + arc(0, 0, width * minorRatio, height * minorRatio, TAU * (j / 12), TAU * ((j + 1) / 12) - 0.000001); + } +} + +function drawMinorText(ang) { + for (i = 0; i < 12; i++) { + let j = i + ang + 3.5; + push(); + rotate(TAU * (j / 12)); + textAlign(CENTER); + textSize(minorTextSize); + noStroke(); + fill(minorCT); + text(minorText[i], 0, -(height * minorRatio / 2 * 0.91)); + pop(); + } +} + +function drawCore(ang) { + switch (coreType) { + case "Empty": + drawCoreEmpty(ang); + break; + case "Black": + drawCoreBlack(ang); + break; + case "Detail": + drawCoreDetail(ang); + break; + default: + drawCoreEmpty(ang); + } +} + +function drawCoreDetail(ang) { + fill(100); + stroke(40); + ellipse(0, 0, width * coreRatio); + fill(20); + noStroke(); + textAlign(CENTER); + textSize(22); + text(detailText1[highlighted()], 0, height * 0.1); +} + + +function drawCoreEmpty(ang) { + fill(100); + stroke(40); + ellipse(0, 0, width * coreRatio); +} + +function drawCoreBlack(ang) { + fill(0); + stroke(40); + ellipse(0, 0, width * coreRatio); +} + +function debugText() { + fill(0); + textAlign(CENTER); + text(dbText, 0, height * 0.45); +} + +function mouseAngle(ang) { + let v = createVector(mouseX - width / 2, mouseY - height / 2); + let h = v.heading(); // -PI ~ PI + let i = map(h, -PI, PI, 6, 18); + // let j = (i - ang); + mouseDir = (i > 12) ? i - 12 : i; + // dbText = `${h}, ${i}, ${mouseDir}`; + +} + +function rotateGlobal() { + acc = (mouseX - width / 2) / width; + dbText = acc; + if (abs(acc) > 0.4) { + vel += acc / 70; + vel = constrain(vel, -0.1, 0.1); + } + + if (locked) { + vel = 0; // friction + } else { + vel *= 0.9 + } + angle += vel; +} + +function draw() { + background(95); + translate(width / 2, height / 2); + mouseAngle(angle); + rotateGlobal() + highlighted(); + drawOuter(angle); + drawOuterText(angle); + drawMajor(angle); + drawMajorText(angle); + drawMinor(angle); + drawMinorText(angle); + drawCore(angle); + // debugText(); +}