Commits

Thejesh GN committed 8fbff6d

initial code example from Ben Fry book

Comments (0)

Files changed (4)

project/ipab4/Integrator.java

+// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
+
+
+public class Integrator {
+
+  static final float DAMPING = 0.5f;  // formerly 0.9f
+  static final float ATTRACTION = 0.2f;  // formerly 0.1f
+
+  public float value   = 0;
+  public float vel     = 0;
+  public float accel   = 0;
+  public float force   = 0;
+  public float mass    = 1;
+
+  public float damping;
+  public float attraction;
+  public boolean targeting;
+  public float target;
+
+  public float prev = Float.MAX_VALUE;
+  public float epsilon = 0.0001f;
+
+
+  public Integrator() {
+    this(0, DAMPING, ATTRACTION);
+  }
+
+
+  public Integrator(float value) {
+    this(value, DAMPING, ATTRACTION);
+  }
+
+
+  public Integrator(float value, float damping, float attraction) {
+    this.value = value;
+    this.damping = damping;
+    this.attraction = attraction;
+  }
+
+
+  public void set(float v) {
+    value = v;
+  }
+
+
+  /**
+   * Update for next time step.
+   * Returns true if actually updated, false if no longer changing.
+   */
+  public boolean update() {
+    if (targeting) {
+      force += attraction * (target - value);
+    }
+
+    accel = force / mass;
+    vel = (vel + accel) * damping;
+    value += vel;
+
+    force = 0;
+
+    if (Math.abs(value - prev) < epsilon) {
+      value = target;
+      return false;
+    }
+    prev = value;
+    return true;
+  }
+
+
+  public void target(float t) {
+    targeting = true;
+    target = t;
+  }
+
+
+  public void noTarget() {
+    targeting = false;
+  }
+}

project/ipab4/RankedList.java

+// Code from Visualizing Data, First Edition, Copyright 2008 Ben Fry.
+
+
+import processing.core.PApplet;
+
+
+public class RankedList {
+  // Number of elements in the list
+  protected int count; 
+  // Array of values for the list
+  protected float[] value;
+  // Minimum and maximum values in the list
+  protected float minValue, maxValue;
+  // How this value is represented visually
+  protected String[] title;
+  // Rank for each item (0 is highest) 
+  protected int[] rank;
+  // Ordering used while sorting by rank
+  protected int[] order;
+  // True if the element 0 is the lowest value, and count-1 the largest.
+  // (This has no bearing on what is considered the minValue and maxValue.)
+  protected boolean ascending;
+
+
+  RankedList(int count, boolean ascending) {
+    this.count = count;
+    this.ascending = ascending;
+
+    value = new float[count];
+    title = new String[count];
+    rank = new int[count];
+  }
+
+
+  public int getCount() {
+    return count;
+  }
+
+
+  public float getValue(int index) {
+    return value[index];
+  }
+  
+  
+  public float getMinValue() {
+    return minValue;
+  }
+  
+  
+  public float getMaxValue() {
+    return maxValue;
+  }
+
+
+  public String getTitle(int index) {
+    return title[index];
+  }
+
+
+  public int getRank(int index) {
+    return rank[index];
+  }
+
+
+  // Sort the data and calculate min/max values
+  void update() {
+    // Set up an initial order to be sorted
+    order = new int[count];
+    for (int i = 0; i < count; i++) {
+      order[i] = i;
+    }
+    sort(0, count-1);
+
+    // Assign rankings based on the order after sorting
+    for (int i = 0; i < count; i++) {
+      rank[order[i]] = i;
+    }
+
+    // Calculate minimum and maximum values
+    minValue = PApplet.min(value);
+    maxValue = PApplet.max(value);
+  }
+
+
+  void sort(int left, int right) {
+    int pivotIndex = (left+right)/2;
+    swap(pivotIndex, right);
+    int k = partition(left-1, right);
+    swap(k, right);
+    if ((k-left) > 1) sort(left, k-1);
+    if ((right-k) > 1) sort(k+1, right);
+  }
+
+
+  int partition(int left, int right) {
+    int pivot = right;
+    do {
+      while (compare(++left, pivot) < 0) ;
+      while ((right != 0) && (compare(--right, pivot) > 0)) ;
+      swap(left, right);
+    } while (left < right);
+    swap(left, right);
+    return left;
+  }
+
+
+  float compare(int a, int b) {
+    if (ascending) {
+      return value[order[a]] - value[order[b]];
+    } else {
+      return value[order[b]] - value[order[a]];
+    }
+  }
+
+
+  void swap(int a, int b) {
+    int temp = order[a];
+    order[a] = order[b];
+    order[b] = temp;
+  }
+}

