LabCode: Learn to Play / Play to Learn

Flipboard Animation
/*
 - making a recording with buttons 
*/
PFont myFont;
Disk [] Ghosts;
Connect [] Connectoid;
float [] data_x1, data_y1; // these arrays record when mousedragged is initiated and store
                           // x and y coordinates of disks respectively
int [] frame; // frames for when mousedragged is initiated 
int index; // 
int tag; // it keeps index of coordinate storage arrays
int mode; // determines which button is pressed 
// globals for keeping tab of 1st and 2nd disk selections  
int firstDiskSelection;
int secondDiskSelection;
// secondDiskSelection is always the last disk on the frame array
// we then move backwards through the array and find the one disk that 
// is not last and then flip diskTag to stop the loop
int diskTag;
// record array
int [] record;
// boolean where if record is on recordOn = 1 else it is -1
int recordOn;

Button buttonPlay, buttonRec, buttonNewDisk, buttonConnectDisks;

void setup() {
  size(450, 300);
  background(20);
  tag = 0;
  index = 0;
  frame = new int[0];
  data_x1 = new float[0];
  data_y1 = new float[0];
  buildDisks();
  mode = -1; // -1 => for normal output; 1 => for animation
  Connectoid = new Connect[1];
  Connectoid[0] = new Connect(Ghosts[0], Ghosts[1]);
  // setup for disk selection tags
  firstDiskSelection  = -1; 
  secondDiskSelection = -1;
  diskTag = 0;
  record = new int[0];
  recordOn = -1;
  // setting up font
  myFont = createFont("Verdana Bold", 20);
  textFont(myFont); 
  color grey = color(204);
  color white = color(255);
  color black = color(0);
  buttonPlay = new Button(420,20,10,grey,white,black);
  buttonRec = new Button(420,40,10,grey,white,black);
  buttonNewDisk = new Button(420,60,10,grey,white,black);
  buttonConnectDisks = new Button(420,80,10,grey,white,black);
}

void draw() {
  if (mode == 1) {
    if(record[tag] == 1) {
      Ghosts[frame[tag]].xPos = data_x1[tag];
      Ghosts[frame[tag]].yPos = data_y1[tag];
    }
    background(20);
    manageButtons();
    textOutput();
    for(int i = 0; i < Ghosts.length; i++) {
      Ghosts[i].render();
    }
    for(int i = 0; i < Connectoid.length; i++) {
      Connectoid[i].render();
    }
    if(tag == (frame.length - 1)) {
      tag = 0;
      setInitialDiskPositions();
    } else {
      tag = tag + 1;
    }   
  } 
  if (mode == -1) {
    background(20);
    manageButtons();
    textOutput();
    for(int i = 0; i < Ghosts.length; i++) {
      Ghosts[i].render();
    }
    for(int i = 0; i < Connectoid.length; i++) {
      Connectoid[i].render();
    }
  }
} // ------------------------------ end of draw routine

void setInitialDiskPositions() {
  for(int j=0; j < Ghosts.length; j++) {
    if(getDiskFirstIndex(j) > -1) {
      Ghosts[j].xPos = data_x1[getDiskFirstIndex(j)];
      Ghosts[j].yPos = data_x1[getDiskFirstIndex(j)];
    }
  }
}

int getDiskFirstIndex(int indexNum) {
  boolean found = false;
  int indexToLook = -1;
  for(int i=0; i < frame.length; i++) {
    if((!found) && (record[i] == 1) && (frame[i] == indexNum)) {
      found = true;
      indexToLook = i;
    }
  }
  return indexToLook;
}

void textOutput() {
  textSize(11);
  text("Play/Stop Animation:", 280, 30);  
  text("Start/Stop Recording:", 275, 47);
  text("New Disk:", 352, 68);
  text("Connect the disks:", 300,89);
}

void manageButtons() {
  buttonPlay.update();
  buttonPlay.display();
  buttonRec.update();
  buttonRec.display();
  buttonNewDisk.update();
  buttonNewDisk.display();
  buttonConnectDisks.update();
  buttonConnectDisks.display();
}

void buildDisks() {
  Ghosts = new Disk[2];
  for(int i = 0; i < Ghosts.length; i++) {
    Ghosts[i] = new Disk((i+1) * 100, (i+1) * 100, 5);
  }
}

void lastDisksChosen() {
  secondDiskSelection = frame[frame.length -1];
  for(int i = frame.length - 2; i >= 0; i--) {
    if((frame[i] != secondDiskSelection) && (diskTag == 0)) {
      firstDiskSelection = frame[i];
      diskTag = 1;
    }
  }
  diskTag = 0;
}

void mousePressed() {
  if(buttonPlay.press() == true) {
    mode = mode * -1;
    if(mode == 1) { recordOn = -1; } // if we are playing animation we should stop recording
  }
  if(buttonRec.press() == true) {
    recordOn = recordOn * -1;
    if(recordOn == 1) { mode = -1; } // if we are recording we should not play animation 
  }
  if(buttonNewDisk.press() == true) {
    Disk b = new Disk(200, 150, 10);
    Ghosts = (Disk[]) append(Ghosts, b);
  }
  if(buttonConnectDisks.press() == true) {
    lastDisksChosen();
    Connect c = new Connect(Ghosts[firstDiskSelection], Ghosts[secondDiskSelection]);
    Connectoid = (Connect[]) append(Connectoid, c);
  }
}

