> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.JNgrI8QAqNh/rev.1667
 * 
 * authors: 
 *   Paul D

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



// This sketch builds on a prior work, "Clocks", created by James Aspnes
// http://studio.sketchpad.cc/sp/pad/view/ro.9XwMmz-oB-t42/rev.3665

// this is all just a test

String chord_filename = "/static/uploaded_resources/p.3839/chords.txt";
LinearMotion clock; // clock value in seconds since midnight
PVector origin;
String current_key = "";
float radius = 100;
int step = 0;
int cur_offset = 0;
int cur_chord = 0;
String[] keys = {"C","G","D","A","E","B","F#","C#","Ab","Eb","Bb","F"};
Chord[] chords;
int index = 0;
String[] lines;

void setup() {
  //noLoop();
  size(int(4*radius),int(3*radius));
  //size(600,600);
  smooth();
  frameRate(60);
  // seconds since start of day
  int t = hour() * 3600 + minute() * 60 + second();
  clock = new LinearMotion(t, 1);

  lines = loadStrings(chord_filename);
  chords = new Chord[lines.length];
  while (index < lines.length) {
    String[] pieces = split(lines[index], '\t');
    String[] values = split(pieces[1], ' ');
    String chord_name = pieces[0];
    int[] chord_values = new int[values.length];
    //int[] chord_values[values.length];
    int i = 0;
    
    for (i = 0; i < values.length; i++) {
      chord_values[i] = int(values[i]);
    }
    Chord c = new Chord(chord_values, chord_name, chord_values.length, 0);
    chords[index] = c;
    //c.drawchord(origin, radius);
    index = index + 1;
  }
}

void draw() {
  background(255);
  stroke(0);
  fill(0);
  drawClock(50+radius, 50+radius, radius, clock.get());
  int i;
  text("use the arrow keys to seleys to select a chord",0,10);
  for (i = 0; i < chords.length; i++) {
    Chord c = chords[i];
    text(c.chordname, radius*3, i*20+30);
  }
  

  if (cur_chord==0) {
    cur_chord = chords.length;
  }
  if (cur_offset ==0) {
    cur_offset = 12;
  }
  
  Chord c = chords[cur_chord%chords.length];
  c.keyoffset = cur_offset;
  //c.keyoffset = (cur_chord/chords.length)%12;
  c.drawchord(origin, radius);
  c.printchord(origin);
  fill(200);
  text(c.chordname, radius*3, (cur_chord%9)*20+30);
  
}

void keyPressed(){
  if(key == CODED) { 
    if (keyCode == UP) { 
      cur_chord--;
    } 
    else if (keyCode == DOWN) { 
      cur_chord++;
    } 
    if (keyCode == LEFT) { 
      cur_offset--;
    } 
    else if (keyCode == RIGHT) { 
      cur_offset++;
    } 
  }
}
// convert radial to rectangular coordinates
// (x,y) is origin
// r is radius as fraction of min(width, height)/2
// TWO_PI*frac is angle clockwise from up
PVector toRect(PVector o, float r, float frac) {
  float theta = TWO_PI * frac;
  return new PVector(o.x + r * sin(theta), o.y - r * cos(theta));
}

// like line() but takes two PVectors as arguments
void vLine(PVector s, PVector e)
{
  line(s.x, s.y, e.x, e.y);
}

// draw a clock centered at (x,y) with radius r
// t specifies seconds since midnight
void drawClock(float x, float y, float r, float t) {
  // don't muck up external styles
  pushStyle();

  origin = new PVector(x, y);

  // clock face
  pushStyle();
  noFill();
  strokeWeight(1);
  ellipse(origin.x, origin.y, 2*r, 2*r);
  popStyle();

  // pips
  for(int i = 1; i < 13; i++) {
    PVector place = toRect(origin, r*1.2, i / 12.0);
    PVector dot_place = toRect(origin, r, i / 12.0);
    textAlign(CENTER, CENTER);
    textSize(0.2*r);
    ellipse(dot_place.x, dot_place.y, r/8, r/8);
    text(keys[i%12], place.x, place.y);
  }
  popStyle();
}

class Chord {
  int chordcolor;
  int[] chordnotes;
  int num_notes;
  int keyoffset;
  String chordname;
  
  Chord(int[] notes, String name, int n, int offset) {
    chordcolor = offset*255/12;
    chordnotes= notes;
    num_notes = n;
    keyoffset = offset;
    chordname = name;
  }

  void printchord(PVector place) {
    String s = "" + keys[keyoffset%12] + " " + chordname;
    text(s, origin.x-(s.length()*5), origin.y);
    PVector key_place = toRect(origin, radius, keyoffset / 12.0);
    //text(keys[keyoffset%12], key_place.x, key_place.y);
    fill(200);
    ellipse(key_place.x, key_place.y, radius/8, radius/8);
  }
  
  void drawchord(PVector orig, float r) {
    pushStyle();
    strokeWeight(4);
    //stroke(50,100,200);
    stroke(chordcolor, chordcolor, chordcolor);
    for(int i = 0; i < num_notes; i++) {
        PVector place = toRect(orig, r, (chordnotes[i%num_notes]+keyoffset)%12 / 12.0);
        PVector next_place = toRect(orig, r, (chordnotes[(i+1)%num_notes]+keyoffset)%12 / 12.0);
        vLine(place, next_place);
    }
    popStyle();
  }
}

// base class for time-dependent variables
class Motion {
  float x; // position
  int t; // last time x was updated as returned by millis()

  Motion(float x0) {
    set(x0);
  }

  // update and return x
  float get() {
    return x;
  }

  // set new position
  void set(float x0) {
    x = x0;
    t = millis();
  }
}

class LinearMotion extends Motion {
  float rate; // velocity in units / second

  LinearMotion(float x0, float r) {
    super(x0);
    rate = r;
  }

  float get() {
    // note we don't change x to avoid accumulating error
    return x + rate*(millis() - t)/1000.0;
  }

  void setRate(float r) {
    // fix where we were
    set(get());
    // then update rate
    rate = r;
  }
}