project/ipab4/data/cities-dept.tsv

+nyy	NY Yankees
+bos	Boston
+nym	New York Mets
+ana	LA Angels
+cws	Chi White Sox
+la	LA Dodgers
+sea	Seattle
+chc	Chi Cubs
+det	Detroit
+bal	Baltimore
+stl	St. Louis
+sf	San Francisco
+phi	Philadelphia
+hou	Houston
+atl	Atlanta
+tor	Toronto
+oak	Oakland
+min	Minnesota
+mil	Milwaukee
+cin	Cincinnati
+tex	Texas
+kc	Kansas City
+cle	Cleveland
+sd	San Diego
+col	Colorado
+ari	Arizona
+pit	Pittsburgh
+was	Washington
+fla	Florida
+tb	Tampa Bay

project/ipab4/ipab4.pde

+/*
+This book is here to help you get your job done. In general, you may use the 
+code in this book in your programs and documentation. You do not need to contact 
+us for permission unless you�re reproducing a significant portion of the code. 
+For example, writing a program that uses several chunks of code from this book 
+does not require permission. Selling or distributing a CD-ROM of examples from 
+O�Reilly books does require permission. Answering a question by citing this book 
+and quoting example code does not require permission. Incorporating a significant
+amount of example code from this book into your product�s documentation does 
+require permission.
+
+We appreciate, but do not require, attribution. An attribution usually includes
+the title, author, publisher, and ISBN. For example: �Visualizing Data, First 
+Edition by Ben Fry. Copyright 2008 Ben Fry, 9780596514556.�
+
+If you feel your use of code examples falls outside fair use or the permission
+given above, feel free to contact us at permissions@oreilly.com.
+*/
+import java.util.regex.*;
+
+int teamCount = 30;
+String[] teamNames; 
+String[] teamCodes;
+HashMap teamIndices;
+  
+static final int ROW_HEIGHT = 23;
+static final float HALF_ROW_HEIGHT = ROW_HEIGHT / 2.0f;
+
+static final int SIDE_PADDING = 30;
+static final int TOP_PADDING = 40;
+
+SalaryList salaries;
+StandingsList standings;
+  
+StandingsList[] season;
+Integrator[] standingsPosition;
+
+PImage[] logos;
+float logoWidth;
+float logoHeight;
+
+PFont font;
+
+  
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+  
+
+String firstDateStamp = "20070401";
+String lastDateStamp = "20070930";
+String todayDateStamp;
+
+static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
+
+// The number of days in the entire season. 
+int dateCount;
+// The current date being shown.
+int dateIndex;
+// Don't show the first 10 days, they're too erratic. 
+int minDateIndex = 10;  
+// The last day of the season, or yesterday, if the season is ongoing.
+// This is the maximum date that can be viewed.
+int maxDateIndex;
+
+// This format makes "20070704" from the date July 4, 2007.
+DateFormat stampFormat = new SimpleDateFormat("yyyyMMdd");
+// This format makes "4 July 2007" from the same.
+DateFormat prettyFormat = new SimpleDateFormat("d MMMM yyyy");
+
+// All dates for the season formatted with stampFormat.
+String[] dateStamp;
+// All dates in the season formatted with prettyFormat.
+String[] datePretty;
+
+void setupDates() {
+  try {
+    Date firstDate = stampFormat.parse(firstDateStamp);
+    long firstDateMillis = firstDate.getTime();
+    Date lastDate = stampFormat.parse(lastDateStamp);
+    long lastDateMillis = lastDate.getTime();
+
+    // Calculate number of days by dividing the total milliseconds 
+    // between the first and last dates by the number of milliseconds per day
+    dateCount = (int) 
+      ((lastDateMillis - firstDateMillis) / MILLIS_PER_DAY) + 1;      
+    maxDateIndex = dateCount;
+    dateStamp = new String[dateCount];
+    datePretty = new String[dateCount];
+
+    todayDateStamp = year() + nf(month(), 2) + nf(day(), 2);
+    // Another option to do this, but more code
+    //Date today = new Date(); 
+    //String todayDateStamp = stampFormat.format(today);
+      
+    for (int i = 0; i < dateCount; i++) {
+      Date date = new Date(firstDateMillis + MILLIS_PER_DAY*i);
+      datePretty[i] = prettyFormat.format(date);
+      dateStamp[i] = stampFormat.format(date);
+      // If this value for 'date' is equal to today, then set the previous 
+      // day as the maximum viewable date, because it means the season is 
+      // still ongoing. The previous day is used because unless it is late 
+      // in the evening, the updated numbers for the day will be unavailable 
+      // or incomplete.
+      if (dateStamp[i].equals(todayDateStamp)) {
+        maxDateIndex = i-1;
+      }
+    }
+  } catch (ParseException e) {
+    die("Problem while setting up dates", e);
+  }
+}  
+
+
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+public void setup() {
+  size(480, 750);
+    
+  setupTeams();
+  setupDates();
+  setupSalaries();
+  // Load the standings after the salaries, because salary 
+  // will be used as the tie-breaker when sorting.
+  setupStandings();
+  setupRanking();
+  setupLogos();
+    
+  font = createFont("Georgia", 12);
+  textFont(font);
+
+  frameRate(15);
+  // Use today as the current day
+  setDate(maxDateIndex);
+}
+  
+
+void setupTeams() {
+  String[] lines = loadStrings("teams.tsv");
+    
+  teamCount = lines.length;
+  teamCodes = new String[teamCount];
+  teamNames = new String[teamCount];
+  teamIndices = new HashMap();
+    
+  for (int i = 0; i < teamCount; i++) {
+    String[] pieces = split(lines[i], TAB);
+    teamCodes[i] = pieces[0];
+    teamNames[i] = pieces[1];
+    teamIndices.put(teamCodes[i], new Integer(i));
+  }
+}
+  
+  
+int teamIndex(String teamCode) {
+  Integer index = (Integer) teamIndices.get(teamCode);
+  return index.intValue();
+}
+
+
+void setupSalaries() {
+  String[] lines = loadStrings("salaries.tsv");
+  salaries = new SalaryList(lines);
+}
+
+
+/*
+void setupStandings() {
+  season = new StandingsList[maxDateIndex + 1];
+  for (int i = minDateIndex; i <= maxDateIndex; i++) {
+    String[] lines = acquireStandings(dateStamp[i]);
+    season[i] = new StandingsList(lines);
+  }
+}
+*/
+  
+
+void setupStandings() {
+  String[] lines = loadStrings("http://benfry.com/writing/salaryper/mlb.cgi");
+  int dataCount = lines.length / teamCount;
+  int expectedCount = (maxDateIndex - minDateIndex) + 1;
+  if (dataCount < expectedCount) {
+    println("Found " + dataCount + " entries in the data file, " +
+            "but was expecting " + expectedCount + " entries.");
+    maxDateIndex = minDateIndex + dataCount - 1;
+  }
+  season = new StandingsList[maxDateIndex + 1];
+  for (int i = 0; i < dataCount; i++) {
+    String[] portion = subset(lines, i*teamCount, teamCount);
+    season[i+minDateIndex] = new StandingsList(portion);
+  }
+}
+  
+
+void setupRanking() {
+  standingsPosition = new Integrator[teamCount];
+  for (int i = 0; i < teamCodes.length; i++) {
+    standingsPosition[i] = new Integrator(i);
+  }
+}
+  
+
+void setupLogos() {
+  logos = new PImage[teamCount];
+  for (int i = 0; i < teamCount; i++) {
+    logos[i] = loadImage("small/" + teamCodes[i] + ".gif");
+  }
+  logoWidth = logos[0].width / 2.0f;
+  logoHeight = logos[0].height / 2.0f;
+}
+  
+  
+public void draw() {
+  background(255);
+  smooth();
+
+  drawDateSelector();
+
+  translate(SIDE_PADDING, TOP_PADDING);
+  
+  boolean updated = false;
+  for (int i = 0; i < teamCount; i++) {
+    if (standingsPosition[i].update()) {
+      updated = true;
+    }
+  }
+  if (!updated) {
+    noLoop();
+  }
+
+  for (int i = 0; i < teamCount; i++) {
+    //float standingsY = standings.getRank(i)*ROW_HEIGHT + HALF_ROW_HEIGHT;
+    float standingsY = standingsPosition[i].value * ROW_HEIGHT + HALF_ROW_HEIGHT;
+
+    image(logos[i], 0, standingsY - logoHeight/2, logoWidth, logoHeight);
+      
+    textAlign(LEFT, CENTER);
+    text(teamNames[i], 28, standingsY);
+
+    textAlign(RIGHT, CENTER);
+    fill(128);
+    text(standings.getTitle(i), 150, standingsY);
+
+    float weight = map(salaries.getValue(i), 
+                       salaries.getMinValue(), salaries.getMaxValue(), 
+                       0.25f, 6);
+    strokeWeight(weight);
+      
+    float salaryY = salaries.getRank(i)*ROW_HEIGHT + HALF_ROW_HEIGHT;
+    if (salaryY >= standingsY) {
+      stroke(33, 85, 156);  // Blue for positive (or equal) difference.
+    } else {
+      stroke(206, 0, 82);   // Red for wasting money.
+    }
+      
+    line(160, standingsY, 325, salaryY);
+
+    fill(128);
+    textAlign(LEFT, CENTER);
+    text(salaries.getTitle(i), 335, salaryY);
+  }
+}
+
+  
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+int dateSelectorX;
+int dateSelectorY = 30;
+
+// Draw a series of lines for selecting the date
+void drawDateSelector() {
+  dateSelectorX = (width - dateCount*2) / 2;
+
+  strokeWeight(1);
+  for (int i = 0; i < dateCount; i++) {
+    int x = dateSelectorX + i*2;
+
+    // If this is the currently selected date, draw it differently
+    if (i == dateIndex) {
+      stroke(0);
+      line(x, 0, x, 13);
+      textAlign(CENTER, TOP);
+      text(datePretty[dateIndex], x, 15);
+
+    } else {
+      // If this is a viewable date, make the line darker
+      if ((i >= minDateIndex) && (i <= maxDateIndex)) {
+        stroke(128);  // Viewable date
+      } else {
+        stroke(204);  // Not a viewable date
+      }
+      line(x, 0, x, 7);
+    }
+  }
+}
+
+
+void setDate(int index) {
+  dateIndex = index;
+  standings = season[dateIndex];
+
+  for (int i = 0; i < teamCount; i++) {
+    standingsPosition[i].target(standings.getRank(i));
+  }
+  // Re-enable the animation loop
+  loop();
+}
+
+
+void mousePressed() {
+  handleMouse();
+}
+  
+void mouseDragged() {
+  handleMouse();
+}
+  
+void handleMouse() {
+  if (mouseY < dateSelectorY) {
+    int date = (mouseX - dateSelectorX) / 2;
+    setDate(constrain(date, minDateIndex, maxDateIndex));
+  }
+}
+
+
+void keyPressed() {
+  if (key == CODED) {
+    if (keyCode == LEFT) {
+      int newDate = max(dateIndex - 1, minDateIndex);
+      setDate(newDate);
+
+    } else if (keyCode == RIGHT) {
+      int newDate = min(dateIndex + 1, maxDateIndex);
+      setDate(newDate);
+    }
+  }
+}
+
+
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+/*
+String[] acquireStandings(String stamp) {
+  int year = int(stamp.substring(0, 4));
+  int month = int(stamp.substring(4, 6));
+  int day = int(stamp.substring(6, 8));
+  return acquireStandings(year, month, day);
+}
+  
+
+String[] acquireStandings(int year, int month, int day) {
+  String filename = year + nf(month, 2) + nf(day, 2) + ".tsv";
+  String path = dataPath(filename);
+  File file = new File(path);
+  if (!file.exists()) {
+    println("Downloading standings file " + filename);
+    PrintWriter writer = createWriter(path);
+
+    String base = "http://mlb.mlb.com/components/game" + 
+      "/year_" + year + "/month_" + nf(month, 2) + "/day_" + nf(day, 2) + "/";
+
+    // American League (AL)
+    parseWinLoss(base + "standings_rs_ale.js", writer);
+    parseWinLoss(base + "standings_rs_alc.js", writer);
+    parseWinLoss(base + "standings_rs_alw.js", writer);
+
+    // National League (NL)
+    parseWinLoss(base + "standings_rs_nle.js", writer);
+    parseWinLoss(base + "standings_rs_nlc.js", writer);
+    parseWinLoss(base + "standings_rs_nlw.js", writer);
+
+    writer.flush();
+    writer.close();
+  }
+  return loadStrings(filename);
+}
+
+  
+void parseWinLoss(String filename, PrintWriter writer) {
+  String[] lines = loadStrings(filename);
+  Pattern p = Pattern.compile("\\s+([\\w\\d]+):\\s'(.*)',?");
+
+  String teamCode = "";
+  int wins = 0;
+  int losses = 0;
+
+  for (int i = 0; i < lines.length; i++) {
+    Matcher m = p.matcher(lines[i]);
+
+    if (m.matches()) {
+      String attr = m.group(1);
+      String value = m.group(2);
+
+      if (attr.equals("code")) {
+        teamCode = value;
+      } else if (attr.equals("w")) {
+        wins = parseInt(value);
+      } else if (attr.equals("l")) {
+        losses = parseInt(value);
+      }
+
+    } else {
+      if (lines[i].startsWith("}")) {
+        // this is the end of a group, write these values
+        //println(team + " " + wins + "-" + losses);
+        //set(teamIndex(teamCode), wins, losses);
+        writer.println(teamCode + TAB + wins + TAB + losses);
+      }
+    }
+  }
+}
+*/
+  
+  
+//. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+  
+class SalaryList extends RankedList {
+    
+  SalaryList(String[] lines) {
+    super(teamCount, false);
+    
+    for (int i = 0; i < teamCount; i++) {
+      String pieces[] = split(lines[i], TAB);
+        
+      // First column is the team 2-3 digit team code.
+      int index = teamIndex(pieces[0]);
+        
+      // Second column is the salary as a number. 
+      value[index] = parseInt(pieces[1]);
+        
+      // Make the title in the format $NN,NNN,NNN
+      int salary = (int) value[index];
+      title[index] = "$" + nfc(salary);
+    }
+    update();
+  }
+}
+
+  
+// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+
+
+class StandingsList extends RankedList {
+    
+  StandingsList(String[] lines) {
+    super(teamCount, false);
+      
+    for (int i = 0; i < teamCount; i++) {
+      String[] pieces = split(lines[i], TAB);
+      int index = teamIndex(pieces[0]);
+      int wins = parseInt(pieces[1]);
+      int losses = parseInt(pieces[2]);
+        
+      value[index] = (float) wins / (float) (wins+losses);
+      title[index] = wins + "\u2013" + losses;
+    }
+    update();
+  }
+
+  float compare(int a, int b) {
+    // First compare based on the record of both teams
+    float amt = super.compare(a, b);
+    // If the record is not identical, return the difference
+    if (amt != 0) return amt;
+
+    // If records are equal, use salary as tie-breaker. 
+    // In this case, a and b are switched, because a higher
+    // salary is a negative thing, unlike the values above.
+    return salaries.compare(a, b);
+  }
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.