void mouseReleased() {
  buttonPlay.release();
  buttonRec.release();
  buttonNewDisk.release();
  buttonConnectDisks.release();
}

void mouseDragged() {
  float distance;
  for(int i = 0; i < Ghosts.length; i++) {
    distance = dist(Ghosts[i].xPos, Ghosts[i].yPos, mouseX, mouseY);
    if(distance <= 2*(Ghosts[i].radius)) {
      //frame = append(frame, i);
      //println(frame);
      index = i;
      Ghosts[i].xPos = mouseX;
      Ghosts[i].yPos = mouseY;
      // ----------added section
      frame = append(frame, index);
      data_x1 = append(data_x1, Ghosts[i].xPos);
      data_y1 = append(data_y1, Ghosts[i].yPos);
      if(recordOn == 1) {
        //fill(153);
        record = append(record, 1);
      } else {
        fill(20);
        text("REC", 520, 40);
        record = append(record, -1);
      }
      // ----------added section
      Ghosts[i].render();
    }
  }
}

void keyPressed() {
  if (key == 'r') { 
    if(recordOn == 1) { recordOn = -1; }
    mode = 1;
  }
  if (key == 's') { mode = -1; }
  if (key == 'c') {
    lastDisksChosen();
    Connect c = new Connect(Ghosts[firstDiskSelection], Ghosts[secondDiskSelection]);
    Connectoid = (Connect[]) append(Connectoid, c);
  }
  if (key == 'n') {
    Disk b = new Disk(15, 15, 5);
    Ghosts = (Disk[]) append(Ghosts, b);
  }
  if (key == 'u') { 
    recordOn = -1 * recordOn; 
  }
}
// ============================================================================== Disk class
class Disk {
  float radius;
  float xPos;
  float yPos;
  
  // following is the constructor
  Disk(float axPos, float ayPos, float aradius) {
    xPos = axPos;
    yPos = ayPos;
    radius = aradius;
  }
  
  // this is the part that will render (draw) the disk
  void render() {
    fill(color(204, 153, 10, 102));
    ellipse(xPos, yPos, 2 * radius, 2 * radius);
  }
}

// ============================================================================== Connect class
class Connect {
  Disk refDisk;
  Disk connectedDisk;
  
  Connect(Disk arefDisk, Disk aconnectedDisk) {
    refDisk = arefDisk;
    connectedDisk = aconnectedDisk;
  }
  
  void render() {
    //refDisk.render();
    //connectedDisk.render();
    line(refDisk.xPos, refDisk.yPos, connectedDisk.xPos, connectedDisk.yPos);
  }
}  //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ end of Connect class

// ============================================================================== button class
class Button {
  int x, y;
  int size;
  color baseGrey;
  color overGrey;
  color pressGrey;
  boolean over = false;
  boolean pressed = false;

  Button(int xp, int yp, int s, color b, color o, color p) {
    x = xp;
    y = yp;
    size = s;
    baseGrey = b;
    overGrey = o;
    pressGrey = p;
  }
  
  void update() {
    if ((mouseX >= x) && (mouseX <= x+size) &&
        (mouseY >= y) && (mouseY <= y+size)) {
      over = true;
    } else {
      over = false;
    }
  }
  
  boolean press() {
    if(over == true) {
      pressed = true;
      return true;
    } else {
      return false;
    }
  }
  
  void release() {
    pressed = false;
  }
  
