> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.nGqNBV1H45v/rev.6288
 * 
 * authors: 
 *   Glen Oakley

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



//CURRENTLY CONFIGURED FOR SOROBAN
public static final int HEAVENS = 1;
public static final int EARTHS = 4;
public static final int HEAVEN_VALUE = EARTHS+1;
public static final int EARTH_VALUE = 1;
public static final int ROWS = 3;
public static final int BEAD_SPACE = 2;

public static final int BEAD_HEIGHT = 16;
public static final int BEAD_WIDTH = 32;
public static final int BAR_THICKNESS = 8;


Bead[][] abacusArray;
int unitRow;


void setup() {
  size(BAR_THICKNESS + ROWS*BEAD_WIDTH + (ROWS+1)*(BEAD_WIDTH/2) + BAR_THICKNESS, 
       BAR_THICKNESS + (EARTHS+BEAD_SPACE)*BEAD_HEIGHT + BAR_THICKNESS + (HEAVENS+BEAD_SPACE)*BEAD_HEIGHT + BAR_THICKNESS);
  
  abacusArray = new Bead[HEAVENS+EARTHS][ROWS];
  for (int x = 0; x < HEAVENS+EARTHS; x++) {
    for (int y = 0; y < ROWS; y++) {
      boolean isHeaven = (x < HEAVENS);
      int posX = rowToPos(y);
      int padding = (isHeaven ? 
                     BAR_THICKNESS :  
                     BAR_THICKNESS*2+(HEAVENS+BEAD_SPACE)*BEAD_HEIGHT);
      int posYStart = padding+(((isHeaven ? x : x-HEAVENS)))*BEAD_HEIGHT;
      int posYEnd = 
        padding+((((isHeaven ? x : x-HEAVENS)))+BEAD_SPACE)*BEAD_HEIGHT;
      abacusArray[x][y] = new Bead(posX-BEAD_WIDTH/2, posYStart, posYEnd, 
                                   isHeaven);
    }
  }
  unitRow = (int)(ROWS/2);
}

void draw() {
  background(255);
  fill(0);
  stroke(0);
  // draw the frame
  rect(0, 0, BAR_THICKNESS, height);
  rect(width-BAR_THICKNESS, 0, BAR_THICKNESS, height);
  rect(0, 0, width, BAR_THICKNESS);
  rect(0, height-BAR_THICKNESS, width, BAR_THICKNESS);
  rect(0, BAR_THICKNESS + (HEAVENS+BEAD_SPACE)*BEAD_HEIGHT, width, BAR_THICKNESS);
  // draw the rods
  strokeWeight(BEAD_WIDTH/8);
  for (int i = 0; i < ROWS; i++) {
    int posX = rowToPos(i);
    line(posX, 0, posX, height);
  }
  // draw the unit rod marker
  stroke(255);
  strokeWeight(BAR_THICKNESS/2);
  int unitX = rowToPos(unitRow);
  int unitY = BAR_THICKNESS*(3/2)+(HEAVENS+BEAD_SPACE)*BEAD_HEIGHT;
  line(unitX, unitY, unitX, unitY);
  // draw the beads
  stroke(0);
  ellipseMode(CORNER);
  strokeWeight(1);
  for (int x = 0; x < abacusArray.length; x++) {
    for (int y = 0; y < abacusArray[x].length; y++) {
      PVector p = abacusArray[x][y].getPos();
      ellipse(p.x, p.y, BEAD_WIDTH, BEAD_HEIGHT);
    }
  }
}

public void mouseDragged() {
  if (mouseButton == LEFT) {
    // [try to] move (a) bead(s)
    parseMovement();
    
  }
}
public void mousePressed() {
  if (mouseButton == RIGHT) {
    // set the unit rod for the abacus
    int rowNum = posToRow(mouseX);
    if (rowNum >= 0)
      unitRow = rowNum;
  }
}

// will return a negative if the row is out of range
public int posToRow(int xPos) {
  double rowWidth = (width-BAR_THICKNESS*2)/ROWS;
  int rowNumber = (int)((xPos-BAR_THICKNESS)/rowWidth);
  if (xPos <= BAR_THICKNESS || xPos >= width-BAR_THICKNESS)
    rowNumber = -1;
  return rowNumber;
}
public int rowToPos(int row) {
  int xPos = (BEAD_WIDTH/4 + BAR_THICKNESS+(3*BEAD_WIDTH)/4+row*((3*BEAD_WIDTH)/2));
  return xPos;
}

public long getNumber() {
  long number = 0;
  for (int y = 0; y < abacusArray[0].length; y++) {
    localNumber = 0;
    for (int a = 0; a < HEAVENS; a++) {
      localNumber += (abacusArray[a][y].isReconed() ? 1 : 0)*HEAVEN_VALUE;
    }
    for (int b = HEAVENS; b < HEAVENS+EARTHS; b++) {
      localNumber += (abacusArray[b][y].isReconed() ? 1 : 0)*EARTH_VALUE;
    }
    number += localNumber * pow(10, unitRod-y);
  }
  return number;
}



private void parseMovement() {
    double lineSlope = (mouseY-pmouseY)/(mouseX-pmouseX);
    int yInt = mouseY-lineSlope*mouseX;
    if (pmouseY == mouseY) return;
    
    if (pmouseX < mouseX) {
        int startRow = posToRow(pmouseX);
        int endRow = posToRow(mouseX);
        for (int i = startRow; i < endRow; i++) {
            int startX = rowToPos(i) - 
                (i==0 ? BAR_THICKNESS : rowToPos(i-1));
            if (pmouseX > startX) startX = pmouseX;
            int endX = -rowToPos(i) + 
                (i==(ROWS-1) ? width-BAR_THICKNESS : rowToPos(i+1));
            if (mouseX < endX) endX = mouseX;
            endX = (mouseX < endX ? mouseX : endX);
            if (int(lineSlope*startX+yInt) > int(lineSlope*endX+yInt)) {
            } else {
            }
        }
    }
    println("ASDF");
}






public class Bead {
  // the x, upper, and lower positions do not change
  private int xPos, upperPos, lowerPos, currentPos;
  //whether or not the bead is in the reconed position
  boolean reconed;
  boolean heaven;
  public Bead(int xPosition, int uPos, int lPos, boolean heaven) {
    xPos = xPosition;
    upperPos = uPos;
    lowerPos = lPos;
    currentPos = (heaven ? uPos : lPos);
    reconed = false;
    this.heaven = heaven;
  }
  public PVector getPos() { return new PVector(xPos, currentPos); }
  public void setPos(int y) {
    currentPos = y;
    if (currentPos < lowerPos) currentPos = lowerPos;
    else if (currentPos > upperPos) currentPos = upperPos;
    if ((upperPos-currentPos) > ((upperPos-lowerPos)/2)) {
      if (heaven) reconed = false;
      else reconed = true;
    }
    else {
      if (heaven) reconed = true;
      else reconed = false;
    }
  }
  public boolean isReconed() { return reconed; }
  public boolean isHeaven() { return isHeaven; }
}