[code]/* Mark VonRosenstiel go get them, tiger */ import processing.serial.*; import processing.opengl.*; import controlP5.*; import processing.video.*; import s373.flob.*; Capture video; Flob flob; Serial arduino; String logFile = "/Users/ototo/Documents/_CODE/Processing/this_time_we_feel/timeLog.txt"; boolean motorDebug = false; long timeLastSendCoordinates; int updateInterval = 5000; //in milliseconds, must be same in Arduino Sketch /// video params ######################################################## int videoViewW = 640; int videoViewH = 480; int tresh             = 10; int fade              = 25; int om                = 1; int videotex          = 0; //3 String info           = ""; float fps             = 10; PFont font            = createFont("arial", 11); /// Others ############################################################## int collisions; ControlP5 cp5; ArduinoData arduinoData; Heartbeat heartbeat; WebcamProcessorThread webcamProcessorThread; WebcamData webcamData; ArrayList blobs; void setup(){  try { quicktime.QTSession.open(); }  catch (quicktime.QTException qte) { qte.printStackTrace(); }  size(videoViewW, videoViewH, OPENGL);  frameRate(5);    textFont(font);    timeLastSendCoordinates = millis();    println(Capture.list());  video = new Capture(this, 320, 240, (int)fps);    flob = new Flob(this, video, 640, 480);  flob.setOm(om);  //  flob.setMirror(true,false);  flob.setThresh(tresh);  flob.setSrcImage(videotex);  flob.settrackedBlobLifeTime(10000);      webcamData = new WebcamData(640, 480);  arduinoData = new ArduinoData(updateInterval);  heartbeat = new Heartbeat(videoViewW, webcamData, arduinoData);  webcamProcessorThread = new WebcamProcessorThread(90.0, webcamData);  webcamProcessorThread.start();    //Buton setup *********************************************************  int startY = 300;  int startX = 10; //650  cp5 = new ControlP5(this);  cp5.addTextlabel("Threshlabel").setColor(0).setText("Threshold").setPosition(startX, startY + 5);  cp5.addButton("threshMinus").setLabel(" -").setPosition(startX + 50, startY).setSize(20,20);  cp5.addButton("threshPlus").setLabel(" +").setPosition(startX + 90, startY).setSize(20,20);    cp5.addTextlabel("motorDebugLabel").setColor(0).setText("Debug").setPosition(startX + 120 , startY + 5);  cp5.addToggle("motorDebug").setPosition(startX + 160, startY).setSize(20,20).setValue(motorDebug).setLabelVisible(false);    cp5.addTextlabel("xDragLabel").setColor(0).setText("X Drag").setPosition(startX + 190, startY + 5);  cp5.addButton("xDragMinus").setLabel(" -").setPosition(startX + 260, startY).setSize(20,20);  cp5.addButton("xDragPlus").setLabel(" +").setPosition(startX + 300, startY).setSize(20,20);    cp5.addTextlabel("magVectorLabel").setColor(0) .setText("Cam Vector").setPosition(startX + 330, startY + 5);  cp5.addButton("magVectorMinus").setLabel(" -").setPosition(startX + 400, startY).setSize(20,20);  cp5.addButton("magVectorPlus").setLabel(" +").setPosition(startX + 440, startY).setSize(20,20);    cp5.addButton("xHit").setLabel("X Hit").setPosition(startX + 50, startY + 30).setSize(60,20);  cp5.addButton("yHit").setLabel("Y Hit").setPosition(startX + 120, startY + 30).setSize(60,20);    cp5.addButton("deleteLog").setLabel("Delete Log").setPosition(startX + 190, startY + 30).setSize(60,20);  cp5.addButton("toggleBox").setLabel("Toggle Draw").setPosition(startX + 260, startY + 30).setSize(60,20);    cp5.addTextlabel("yScalarLabel").setColor(0) .setText("Y Scalar").setPosition(startX + 330, startY + 30);  cp5.addButton("yScalarMinus").setLabel(" -").setPosition(startX + 400, startY + 30).setSize(20,20);  cp5.addButton("yScalarPlus").setLabel(" +").setPosition(startX + 440, startY + 30).setSize(20,20);    //Serial setups *******************************************************    println(Serial.list());  arduino = new Serial(this, Serial.list()[4], 9600);  arduino.bufferUntil('\n'); } void draw(){  //##################################################################################################  //Put some more stuff on screen in order to see what is going on ###############################################################  //##################################################################################################  rectMode(CORNER);  fill(255);  rect(0, 0, videoViewW, videoViewH);    webcamData.showCombinedImage();  String stats = "Arduino Stats ##########################################" +                 "\nCurrent Coordinates: " + arduinoData.xPrev + ", " + arduinoData.yPrev +                 "\nNext Coordinates" + arduinoData.x + ", " + arduinoData.y +                 "\nSpeeds" + arduinoData.xSpeed + ", " + arduinoData.ySpeed +                 "\n\nHeartBeat Stats ########################################" +                 "\nCurrent Coordinates: " + heartbeat.xCurrent + ", " + heartbeat.yCurrent +                 "\nCurrent Velocities: " + heartbeat.xVel + ", " + heartbeat.yVel +                 "\nDirections: xDir:" + heartbeat.xDir + " yDir: " + heartbeat.yDir +                 "\nheartbeat Box: " + heartbeat.frameTop + " and Height " + heartbeat.frameHeight +                 "\nDrift: X:" + heartbeat.drift.x + " Y: " + heartbeat.drift.y +                 "\nBounces: X:" + heartbeat.xBounces + " yFirstContact: " + heartbeat.yFirstContact +                 "\nxDrag:" + heartbeat.xDrag + " Webcam Mag: " + (webcamData.maxVectorMag) +                 "\nyScalar:" + arduinoData.yScalar;  fill(0);  text(stats,5,10);    //start ##########################################################################################  collisions = 0;  if(video.available()) {    video.read();    blobs = flob.track(  flob.binarize(video) );      }    //image(flob.getSrcImage(), 0, 0, videoViewW, videoViewH);  for(int i = 0; i < blobs.size(); i++) {        trackedBlob tb = flob.getTrackedBlob(i);        float tbxLow = tb.cx - tb.dimx;    float tbxHigh = tb.cx + tb.dimx;    float tbyLow = tb.cy - tb.dimy;    float tbyHigh = tb.cy + tb.dimy;    for(int j = 0; j < blobs.size(); j++) {      trackedBlob tbj = flob.getTrackedBlob(j);      float tbjxLow = tbj.cx - tbj.dimx;      float tbjxHigh = tbj.cx + tbj.dimx;      float tbjyLow = tbj.cy - tbj.dimy;      float tbjyHigh = tbj.cy + tbj.dimy;      if (tbxLow < tbjxHigh && tbxHigh > tbjxLow && tbyLow < tbjyHigh && tbyHigh > tbjyLow) {        collisions++;      }    }        /*    rectMode(CENTER);    String txt = "id: " + tb.id + " time: " + tb.presencetime + " ";    float velmult = 100.0f;    fill(220,220,255,50);    rect(tb.cx,tb.cy,tb.dimx,tb.dimy);    fill(0,255,0,50);    rect(tb.cx,tb.cy, 5, 5);    fill(0, 50);    line(tb.cx, tb.cy, tb.cx + tb.velx * velmult ,tb.cy + tb.vely * velmult );    fill(0, 200);    text(txt,tb.cx -tb.dimx*0.10f, tb.cy + 5f);      */        heartbeat.update(tb.cx, tb.velx, tb.cy, tb.vely, collisions);  }    if (millis() - timeLastSendCoordinates > updateInterval) {    logData("Sending off for coordinates and sending to Arduino", true);    sendCoordinates();  } } void sendCoordinates() {  timeLastSendCoordinates = millis();  arduinoData.update(heartbeat.xNext, heartbeat.yNext);  arduino.write("#" + arduinoData.xDir + "," + arduinoData.xSpeed + "," + arduinoData.yDir + "," + arduinoData.ySpeed + "\n");  logData("Sending Arduino more coordinates. Moving to: " + arduinoData.x + ", " + arduinoData.y + " From: " + arduinoData.xPrev + ", " + arduinoData.yPrev, true); } void serialEvent(Serial arduino) {  try {    String serialString = arduino.readStringUntil('\n');    serialString = trim(serialString);        if (serialString.length() > 1 && serialString.indexOf(":") >= 0) {      logData(serialString, true);      String[] idAndPayload = split(serialString, ":");      String id = idAndPayload[0];      String payload = idAndPayload[1];      if (id.equals("moving")) {        String[] data = split(payload, ","); //xDir, xSpeed, yDir, ySpeed        logData("Arduino moving with the following data: " + data[0] + ", "+ data[1] + ", "+ data[2] + ", "+ data[3], true);              }      if (id.equals("hit")) {        String[] parts = split(payload, ","); //dirHit, xDir, xSpeed, yDir, ySpeed, incompleteStepsRatio        float data[];        int index;        data = new float[parts.length];        for (index = 0; index < parts.length; ++index) {            data[index] = Float.parseFloat(parts[index].trim());        }        if (data[0] == 0) { //x was hit          logData("X Hit: ", true);          heartbeat.xHit((int)data[1]);        }        else { //y was hit          logData ("Y Hit: ", true);          heartbeat.yHit((int)data[3], (int)data[4], arduinoData.yAdjust(data[5]));        }        logData(data[1] + ", " + data[2] + ", " + data[3] + ", " + data[4] + ", " + data[5], true);      }          } //end if serialString.length() > 1*/  } //end try  catch (Exception e) {    logData("Error in serial event", true);    println(e);  } } void logData(String newData, boolean appendFile) {    println(newData);    newData = getDateTime() + " " + newData + "\n";    BufferedWriter bw=null;    try { //try to open the file    FileWriter fw = new FileWriter(logFile, appendFile); //set to false to overwrite    bw = new BufferedWriter(fw);    bw.write(newData);// + System.getProperty("line.separator"));    } catch (IOException e) {    } finally {    if (bw != null) { //if file was opened try to close        try {        bw.close();        } catch (IOException e) {}    }    } } String getDateTime() {      SimpleDateFormat df = new SimpleDateFormat("[ dd/MM/yyyy HH:mm:ss ] ");      Calendar date = Calendar.getInstance(); // the current date and time      return(df.format(date.getTime())); } public void controlEvent(ControlEvent theEvent) {  String e = theEvent.getController().getName();  if (e.equals("threshPlus")) {    tresh++;    flob.setTresh(tresh);  }  if (e.equals("threshMinus")) {    tresh--;    flob.setTresh(tresh);  }  if (e.equals("xHit")) {    heartbeat.xHit(-heartbeat.xDir);  }  if (e.equals("yHit")) {    heartbeat.yHit(-heartbeat.yDir, 650, heartbeat.yNext - 20);  }  if (e.equals("motorDebug")) {    motorDebug = !motorDebug;    logData("motor debug set to " + motorDebug, false);  }  if (e.equals("deleteLog")) {    logData("", false);  }  if (e.equals("toggleBox")) {    heartbeat.showBoxView = !heartbeat.showBoxView;  }  if (e.equals("xDragPlus")) {    heartbeat.xDrag += 0.05;  }  if (e.equals("xDragMinus")) {    heartbeat.xDrag -= 0.05;  }  if (e.equals("magVectorPlus")) {    webcamData.maxVectorMag += 5;  }  if (e.equals("magVectorMinus")) {    webcamData.maxVectorMag -= 5;  }  if (e.equals("yScalarPlus")) {    arduinoData.yScalar += 0.05;  }  if (e.equals("yScalarMinus")) {    arduinoData.yScalar -= 0.05;  } }   void keyPressed(){  if (key=='i'){      videotex = (videotex+1) % 4;    flob.setImage(videotex);  }  if(key=='f'){    fade--;    flob.setFade(fade);  }  if(key=='F'){    fade++;    flob.setFade(fade);  }    if (key==' ') {    sendCoordinates();  } }[/code] Here's ArduinoData Class [code]class ArduinoData {  int xPrev = 0;  int yPrev = 0;  int x = 0;  int y = 0;  int xSpeed = 0;  int ySpeed = 0;  int xDir = 1;  int yDir = 1;  int frameHeight = 100; //from class Heartbeat;  int drawingFrameHeight = 3200; //drawing frame height in total steps  int maxSpeed = 200;    float yScalar;    int updateInterval; //in seconds, how often does arduino get sent coordinates.  int stepsPerPixelPerInterval;    ArduinoData(int _updateInterval) {    updateInterval = _updateInterval / 1000;    yScalar = 1.1;    stepsPerPixelPerInterval = ceil(drawingFrameHeight / (updateInterval * frameHeight) * 0.5);  }  void setYScalar(float _yScalar) {    yScalar = _yScalar;  }  int yAdjust(float incompleteStepsRatio) {    y = y - round((y - yPrev) * incompleteStepsRatio);    return y;  }  void update(int xNext, int yNext) {    if (yNext - yPrev < 0) {      yNext = round(yNext * yScalar);    }    else {      yNext = round(yNext * 1/yScalar);    }        xPrev = x;    yPrev = y;    x = xNext;    y = yNext;        xSpeed = (x - xPrev) * stepsPerPixelPerInterval;    ySpeed = (y - yPrev) * stepsPerPixelPerInterval;    yDir = (ySpeed < 0 ? -1 : 1);    xSpeed = abs(xSpeed);    ySpeed = abs(ySpeed);        //safety net to keep from dividing by zero, plus BigEasy doesn't like 0 as a speed    zeroSpeedFix();        //check speeds for being over the maximum as allowed by the bigEasy driver;    if (xSpeed > ySpeed && xSpeed > maxSpeed) {      //ySpeed = round((maxSpeed * ySpeed) / xSpeed);      xSpeed = maxSpeed;      zeroSpeedFix();      adjustXY();    }    if (ySpeed > xSpeed && ySpeed > maxSpeed) {      xSpeed = round((maxSpeed * xSpeed) / ySpeed);      ySpeed = maxSpeed;      zeroSpeedFix();      adjustXY();    }        if (xSpeed < 100) xSpeed = 100;  }  void zeroSpeedFix() {    return;    /*if (xSpeed == 0) {      xSpeed = 1;    }    if (ySpeed == 0){      ySpeed = 1;    }*/  }  void adjustXY() {    x = round((xSpeed * xDir) / stepsPerPixelPerInterval) + xPrev;    y = round((ySpeed * yDir) / stepsPerPixelPerInterval) + yPrev;  } }[/code] Here's the Heartbeat class [code]class Heartbeat {  int videoViewW;    int frameHeight = 100;  int frameHeightHalf = frameHeight / 2;  int frameTop = 0;  int flobs = 0;  int yDir = 1;  int xDir = 1;  int xCurrent = 0;  int xNext = 0;  int yCurrent = frameHeightHalf;  int yNext = frameHeightHalf;  float xDrag = 0.4;  float xVel = 0;  float yVel = 0;  PVector drift;  int xBounces = 0;  boolean yFirstContact = false;  boolean showBoxView = true;    float temp; //holds temp floats    WebcamData webcamData;  ArduinoData arduinoData;    Heartbeat(int _videoViewW, WebcamData _webcamData, ArduinoData _arduinoData) {    drift = new PVector(0, 0);    videoViewW = _videoViewW;    webcamData = _webcamData;    arduinoData = _arduinoData;  }  void update(float x, float xVelFlob, float y, float yVelFlob, int collisions) {    drift = webcamData.getPixelVector((int)x, (int)y);    //drift.normalize();    //drift.mult(collisions);        xVel += (xVelFlob + drift.x) * xDrag;    yVel += (yVelFlob + drift.y);        xCurrent = xNext;    yCurrent = yNext;        xNext += abs(xVel) * arduinoData.xDir;    yNext += yVel;        if (yNext <= frameTop)  {      yNext = frameTop;      drift.y = -drift.y;      yVel = yVel;    }    if (yNext >= frameTop + frameHeight) {      yNext = frameTop + frameHeight;      drift.y = -drift.y;      yVel = -yVel;    }        //drawHeartbeat();        if (xVel < 1) {      temp = random(-drift.x, drift.x);      temp = (temp < 0 ? floor(temp) : ceil(temp));      xVel += temp;    }    else {      xVel *= xDrag;    }        if (yVel < 1) {      temp = random(-drift.y, drift.y);      temp = (temp < 0 ? floor(temp) : ceil(temp));      yVel += temp;    }        //yVel += ((frameTop + frameHeightHalf) - yNext) * 0.8; //this bounces around the center more  }  void drawHeartbeat() {    int xCurrentWrap = xCurrent;    while(xCurrentWrap < 0) {      xCurrentWrap += videoViewW;    }    xCurrentWrap = (xCurrentWrap % videoViewW) + videoViewW;        int xNextWrap = xNext;    while(xNextWrap < 0) {      xNextWrap += videoViewW;    }    xNextWrap = (xNextWrap % videoViewW) + videoViewW;          stroke(0, 200);    if (showBoxView) {      rectMode(CORNER);      fill(255);      rect(videoViewW, 0, width - videoViewW, height-200);            fill(0, 200);      ellipse(xCurrentWrap, yCurrent, 2.0, 2.0);            noFill();      rect(xCurrentWrap - 10, frameTop, 20, frameHeight);    }    else {      if (abs(xCurrentWrap - xNextWrap) < 600) {        line(xCurrentWrap, yCurrent, xNextWrap, yNext);      }    }  }  void xHit(int xDirArduino) {    xDir = xDirArduino;    arduinoData.xDir = xDirArduino;    xBounces++;        if (xBounces > 0) {      frameTop += (frameHeight * yDir);      yNext += (frameHeight * yDir);    }    if (xBounces > 2) {      //this makes sure that the machine has bounced a couple times away from the bottom or top before it starts trying to adjust the y settings      yFirstContact = false;    }  }  void yHit(int yDirArduino, int ySpeedArduino, int yBoundaryValue) {    if (yFirstContact == false) {      //we have just touched down on either the top or bottom of the machine. Offset by random amount.      xBounces = 0;      yFirstContact = true;      yDir = yDirArduino;      arduinoData.yDir = yDirArduino;      frameTop = yBoundaryValue + (yDir* frameHeight)  + (yDir * floor(random(10, frameHeightHalf))); //boundary + offsetFrame + randomOffset    }  } }[/code] Here's the WebcamData class [code]class WebcamData {  String filePrefix;  String[] urls = {    "http://cam.bangkokriverview.com/view_live1.jpg",    "http://images.webcams.travel/webcam/1280252633-Weather-Bangkok-Pom-Prap-Sattru-Phai.jpg"    //"http://webcam.ose-software.com/video.jpg"  };  String[] filenames = {    "river.jpg",    "pomPrap.jpg"    //"sukhumvitSoi71.jpg"  };  PImage[] webcamImgs = new PImage[urls.length];  PImage combinedImg;    int minWidth = 999999;  int minHeight = 999999;  int maxVectorMag = 20;  int[] vectorParts = {0, 0, 0};  PVector v;    int flobWidth;  int flobHeight;    WebcamData(int w, int h) {    flobWidth = w;    flobHeight = h;    getCamImages(0);  }    void getCamImages(int count) {     filePrefix = Integer.toString(count) + "_";     for (int i = 0; i < webcamImgs.length; i++) {       webcamImgs[i] = loadCachedImage(filePrefix, filenames[i], urls[i]);       if (webcamImgs[i].width < minWidth) minWidth = webcamImgs[i].width;       if (webcamImgs[i].height < minHeight) minHeight = webcamImgs[i].height;     }     for (int i = 0; i < webcamImgs.length; i++) {       webcamImgs[i].resize(minWidth, minHeight);     }     createCombinedImage();  }    PImage loadCachedImage(String filePrefix, String fileName, String url) {    PImage img = loadImage(filePrefix + fileName);    if (img == null) {  // Not downloaded yet      img = loadImage(url);      if (img != null) {        img.save(filePrefix + fileName); // Cache of the file      }      else      {        println("Could not load images for the prefix " + filePrefix);        img = loadImage("0_" + fileName);      }    }    return img;  }    void createCombinedImage() {    combinedImg = createImage(minWidth, minHeight, RGB);    int[] r = new int[minWidth * minHeight];    int[] g = new int[minWidth * minHeight];    int[] b = new int[minWidth * minHeight];        for (int j = 0; j < minWidth * minHeight; j++) {      r[j] = g[j] = b[j] = 0;    }            for (int i = 0; i < webcamImgs.length; i++) {      webcamImgs[i].loadPixels();      for (int j = 0; j < minWidth * minHeight; j++) {        r[j] += (webcamImgs[i].pixels[j] >> 16) & 0xFF;  // Faster way of getting red(argb)        g[j] += (webcamImgs[i].pixels[j] >> 8) & 0xFF;   // Faster way of getting green(argb)        b[j] += webcamImgs[i].pixels[j] & 0xFF;        }    }        combinedImg.loadPixels();    for (int j = 0; j < minWidth * minHeight; j++) {      r[j] = r[j] / webcamImgs.length;        g[j] = g[j] / webcamImgs.length;      b[j] = b[j] / webcamImgs.length;      stroke(r[j], g[j], b[j]);      combinedImg.pixels[j] = color(r[j], g[j], b[j]);    }        combinedImg.save(filePrefix + "combined.jpg");  }//end showCombinedImage function    void showCombinedImage() {    image(combinedImg, 320, 0);  }    PVector getPixelVector(int x, int y) {    vectorParts[0] = vectorParts[1] = vectorParts[2] = 0;    //scale from flob width/height to cam width height;    x = (x * minWidth) / flobWidth;    y = (y * minHeight) / flobHeight;        int pixel = x * y;        if (pixel > webcamImgs[0].pixels.length) {      pixel = webcamImgs[0].pixels.length - 1;    }        for (int i = 0; i < webcamImgs.length; i++) {      vectorParts[0] += (webcamImgs[i].pixels[pixel] >> 16) & 0xFF;  // Faster way of getting red(argb)      vectorParts[1] += (webcamImgs[i].pixels[pixel] >> 8) & 0xFF;   // Faster way of getting green(argb)      vectorParts[2] += webcamImgs[i].pixels[pixel] & 0xFF;       }         vectorParts[0] = round(vectorParts[0] / webcamImgs.length);     vectorParts[1] = round(vectorParts[1] / webcamImgs.length);     vectorParts[2] = round(vectorParts[2] / webcamImgs.length);         shuffle(vectorParts);         int lowBound = -255;     int highBound = 255;     if (random(1) < 0.5) {       lowBound = 255;       highBound = -255;     }         vectorParts[0] = (int)map(vectorParts[0], 0, 255, lowBound, highBound);     vectorParts[1] = (int)map(vectorParts[1], 0, 255, lowBound, highBound);     vectorParts[2] = (int)map(vectorParts[2], 0, 255, 0, maxVectorMag);         v = new PVector(vectorParts[0], vectorParts[1]);     v.normalize();     v.mult(vectorParts[2]);         return v;  }    void shuffle(int[]value){   int temp;   int pick;   for(int i=0;i