  void display() {
    if(pressed == true) {
      fill(pressGrey);
    } else if(over == true) {
      fill(overGrey);
    } else {
      fill(baseGrey);
    }
    stroke(255);
    rect(x, y, size, size);
  }
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ end of button class
Lets try an electronic flipboard. Here is how to run the program:
  • Press Actual Program.
  • Move any circle by dragging it with mouse.
  • Create new circles by pressing New Disk.
  • To connect disks: Press and slightly drag first disk. Do the same for second disk. Then press Connect the disks.
  • To start or stop recording your motion at any point press Play/Stop Recording.
  • To start or stop animation press Start/Stop Animation.

Tringle Plane Animation
/*
  - whole point of this version is to help move the center of all triangles. This has been done with the
    use of frame and tag. These allow the use of draw() loop to bring out the movement of the center of all
    triangles.
*/
int numBalls = 3;
int numTriBall = 1;
int numConnectTriBall = 1;
int code = 1;
Ball[] ball = new Ball[numBalls];
TriangleBall[] triBall = new TriangleBall[numTriBall];
Connectoid[] connect = new Connectoid[numConnectTriBall];

// here is the recording section
float[] moveCenterX;
float[] moveCenterY;

// defining buttons
Button buttonZRotatePlus, buttonZRotateMinus;
Button buttonXRotatePlus, buttonXRotateMinus;
Button buttonYRotatePlus, buttonYRotateMinus;
Button buttonPlay, buttonNewTriangle;

// here are the animation parameters
int[] frame;
int tag;

void setup() { 
  size(450,300);
  background(255);
  for(int i=0; i < numBalls; i++) {
    ball[i] = new Ball(20*(i+1),20*(i*i+1),0,5,6,2);
  }
  triBall[0] = new TriangleBall(ball[0],ball[1],ball[2]);
  // add constructor declaration here....
  connect[0] = new Connectoid(triBall[0]);
  // here is move stats
  moveCenterX = new float[0];
  moveCenterY = new float[0]; 
  frame = new int[0];
  tag = 0;
  //button related setup
  color grey = color(204);
  color white = color(250);
  color black = color(0);
  buttonZRotatePlus = new Button(400,20,10,grey,white,black);
  buttonZRotateMinus = new Button(420,20,10,grey,white,black);
  buttonXRotatePlus = new Button(400,40,10,grey,white,black);
  buttonXRotateMinus = new Button(420,40,10,grey,white,black);
  buttonYRotatePlus = new Button(400,60,10,grey,white,black);
  buttonYRotateMinus = new Button(420,60,10,grey,white,black);
  buttonPlay = new Button(420,80,10,grey,white,black);
  buttonNewTriangle = new Button(420,100,10,grey,white,black); 
}

void draw() {
  //----------------- if code = -1 display the animation
  if(code == -1) {
    background(20);
    manageButtons();
    textOutput();
    //noStroke();
    fill(0);
    if(frame[tag] == 1) { connect[0].rotateX(1); }
    if(frame[tag] == 2) { connect[0].rotateY(1); }
    if(frame[tag] == 3) { connect[0].rotateZ(1); }
    if(frame[tag] == -1) { connect[0].rotateX(-1); }
    if(frame[tag] == -2) { connect[0].rotateY(-1); }
    if(frame[tag] == -3) { connect[0].rotateZ(-1); }
    if(frame[tag] == 4) {
      if(tag == getFirstLTIndex()) {
        //connect[0].moveCenter(moveCenterX[tag]-moveCenterX[getLastLTIndex()], moveCenterY[tag]-moveCenterY[getLastLTIndex()]);
        tag += 1;
      } else {
        connect[0].moveCenter(moveCenterX[tag]-moveCenterX[getPrevLTIndex(tag)], moveCenterY[tag]-moveCenterY[getPrevLTIndex(tag)]);
      } 
    } // if (frame[tag] == 4)
    for(int i=0; i < numTriBall; i++) { connect[0].otherTriBalls[i].display(); }
    connect[0].display();
    if(tag == (frame.length - 1)) { 
      tag = 0;
      if(getFirstLTIndex() > -1) {
        connect[0].moveCenter(moveCenterX[getFirstLTIndex()]-moveCenterX[getLastLTIndex()], moveCenterY[getFirstLTIndex()]-moveCenterY[getLastLTIndex()]);
      }
      // we have to reverse the order of the rotations to start the animation from the beginning.
      for(int j=frame.length-1; j > -1; j--) {
        if(frame[j] == 1) { connect[0].rotateX(-1); }
        if(frame[j] == 2) { connect[0].rotateY(-1); }
        if(frame[j] == 3) { connect[0].rotateZ(-1); }
        if(frame[j] == -1) { connect[0].rotateX(1); }
        if(frame[j] == -2) { connect[0].rotateY(1); }
        if(frame[j] == -3) { connect[0].rotateZ(1); }        
      }
    }
    else { tag = tag + 1; }
  } //end of code == -1 (this is the part where animation is being played)
    
    /*
    background(255);
    if(tag == 0) { 
      connect[0].moveCenter(moveCenterX[tag]-moveCenterX[moveCenterX.length-1],moveCenterY[tag]-moveCenterY[moveCenterY.length-1]); 
    } else {
      connect[0].moveCenter(moveCenterX[tag]-moveCenterX[tag-1], moveCenterY[tag]-moveCenterY[tag-1]); 
    }
    for(int i=0; i < numTriBall; i++) {
      connect[0].otherTriBalls[i].display();
    }
    connect[0].display();
    if(tag == (frame.length - 1)) { tag = 0; }
    else { tag = tag + 1; }
  }
  */
  //----------------- if code = 1 display the normal stuff
  else if(code == 1) {
    background(20);
    manageButtons();
    textOutput();
    //noStroke();
    fill(0);
    for(int i=0; i < numBalls; i++) { ball[i].display(); }
    for(int i=0; i < numTriBall; i++) {
      triBall[i].display();
    }
    connect[0].calculateConnectMidPoint();
    connect[0].display();
  } //end of code == 1 (this is the part where there is no replay)
} // end of draw()

void manageButtons() {
  buttonZRotatePlus.update();
  buttonZRotateMinus.update();
  buttonXRotatePlus.update();
  buttonXRotateMinus.update();
  buttonYRotatePlus.update();
  buttonYRotateMinus.update();
  
  buttonZRotatePlus.display();
  buttonZRotateMinus.display();
  buttonXRotatePlus.display();
  buttonXRotateMinus.display();
  buttonYRotatePlus.display();
  buttonYRotateMinus.display();
  
  buttonPlay.update();
  buttonPlay.display();
  buttonNewTriangle.update();
  buttonNewTriangle.display();
}

void textOutput() {
    textSize(12);
    text("+", 400, 15);
    text("-", 422, 15);
    text("Rotate in X:", 330, 30);
    text("Rotate in Y:", 330, 50);
    text("Rotate in Z:", 330, 70);
    text("Play/Stop animation:", 300, 90);
    text("New Triangle:", 335,110);
}

// This will return the index of the first LT(linear transformation) index
int getFirstLTIndex() {
  boolean found = false;
  int indexTag = -1;
  for(int i=0; i < frame.length; i++) {
    if((!found) && (frame[i]==4)) {
        found = true;
        indexTag = i;
    }
  }
  return indexTag;
}

// This will return the index of the last LT(linear transformation) index
int getLastLTIndex() {
  boolean found = false;
  int indexTag = -1;
  for(int i=frame.length-1; i > -1; i--) {
    if((!found) && (frame[i]==4)) {
      found = true;
      indexTag = i;
    }
  }
  return indexTag;
}

// Returns index of LT previous to one specified as function parameter
int getPrevLTIndex(int startIndex) {
  boolean found = false;
  int indexTag = -1;
  for(int i=startIndex-1; i > -1; i--) {
    if((!found) && (frame[i]==4)) {
      found = true;
      indexTag = i;
    }
  }
  return indexTag;
}

void mouseDragged() {
  if((mouseX > 550) && (mouseX < 560) && (mouseY > 10) && (mouseY < 20)) { code = -1; }
  if((mouseX > 550) && (mouseX < 560) && (mouseY > 30) && (mouseY < 40)) { code = 1; }
  // want to write a function here 
  for(int j=0; j < numTriBall; j++) { triBall[j].move(); }
  for(int j=0; j < numTriBall; j++) { 
    float distTriangle = dist(mouseX,mouseY,triBall[j].midBall[0].x,triBall[j].midBall[0].y);
    if(distTriangle < triBall[j].midBall[0].radius) {
      for(int i=0; i < 3; i++) {
        triBall[j].otherBalls[i].x = triBall[j].otherBalls[i].x + (mouseX - triBall[j].midBall[0].x);
        triBall[j].otherBalls[i].y = triBall[j].otherBalls[i].y + (mouseY - triBall[j].midBall[0].y); 
      }
    }
  }
  if(key =='m') {
    for(int i=0; i < numConnectTriBall; i++) {
      float distConnectedCenter = dist(mouseX,mouseY,connect[i].connectMidBall[0].x,connect[i].connectMidBall[0].y);
      if(distConnectedCenter < connect[i].connectMidBall[0].radius) {
        for(int j=0; j < numTriBall; j++) {
          for(int k=0; k < 3; k++) {
            connect[i].otherTriBalls[j].otherBalls[k].x = connect[i].otherTriBalls[j].otherBalls[k].x + (mouseX - connect[i].connectMidBall[0].x);
            connect[i].otherTriBalls[j].otherBalls[k].y = connect[i].otherTriBalls[j].otherBalls[k].y + (mouseY - connect[i].connectMidBall[0].y);
          }
        }
        frame = append(frame,4);
        moveCenterX = append(moveCenterX, mouseX);
        moveCenterY = append(moveCenterY, mouseY);
      }
    }
  } // key == m
} // end of void mouseDragged()

void mousePressed() {
  if(buttonZRotatePlus.press() == true) {
    connect[0].rotateZ(1);
    frame = append(frame,3);
    appendToMoveArray();
  }
  if(buttonZRotateMinus.press() == true) {
    connect[0].rotateZ(-1);
    frame = append(frame,-3);
    appendToMoveArray();
  }
  if(buttonXRotatePlus.press() == true) {
    connect[0].rotateX(1);
    frame = append(frame,1);
    appendToMoveArray();  
  }
  if(buttonXRotateMinus.press() == true) {
    connect[0].rotateX(-1);
    frame = append(frame,-1);
    appendToMoveArray();
  }
  if(buttonYRotatePlus.press() == true) {
    connect[0].rotateY(1);
    frame = append(frame,2);
    appendToMoveArray();    
  }
  if(buttonYRotateMinus.press() == true) {
    connect[0].rotateY(-1);
    frame = append(frame,-2);
    appendToMoveArray();
  }
  if(buttonPlay.press() == true) {
    code = code * -1;
  }
  if(buttonNewTriangle.press() == true) {
    numTriBall += 1;
    numBalls += 3;
    Ball a = new Ball(20,10,0,5,6,2);
    Ball b = new Ball(80,10,0,5,6,2);
    Ball c = new Ball(20,80,0,5,6,2);    
    ball = (Ball[]) append(ball,a);
    ball = (Ball[]) append(ball,b);
    ball = (Ball[]) append(ball,c);
 
    TriangleBall t = new TriangleBall(a,b,c);
    triBall = (TriangleBall[]) append(triBall,t);  
    connect[0].otherTriBalls = (TriangleBall[]) append(connect[0].otherTriBalls,t);
  }
}

void appendToMoveArray() {
  moveCenterX = append(moveCenterX, 0);
  moveCenterY = append(moveCenterY, 0);
}

void mouseReleased() {
  buttonZRotatePlus.release();
  buttonZRotateMinus.release();
  buttonXRotatePlus.release();
  buttonXRotateMinus.release();
  buttonYRotatePlus.release();
  buttonYRotateMinus.release();
  buttonPlay.release();
  buttonNewTriangle.release();
}

void keyPressed() {
  for(int j=0; j < numTriBall; j++){
    float distanceToCG = dist(mouseX,mouseY,triBall[j].midBall[0].x,triBall[j].midBall[0].y);
    if(distanceToCG < triBall[j].midBall[0].radius) {
      if(key == 'Z')   { triBall[j].rotateZ(1); }  // end of z + up
      if(key == 'z') { triBall[j].rotateZ(-1); } // end of Z + down
      if(key == 'X')   { triBall[j].rotateX(1); }  // end of x + up
      if(key == 'x') { triBall[j].rotateX(-1); } // end of x + down
      if(key == 'Y')   { triBall[j].rotateY(1); }  // end of y + up
      if(key == 'y') { triBall[j].rotateY(-1); } // end of y + down
    } // end of if condition with distanceToCG
  }
  
  //float finalDist = dist(mouseX,mouseY,connect[0].connectMidBall[0].x,connect[0].connectMidBall[0].y);
  //if(finalDist < connect[0].connectMidBall[0].radius) {
    if(key == 'A') { 
      connect[0].rotateZ(1);
      frame = append(frame,3);
      moveCenterX = append(moveCenterX, 0);
      moveCenterY = append(moveCenterY, 0);
    }
    if(key == 'B') { 
      connect[0].rotateX(1); 
      frame = append(frame,1);
      moveCenterX = append(moveCenterX, 0);
      moveCenterY = append(moveCenterY, 0);
    }
    if(key == 'C') { 
      connect[0].rotateY(1);
      frame = append(frame,2);
      moveCenterX = append(moveCenterX, 0);
      moveCenterY = append(moveCenterY, 0); 
    }  
    if(key == 'a') { 
      connect[0].rotateZ(-1); 
      frame = append(frame,-3);
      moveCenterX = append(moveCenterX, 0);
      moveCenterY = append(moveCenterY, 0);
    }
    if(key == 'b') {   
      connect[0].rotateX(-1);
      frame = append(frame,-1);
      moveCenterX = append(moveCenterX, 0);
      moveCenterY = append(moveCenterY, 0); 
    }
    if(key == 'c') {   
      connect[0].rotateY(-1);
      frame = append(frame,-2);
      moveCenterX = append(moveCenterX, 0);
      moveCenterY = append(moveCenterY, 0);
    }
  //}
  
  if(key == 'n') {
    numTriBall += 1;
    numBalls += 3;
    Ball a = new Ball(20,10,0,5,6,2);
    Ball b = new Ball(80,10,0,5,6,2);
    Ball c = new Ball(20,80,0,5,6,2);    
    ball = (Ball[]) append(ball,a);
    ball = (Ball[]) append(ball,b);
    ball = (Ball[]) append(ball,c);
 
    TriangleBall t = new TriangleBall(a,b,c);
    triBall = (TriangleBall[]) append(triBall,t);  
    connect[0].otherTriBalls = (TriangleBall[]) append(connect[0].otherTriBalls,t);
  }
  if(key == 'p') { code = code * -1; }
} // end of void keyPressed()

class Ball {
  float x, y, z;
  float radius, innerRadius, outerRadius;
  
