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(); }