  Ball(float xdir, float ydir, float zdir, float r, float or, float ir) {
    x = xdir;
    y = ydir;
    z = zdir;
    radius = r;
    outerRadius = or;
    innerRadius = ir;
  }
  
  void display() {
    float distance = dist(mouseX,mouseY,x,y);
    if(distance < radius) {
      strokeWeight(3);
      point(xP(128.),yP(128.));
    } else {
      strokeWeight(1);
      point(xP(128.),yP(128.));
    }
  }
  
  void render() {
    float distance = dist(mouseX,mouseY,x,y);
    if(distance < radius) {
      radius = outerRadius;
      fill(100);
      ellipse(xP(128.),yP(128.),2*radius,2*radius);
    } else {
      radius = innerRadius;
      noFill();
      ellipse(xP(128.),yP(128.),2*radius,2*radius);
    }
  }
  
  float xP(float eye) {
    float t = 1.0/(1.0+((float) z / eye));
    int px = int(x*t);
    return(px);
  }
  
  float yP(float eye) {
    float t = 1.0/(1.0+((float) z / eye));
    int py = int(y*t);
    return(py);
  }
}

class TriangleBall {
  Ball[] otherBalls = new Ball[3];
  Ball[] midBall = new Ball[1];
  
  TriangleBall(Ball oballA, Ball oballB, Ball oballC) {
    otherBalls[0] = oballA;
    otherBalls[1] = oballB;
    otherBalls[2] = oballC;
    calculateMidBall(); //if this is not calculated now, we will get a nullpointer error for ball overlapping
  }
  
  void calculateMidBall() {
    float xCalcValue, yCalcValue, zCalcValue;
    xCalcValue = 0;
    yCalcValue = 0;
    zCalcValue = 0;
    for(int i=0; i < (numBalls/numTriBall); i++) {
      xCalcValue += otherBalls[i].x;
      yCalcValue += otherBalls[i].y;
      yCalcValue += otherBalls[i].z;
    }
    midBall[0] = new Ball(xCalcValue/3,yCalcValue/3,zCalcValue/3,5,7,1);
  }
    
  void display() {
    fill(color(204, 153, 10, 102));
    triangle(otherBalls[0].x,otherBalls[0].y,otherBalls[1].x,otherBalls[1].y,otherBalls[2].x,otherBalls[2].y);
    calculateMidBall();
    midBall[0].render();   
  }
  
  void move() {
      for(int i=0; i < 3; i++) {
      float distance = dist(mouseX,mouseY,otherBalls[i].x,otherBalls[i].y);
      if(distance < ball[i].radius) {
        otherBalls[i].x = mouseX;
        otherBalls[i].y = mouseY;
      }
    }
  }
  
  void moveCenter(float mx, float my) {
      for(int i=0; i < 3; i++) {
        otherBalls[i].x = otherBalls[i].x + (mx - midBall[0].x);
        otherBalls[i].y = otherBalls[i].y + (my - midBall[0].y); 
      }
  }
  
  void rotateZ(int dir) {
    for(int i=0; i < 3; i++) {
          float originalXValue = otherBalls[i].x;
          float originalYValue = otherBalls[i].y;
          float previousCenterX = midBall[0].x;
          float previousCenterY = midBall[0].y;
          float deltaXValue = originalXValue - previousCenterX;
          float deltaYValue = originalYValue - previousCenterY;
          float newXValue = ((deltaXValue * cos(radians(dir*1))) + (deltaYValue * sin(radians(dir*1)))) + previousCenterX;
          float newYValue = ((-1 * deltaXValue * sin(radians(dir*1))) + (deltaYValue * cos(radians(dir*1)))) + previousCenterY;
          otherBalls[i].x = newXValue;
          otherBalls[i].y = newYValue;
    } 
  }
  
  void rotateX(int dir) {
        for(int i=0; i < 3; i++) {
          float originalYValue = otherBalls[i].y;
          float originalZValue = otherBalls[i].z;
          float previousCenterY = midBall[0].y;
          float previousCenterZ = midBall[0].z;
          float deltaYValue = originalYValue - previousCenterY;
          float deltaZValue = originalZValue - previousCenterZ;
          float newYValue = ((deltaYValue * cos(radians(dir*1))) - (deltaZValue * sin(radians(dir*1)))) + previousCenterY;
          float newZValue = ((deltaZValue * cos(radians(dir*1))) + (deltaYValue * sin(radians(dir*1)))) + previousCenterZ;
          otherBalls[i].y = newYValue;
          otherBalls[i].z = newZValue;
    }
  }
  
  void rotateY(int dir) {
        for(int i=0; i < 3; i++) {
          float originalXValue = otherBalls[i].x;
          float originalZValue = otherBalls[i].z;
          float previousCenterX = midBall[0].x;
          float previousCenterZ = midBall[0].z;
          float deltaXValue = originalXValue - previousCenterX;
          float deltaZValue = originalZValue - previousCenterZ;
          float newXValue = ((deltaXValue * cos(radians(dir*1))) - (deltaZValue * sin(radians(dir*1)))) + previousCenterX;
          float newZValue = ((deltaZValue * cos(radians(dir*1))) + (deltaXValue * sin(radians(dir*1)))) + previousCenterZ;
          otherBalls[i].x = newXValue;
          otherBalls[i].z = newZValue;
    }
  } 
}

class Connectoid {
  TriangleBall[] otherTriBalls = new TriangleBall[1];
  Ball[] connectMidBall = new Ball[1];
  
  Connectoid(TriangleBall tri) {
    otherTriBalls[0] = tri;
    calculateConnectMidPoint();
  }
  
  void calculateConnectMidPoint() {
    float xCalcValue, yCalcValue, zCalcValue;
    xCalcValue = 0;
    yCalcValue = 0;
    zCalcValue = 0;
    for(int i=0; i < numTriBall; i++) {
      xCalcValue += otherTriBalls[i].midBall[0].x;
      yCalcValue += otherTriBalls[i].midBall[0].y;
    }
    connectMidBall[0] = new Ball(xCalcValue/numTriBall,yCalcValue/numTriBall,zCalcValue/numTriBall,5,9,1);
  }
  
  void rotateZ(int dir) {
    for(int j=0; j < numTriBall; j++) {
      for(int i=0; i < 3; i++) {
        float originalXValue = otherTriBalls[j].otherBalls[i].x;
        float originalYValue = otherTriBalls[j].otherBalls[i].y;
        float previousCenterX = connectMidBall[0].x;
        float previousCenterY = connectMidBall[0].y;
        float deltaXValue = originalXValue - previousCenterX;
        float deltaYValue = originalYValue - previousCenterY;
        float newXValue = ((deltaXValue * cos(radians(dir*1))) + (deltaYValue * sin(radians(dir*1)))) + previousCenterX;
        float newYValue = ((-1 * deltaXValue * sin(radians(dir*1))) + (deltaYValue * cos(radians(dir*1)))) + previousCenterY;
        otherTriBalls[j].otherBalls[i].x = newXValue;
        otherTriBalls[j].otherBalls[i].y = newYValue;
      }
    } 
  }
  
  void rotateX(int dir) {
    for(int j=0; j < numTriBall; j++) {
      for(int i=0; i < 3; i++) {
        float originalYValue = otherTriBalls[j].otherBalls[i].y;
        float originalZValue = otherTriBalls[j].otherBalls[i].z;
        float previousCenterY = connectMidBall[0].y;
        float previousCenterZ = connectMidBall[0].z;
        float deltaYValue = originalYValue - previousCenterY;
        float deltaZValue = originalZValue - previousCenterZ;
        float newYValue = ((deltaYValue * cos(radians(dir*1))) - (deltaZValue * sin(radians(dir*1)))) + previousCenterY;
        float newZValue = ((deltaZValue * cos(radians(dir*1))) + (deltaYValue * sin(radians(dir*1)))) + previousCenterZ;
        otherTriBalls[j].otherBalls[i].y = newYValue;
        otherTriBalls[j].otherBalls[i].z = newZValue;
      }
    }
  }
  
  void rotateY(int dir) {
      for(int j=0; j < numTriBall; j++) {
        for(int i=0; i < 3; i++) {
          float originalXValue = otherTriBalls[j].otherBalls[i].x;
          float originalZValue = otherTriBalls[j].otherBalls[i].z;
          float previousCenterX = connectMidBall[0].x;
          float previousCenterZ = connectMidBall[0].z;
          float deltaXValue = originalXValue - previousCenterX;
          float deltaZValue = originalZValue - previousCenterZ;
          float newXValue = ((deltaXValue * cos(radians(dir*1))) - (deltaZValue * sin(radians(dir*1)))) + previousCenterX;
          float newZValue = ((deltaZValue * cos(radians(dir*1))) + (deltaXValue * sin(radians(dir*1)))) + previousCenterZ;
          otherTriBalls[j].otherBalls[i].x = newXValue;
          otherTriBalls[j].otherBalls[i].z = newZValue;
      }
    }
  }
  
  void display() {
    connectMidBall[0].render();
  }
  
  void moveConnectedCenter(float mx, float my) {
    float distConnectedCenter = dist(mouseX,mouseY,connectMidBall[0].x,connectMidBall[0].y);
    if(distConnectedCenter < connectMidBall[0].radius) {
      for(int i=0; i < numTriBall; i++) {
        for(int j=0; j < 3; j++) {
          otherTriBalls[i].otherBalls[j].x = otherTriBalls[i].otherBalls[j].x + (otherTriBalls[i].midBall[0].x-connectMidBall[0].x) + (mouseX - connectMidBall[0].x);
          otherTriBalls[i].otherBalls[j].y = otherTriBalls[i].otherBalls[j].y + (otherTriBalls[i].midBall[0].y-connectMidBall[0].y) + (mouseY - connectMidBall[0].y);
        }
      }
    }
  }
  
  void moveCenter(float mx, float my) {
    for(int j=0; j < numTriBall; j++) {
      for(int k=0; k < 3; k++) {
        otherTriBalls[j].otherBalls[k].x = otherTriBalls[j].otherBalls[k].x + mx;
        otherTriBalls[j].otherBalls[k].y = otherTriBalls[j].otherBalls[k].y + my;
      }
      otherTriBalls[j].calculateMidBall();
      //otherTriBalls[j].display();
    }
    calculateConnectMidPoint();
  }
} // end of class Connectoid

//----------------------------------
class Button {
  int x, y;
  int size;
  color baseGrey;
  color overGrey;
  color pressGrey;
  boolean over = false;
  boolean pressed = false;

  Button(int xp, int yp, int s, color b, color o, color p) {
    x = xp;
    y = yp;
    size = s;
    baseGrey = b;
    overGrey = o;
    pressGrey = p;
  }
  
  void update() {
    if ((mouseX >= x) && (mouseX <= x+size) &&
        (mouseY >= y) && (mouseY <= y+size)) {
      over = true;
    } else {
      over = false;
    }
  }
  
  boolean press() {
    if(over == true) {
      pressed = true;
      return true;
    } else {
      return false;
    }
  }
  
  void release() {
    pressed = false;
  }
  
  void display() {
    if(pressed == true) {
      fill(pressGrey);
    } else if(over == true) {
      fill(overGrey);
    } else {
      fill(baseGrey);
    }
    stroke(255);
    rect(x, y, size, size);
  }
}
Any structure can be made up of triangles. New triangles can be made; their shapes altered; and each rotated about the x,y or z axis! When we have more than
  • Press Actual Program.
  • To move a triangle → point to its center (this will be highlighted) → now drag it to it final position.
  • To change shape of triangle point to its vertex and drag
  • To rotate individual triangles: Point to its center press x, y or z to rotate it about +x, +y or +z axis. For reverse rotation use X, Y or Z instead.
  • New triangle: Press New Triangle
  • To move center of multiple triangles: Press m and then drag with mouse
  • To play animation: (1) First apply any rotation with six buttons on screen (2) Move center of all triangles (how to move is mentioned above) (3) To start/stop animation press (anytime) Play/Stop Animation

Rotating planes
int value = 0;       
float rotate_map = 0;
			
void setup() {		
   size(200,200,P3D);	
   frameRate(20);	
}			
			
void draw() {		
   background(255);
   if(value >= 100) {
      value = 0;
   } else {	
      value = value + 1;
   }			

   rotate_map = map(value,0,100,0,360);	
   translate(width/2,height/2);		
   rotateX(radians(rotate_map));		
   rect(-50,-50,100,100);		
   rotateY(radians(-rotate_map));	
   rect(-25,-25,50,50);	
}
Rotating planes:
  • Planes are simulated to feel as if they move in 3D.

Fortress of solitude
/*
  - point here to make sure that rectangles repel on another if in
    close proximity
*/
int numWalls = 5;
int numBalls = 1;
Wall[] wall = new Wall[numWalls];
Ball[] ball = new Ball[numBalls];

void setup() {
  frameRate(20);
  size(400,400);
  for(int i=0; i < numWalls; i++) {
    wall[i] = new Wall(random(width),random(height),i,random(20,40));
  }
  //ball(x,y,xvel,yvel,radius,Walls)
  ball[0] = new Ball(60,60,1,1,10,wall);
}


void draw() {
  background(255);
  for(int i=0; i < numWalls; i++) {
    wall[i].display();
  }
  ball[0].collide();
  ball[0].moveIt();
  ball[0].display();
}

void mouseDragged() {
  for(int i=0; i < numWalls; i++) {
    if((mouseX > wall[i].x) && (mouseX < (wall[i].x + wall[i].sideLength)) && (mouseY > wall[i].y) && (mouseY < (wall[i].y + wall[i].sideLength))) {
      wall[i].x = mouseX-((wall[i].sideLength)/2);
      wall[i].y = mouseY-((wall[i].sideLength)/2);
    }
  }
}

class Ball {
  float x;
  float y;
  float xvel;
  float yvel;
  int radius;
  Wall[] otherWalls;
  
  Ball(float xdir, float ydir, float xv, float yv, int r, Wall[] owalls) {
    x = xdir;
    y = ydir;
    xvel = xv;
    yvel = yv;
    radius = r;
    otherWalls = owalls;
  }
  
  void display() {
    ellipse(x,y,2*radius,2*radius);
  }
  
  void collide() {
    if(((x-radius)<0) || ((x+radius)>width)) {
      xvel = xvel*(-1);
    }
    if(((y-radius)<0) || (y+radius)>height) {
      yvel = yvel*(-1);
    }
    
    for(int i = 0; i < numWalls; i++) {
      float dx = (otherWalls[i].x + (otherWalls[i].sideLength)/2) - x;
      float dy = (otherWalls[i].y + (otherWalls[i].sideLength)/2) - y;
      
      
      float distance = sqrt(dx*dx + dy*dy);
      //float minDist = (otherWalls[i].sideLength);

      //float minDist = sqrt(  (otherWalls[i].x+(otherWalls[i].sideLength)/2)*(otherWalls[i].x+(otherWalls[i].sideLength)/2)     + (otherWalls[i].y+(otherWalls[i].sideLength)/2)*(otherWalls[i].y+(otherWalls[i].sideLength)/2)          );
      float minDist = (otherWalls[i].sideLength)/sqrt(2) + radius;
      
      if(distance < minDist) {
        float angle = atan2(dy, dx);
        float targetX = cos(angle)*1;
        float targetY = sin(angle) * 1;
        float ax = (targetX - otherWalls[i].x);
        float ay = (targetX - otherWalls[i].y);
        xvel -= targetX;
        yvel -= targetY;    
      }
    }
  }

  void moveIt() {
    x = x + xvel;
    y = y + yvel;
  }  
}
  
class Wall {
  float x;
  float y;
  int id;
  float sideLength;
  
  Wall(float xPos, float yPos, int idin, float side) {
    x = xPos;
    y = yPos;
    id = idin;
    sideLength = side;
  }
  
  void display() {
    rect(x,y,sideLength,sideLength);
  }
}
Barriers can also be simulated in any shape:
  • Move any box and ball will bounce off of it.
  • Everytime browser is refreshed boxes are randomly placed.

Fortress of solitude
int numBalls = 3;
Ball[] ball = new Ball[numBalls];

void setup() {
  size(600,600);
  for(int i=0; i < numBalls; i++) {
    ball[i] = new Ball(50*(i+1),50*(i*i+1),5,6,4);
  }
}

void draw() {
  background(255);
  for(int i=0; i < numBalls; i++) {
    for(int j=0; j < numBalls-1; j++) {
      if(i == j) {j=j+1;}
       float distItoJ = dist(ball[i].x,ball[i].y,ball[j].x,ball[j].y);
       float sumOfRadii = ball[i].radius + ball[j].radius;
       if(distItoJ < sumOfRadii) {
         ball[j].x += sumOfRadii;
         ball[j].y += sumOfRadii;
       }
      ball[i].display();
    }
  }

}

void keyPressed() {
  if (key == 'f') {
    saveFrame("balls-######.jpg");
  }
}

void mouseDragged() {
  for(int i=0; i < numBalls; i++) {
    float distance = dist(mouseX,mouseY,ball[i].x,ball[i].y);
    if(distance < ball[i].radius) {
     ball[i].x = mouseX;
     ball[i].y = mouseY;
    }
  }
}

class Ball {
  float x;
  float y;
  float radius;
  float outerRadius;
  float innerRadius;
  
  Ball(float xdir, float ydir, float r, float or, float ir) {
    x = xdir;
    y = ydir;
    radius = r;
    outerRadius = or;
    innerRadius = ir;
  }
  
  // not being used currently
  void updateRadius() {
      float distance = dist(mouseX,mouseY,x,y);
      if(distance < radius) {
        radius = outerRadius;
      } else {
        radius = innerRadius;
      }
  }
  
  void display() {
    float distance = dist(mouseX,mouseY,x,y);
    if(distance < radius) {
        radius = outerRadius;
        fill(100);
        ellipse(x,y,2*radius,2*radius);  
    } else {
        radius = innerRadius;
        //noStroke();
        noFill();
        ellipse(x,y,2*radius,2*radius);
      }
  }
}
Absolute repulsion
  • Move any circle to any of its fellow and they will repell
  • If developing many particle systems, this code could be useful to simulate this property

Reference check: