import java.text.DateFormatSymbols; import java.util.ArrayList; import processing.core.*; import processing.opengl.*; import peasy.*; import controlP5.*; public class Viz3D extends PApplet { // public static void main(String args[]) // { // PApplet.main(new String[] { "--bgcolor=#ffffff" , "Viz3D" }); // } public class Font { PFont font; float size; Font(PFont font, float size) { this.font = font; this.size = size; } } public class Transaction { String ckoutDateTime; String ckinDateTime; String barcode; String collcode; String itemtype; String title; String callNumber; String deweyClass; int checkout; } public class FallingTransaction { Transaction transaction; float position; int age; FallingTransaction(Transaction transaction, float position) { this.transaction = transaction; this.position = position; this.age = 0; } } public class LeftDragHandler implements PeasyDragHandler { public void handleDrag(double dx, double dy) { if (mouseY < controlHeight) return; else cam.getRotateDragHandler().handleDrag(dx, dy); } } public class RightDragHandler implements PeasyDragHandler { public void handleDrag(double dx, double dy) { if (mouseY < controlHeight) return; else cam.getZoomDragHandler().handleDrag(dx, dy); } } public class MiddleDragHandler implements PeasyDragHandler { public void handleDrag(double dx, double dy) { if (mouseY < controlHeight) return; else cam.getPanDragHandler().handleDrag(dx, dy); } } String typeNames [] = { "other", "books", "audio", "video" }; String deweyClasses[] = { "Computer science, information and general works", "Philosophy and psychology", "Religion", "Social sciences", "Language", "Science", "Technology and applied Science", "Arts and recreation", "Literature", "History, geography, and biography", "Non-Dewey" }; int dcHue[] = {0, 30, 60, 90, 120, 180, 200, 240, 280, 320, 0}; int dcRGB[] = {0xffff0000, 0xffff8000, 0xffffff00, 0xff80ff00, 0xff00ff00, 0xff00ffff, 0xff00aaff, 0xff0000ff, 0xffaa00ff, 0xffff00aa, 0xffaaaaaa}; String[] monthNames = (new DateFormatSymbols()).getMonths(); String numStrings[] = { "None", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"}; int windowWidth = 1024; int windowHeight = 768; int controlHeight = 85; int controlLeft = 155; int infoHeight = 60; // control elements ControlP5 controlP5; CheckBox checkbox; Slider timeSlider; Slider speedSlider; Button forwardButton; Button backButton; Button playButton; boolean showLabels = true; boolean showTitles = true; boolean showCounters = false; boolean showBars = false; int sky = 200; int gridGap = 40; float dropGap = 10; float ringGap = 10; float ringStart = 5; float ringHeight = 2; float ringInner = 0.66f; Font fontLabels; Font fontStackCounters; Font fontBarCounters; Font fontTitles; Font fontClock; Font fontDate; double clock = 7*60*60 + 30*60; int framerate = 60; boolean paused = false; int speed = 1; float rate = (float)(speed * 10) / (float)framerate; PeasyCam cam; float camRotation[]; int dataYear = 2008; int dataMonth = 7; int dataDay = 18; Transaction transactions[][][] = new Transaction[11][4][]; ArrayList fallingTransactions[][] = new ArrayList[11][4]; int count[][] = new int[11][4]; int nextdrop[][] = new int[11][4]; int selectedDewey = -1; int selectedType = 0; PGraphics selectionBuffer; public void setup() { size(windowWidth, windowHeight, OPENGL); hint(DISABLE_OPENGL_2X_SMOOTH); hint(ENABLE_OPENGL_4X_SMOOTH); //hint(ENABLE_DEPTH_SORT); smooth(); frameRate(framerate); selectionBuffer = createGraphics(windowWidth, windowHeight, P3D); cam = new PeasyCam(this, 5 * gridGap, -25, 95, 460); cam.rotateX(-PI*3/8); cam.setMinimumDistance(0.1); cam.setMaximumDistance(50000); cam.setResetOnDoubleClick(false); cam.setLeftDragHandler(new LeftDragHandler()); cam.setRightDragHandler(new RightDragHandler()); cam.setCenterDragHandler(new MiddleDragHandler()); perspective(PI/3, (float)(windowWidth)/(float)(windowHeight), 0.1f, 50000); PFont fontDVSans12 = loadFont("fonts/DejaVuSans-12.vlw"); PFont fontDVSans100 = loadFont("fonts/DejaVuSans-100.vlw"); PFont fontArialMT30 = loadFont("fonts/ArialMT-30.vlw"); PFont fontArialMT100 = loadFont("fonts/ArialMT-100.vlw"); PFont fontArialBoldMT100 = loadFont("fonts/Arial-BoldMT-100.vlw"); fontLabels = new Font(fontArialMT100, 7); fontStackCounters = new Font(fontArialBoldMT100, 6); fontBarCounters = new Font(fontArialMT100, 7); fontTitles = new Font(fontDVSans100, 5); fontDate = new Font(fontDVSans12, 12); fontClock = new Font(fontArialMT30, 30); loadTransactions(dataYear, dataMonth, dataDay); controlP5 = new ControlP5(this); controlP5.setAutoDraw(false); controlP5.setColorLabel(0); checkbox = controlP5.addCheckBox("checkbox", controlLeft + 500 + 80, 10); checkbox.setItemsPerRow(4); checkbox.setSpacingColumn(65); checkbox.addItem("labels", 0).setValue(showLabels); checkbox.addItem("titles", 0).setValue(showTitles); checkbox.addItem("counters", 0).setValue(showCounters); checkbox.addItem("bars", 0).setValue(showBars); timeSlider = controlP5.addSlider("sliderTime", 0, 24*60*60, 0, controlLeft + 95, 31, 754, 25); timeSlider.setSliderMode(Slider.FLEXIBLE); timeSlider.setLabelVisible(false); timeSlider.setNumberOfTickMarks(25); timeSlider.showTickMarks(true); timeSlider.snapToTickMarks(false); ArrayList tm = timeSlider.getTickMarks(); for (int i = 0; i < 25; i++) { Label l = tm.get(i).setLabel(String.format("%02d",i)); l.setVisible(true); l.setColor(0); } speedSlider = controlP5.addSlider("sliderSpeed", 0, 4, 0, controlLeft, 10, 500, 11); speedSlider.setLabel("SPEED"); speedSlider.setColorCaptionLabel(0); speedSlider.setSliderMode(Slider.FIX); speedSlider.showTickMarks(false); backButton = controlP5.addButton("btnBack", 0, controlLeft, 31, 25, 25); backButton.setCaptionLabel(" |<"); backButton.setColorCaptionLabel(0xffffffff); backButton.captionLabel().setFont(controlP5.grixel); playButton = controlP5.addButton("btnPlay", 0, controlLeft + 30, 31, 25, 25); playButton.setCaptionLabel(" ||"); playButton.setColorCaptionLabel(0xffffffff); playButton.captionLabel().setFont(controlP5.grixel); forwardButton = controlP5.addButton("btnForward", 0, controlLeft + 60, 31, 25, 25); forwardButton.setCaptionLabel(" >|"); forwardButton.setColorCaptionLabel(0xffffffff); forwardButton.captionLabel().setFont(controlP5.grixel); // set time to noon setTime(12*60*60); } void setTime(int time) { clock = time; float start = sky; for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { count[dc][type] = 0; nextdrop[dc][type] = 0; fallingTransactions[dc][type].clear(); for (int i = 0; i < transactions[dc][type].length; i++) { Transaction t = transactions[dc][type][i]; if (t.checkout <= clock - 60) { count[dc][type]++; } else if (t.checkout <= clock) { fallingTransactions[dc][type].add( new FallingTransaction(t, start) ); start += 10; } else { break; } nextdrop[dc][type] = i + 1; } } } } void loadTransactions(int year, int month, int day) { String filename = "list-" + year + "-" + month + "-" + day + ".txt"; String lines[] = loadStrings(filename); ArrayList trans[][] = new ArrayList[11][4]; for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { trans[dc][type] = new ArrayList(); fallingTransactions[dc][type] = new ArrayList(); } } // 'ckoutDateTime', 'ckinDateTime', 'barcode', 'id', 'itemId', 'barcode', 'collcode', 'itemtype', 'title', 'callNumber', 'deweyclass', 'id', 'count' for (int i = 0; i < lines.length; i++) { String[] fields = lines[i].split(" , "); Transaction t = new Transaction(); t.ckoutDateTime = fields[0]; t.ckinDateTime = fields[1]; t.barcode = fields[2]; t.collcode = fields[6]; t.itemtype = fields[7]; t.title = fields[8]; t.callNumber = fields[9]; t.deweyClass = fields[10]; String dt[] = t.ckoutDateTime.split(" "); t.checkout = Integer.parseInt(dt[1].substring(0, 2)) * 60 * 60 + Integer.parseInt(dt[1].substring(3, 5)) * 60; // only use data from the central library if (t.collcode.startsWith("c")) { int dc = getClass(t.deweyClass); int type = getType(t.itemtype); trans[dc][type].add(t); } } for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { int len = trans[dc][type].size(); transactions[dc][type] = trans[dc][type].toArray(new Transaction[len]); } } } int getType(String type) { if (type.endsWith("bk")) return 1; else if (type.endsWith("cd") || type.endsWith("cas")) return 2; else if (type.endsWith("dvd") || type.endsWith("vhs")) return 3; return 0; } int getClass(String dclass) { if (dclass.equals("None")) return 10; return Integer.parseInt(dclass.substring(0,1)); } String timeToString(int time) { int h = time / (60 * 60); int m = (time % (60 * 60)) / 60; int s = (time % (60 * 60)) % 60; return String.format("%02d:%02d:%02d", h, m, s); } public void draw() { colorMode(HSB, 360, 100, 100); background(0xffffffff); //lights(); ambientLight(0, 0, 50); lightFalloff(1, 0, 0); lightSpecular(0, 0, 0); directionalLight(0, 0, 50, 0, 0, -1); directionalLight(0, 0, 50, -1, -1, 0); directionalLight(0, 0, 50, 1, -1, 1); camRotation = cam.getRotations(); if (clock > 24*60*60) { clock = 24*60*60; paused = true; } if (!paused) { clock += speed / (float)framerate; } timeSlider.changeValue((float)clock); addDrops(); showLabels = checkbox.getState(0); showTitles = checkbox.getState(1); showCounters = checkbox.getState(2); showBars = checkbox.getState(3); if (showBars) drawBars(showCounters); drawStacks(showLabels, showCounters, g); drawFalling(showTitles); drawHud(); } void addDrops() { // get top position float start = sky; for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { int inair = fallingTransactions[dc][type].size(); if (inair > 0) { float top = fallingTransactions[dc][type].get(inair-1).position; if (top + dropGap > start) start = top + dropGap; } } } // add transactions for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { for (int i = nextdrop[dc][type]; i < transactions[dc][type].length; i++) { Transaction t = transactions[dc][type][i]; if (t.checkout <= clock) { fallingTransactions[dc][type].add( new FallingTransaction(t, start) ); start += dropGap; } else { break; } nextdrop[dc][type] = i + 1; } } } } void drawBars(boolean showCounters) { // get totals int deweyTotal[] = new int[11]; int typeTotal[] = new int[4]; for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { deweyTotal[dc] += count[dc][type]; typeTotal[type] += count[dc][type]; } } // draw dewey bars for (int dc = 0; dc < 11; dc++) { beginShape(QUADS); float sumc = 0; for (int type = 3; type >= 0; type--) { if (dc == 10) fill(0, 0, 80 - type * 5); else fill(dcHue[dc], 60 + type * 10, 90); float cc = count[dc][type]; vertex(dc * gridGap - 5, -gridGap - sumc/10); vertex(dc * gridGap + 5, -gridGap - sumc/10); vertex(dc * gridGap + 5, -gridGap - sumc/10 - cc/10); vertex(dc * gridGap - 5, -gridGap - sumc/10 - cc/10); sumc += cc; } endShape(); // draw counters if (showCounters) { float c = deweyTotal[dc]; textAlign(CENTER, CENTER); textFont(fontBarCounters.font, fontBarCounters.size); fill(0); pushMatrix(); translate(dc * gridGap, -c/10 - gridGap - 10, 0); fixRotation(); text(Integer.toString((int)c), 0, 0); popMatrix(); } } // draw type bars for (int type = 0; type < 4; type++) { noStroke(); beginShape(QUADS); float sumc = 0; for (int dc = 0; dc < 11; dc++) { if (dc == 10) fill(0, 0, 80); else fill(dcHue[dc], 90, 90); float cc = count[dc][type]; vertex(gridGap * 11 + sumc/10, type * gridGap - 5f); vertex(gridGap * 11 + sumc/10, type * gridGap + 5f); vertex(gridGap * 11 + sumc/10 + cc/10, type * gridGap + 5f); vertex(gridGap * 11 + sumc/10 + cc/10, type * gridGap - 5f); sumc += cc; } endShape(); // draw counters if (showCounters) { textAlign(LEFT, CENTER); if (abs(camRotation[2]) > HALF_PI) textAlign(RIGHT, CENTER); float c = typeTotal[type]; textFont(fontBarCounters.font, fontBarCounters.size); fill(0); pushMatrix(); translate(c/10 + gridGap * 11 + 10, type * gridGap, 0); fixRotation(); text(Integer.toString((int)c), 0, 0); popMatrix(); } } } void drawHud() { cam.beginHUD(); noLights(); // draw background fill(0xaaffffff); rect(0, 0, windowWidth, controlHeight); fill(0); // draw date textAlign(CENTER); textFont(fontDate.font); String date = dataDay + " " + monthNames[dataMonth-1].toUpperCase() + " " + dataYear; text(date, controlLeft/2f, 20); // draw time textFont(fontClock.font, fontClock.size); String time = timeToString((int)clock); text(time, controlLeft/2, 55); // draw selection if (selectedDewey >= 0) { fill(0xaaffffff); rect(0, 768 - infoHeight, windowWidth, infoHeight); if (selectedDewey == 10) fill(0, 0, 50); else fill(dcHue[selectedDewey], 100, 100); noStroke(); pushMatrix(); translate(30, 739); switch(selectedType) { case 0: translate(0, -8); scale(2*12,2*12,1); drawPrism(3, false, g); break; case 1: scale(2*16,2*16,1); box(1); break; case 2: scale(2*9,2*9,1); drawPrism(24, true, g); break; case 3: scale(2*10,2*10,1); drawPrism(6, false, g); break; } popMatrix(); textAlign(LEFT); textFont(fontClock.font); String dc = deweyClasses[selectedDewey]; String type = typeNames[selectedType]; fill(0); text(dc + " (" + type + "): " + count[selectedDewey][selectedType], 60, 750); } // draw controls controlP5.draw(); cam.endHUD(); } void drawFalling(boolean showTitles) { for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { ArrayList hitlist = new ArrayList(); for (int i = 0; i < fallingTransactions[dc][type].size(); i++) { FallingTransaction drop = fallingTransactions[dc][type].get(i); //*/ int c = count[dc][type] % 100; /*/ int c = 150 + (count[dc][type] - 50) % 100; if (count[dc][type] < 50) c = count[dc][type]; //*/ if ( drop.position < c + 1 || (!paused && drop.position - rate < 0) ) { hitlist.add(i); count[dc][type]++; } else if (!paused) { drop.position -= rate; } drop.age++; if (dc == 10) fill(0, 0, 75); else fill(dcHue[dc], 90, 90); noStroke(); pushMatrix(); translate(dc * gridGap, type * gridGap, drop.position); switch(type) { case 0: scale(12,12,1); drawPrism(3, false, g); break; case 1: scale(16,16,1); box(1); break; case 2: scale(9,9,1); drawPrism(24, true, g); break; case 3: scale(10,10,1); drawPrism(6, false, g); break; } popMatrix(); if (showTitles) { textFont(fontTitles.font, fontTitles.size); String label = drop.transaction.title; while (textWidth(label) > 100) { label = label.substring(0, label.length() - 4) + "..."; } colorMode(RGB, 255, 255, 255); if (drop.age < 32) fill(0x01000000 + drop.age/4 * 0x11000000); else fill(0); textAlign(CENTER); pushMatrix(); translate(dc * gridGap, type * gridGap, drop.position + 2); if (camRotation[0] > 0) rotateZ(PI); rotateX(-HALF_PI); text(label, 0, 0); popMatrix(); colorMode(HSB, 360, 100, 100); } } // clean list for (int i = 0, j = 0; i < hitlist.size(); i++, j++) { fallingTransactions[dc][type].remove(hitlist.get(i) - j); } } } } void fixRotation() { if (abs(camRotation[0]) > HALF_PI) { rotateY(PI); if (abs(camRotation[2]) < HALF_PI) rotateZ(PI); } else if (abs(camRotation[2]) > HALF_PI) { rotateZ(PI); } } void drawRing(int sides, float innerRadius, boolean smooth, PGraphics pg) { // bottom pg.beginShape(QUAD_STRIP); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; if (sides == 4) angle += QUARTER_PI; float x1 = sin(angle); float y1 = cos(angle); pg.vertex(x1,y1,-0.5f); float x2 = innerRadius * sin(angle); float y2 = innerRadius * cos(angle); pg.vertex(x2,y2,-0.5f); } pg.endShape(); // outer sides pg.beginShape(QUAD_STRIP); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; if (sides == 4) angle += QUARTER_PI; float x = sin(angle); float y = cos(angle); if (smooth) normal(x,y,0); pg.vertex(x,y,-0.5f); if (smooth) normal(x,y,0); pg.vertex(x,y,+0.5f); } pg.endShape(); // inner sides pg.beginShape(QUAD_STRIP); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; if (sides == 4) angle += QUARTER_PI; float x = innerRadius * sin(angle); float y = innerRadius * cos(angle); if (smooth) normal(x,y,0); pg.vertex(x,y,-0.5f); if (smooth) normal(x,y,0); pg.vertex(x,y,+0.5f); } pg.endShape(); // top pg.beginShape(QUAD_STRIP); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; if (sides == 4) angle += QUARTER_PI; float x1 = sin(angle); float y1 = cos(angle); pg.vertex(x1,y1,+0.5f); float x2 = innerRadius * sin(angle); float y2 = innerRadius * cos(angle); pg.vertex(x2,y2,+0.5f); } pg.endShape(); } void drawPrism(int sides, boolean smooth, PGraphics pg) { // bottom pg.beginShape(TRIANGLE_FAN); pg.vertex(0,0,-0.5f); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; float x = sin(angle); float y = cos(angle); pg.vertex(x,y,-0.5f); } pg.endShape(); // sides pg.beginShape(QUAD_STRIP); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; float x = sin(angle); float y = cos(angle); if (smooth) pg.normal(x,y,0); pg.vertex(x,y,-0.5f); if (smooth) pg.normal(x,y,0); pg.vertex(x,y,+0.5f); } pg.endShape(); // top pg.beginShape(TRIANGLE_FAN); pg.vertex(0,0,+0.5f); for (int i = 0; i <= sides; i ++) { float angle = i / (float)sides * 2 * PI; float x = sin(angle); float y = cos(angle); pg.vertex(x,y,+0.5f); } pg.endShape(); } void drawStacks(boolean showLabel, boolean showCount, PGraphics pg) { pg.fill(0); pg.textFont(fontLabels.font, fontLabels.size); // draw type labels if (showLabel) { pg.textAlign(RIGHT, CENTER); if (abs(camRotation[2]) > HALF_PI) { pg.textAlign(LEFT, CENTER); } pg.pushMatrix(); pg.translate(-gridGap, 0, 0); fixRotation(); pg.text("OTHER", 0, 0); pg.popMatrix(); pg.pushMatrix(); pg.translate(-gridGap, gridGap, 0); fixRotation(); pg.text("BOOKS", 0, 0); pg.popMatrix(); pg.pushMatrix(); pg.translate(-gridGap, 2*gridGap, 0); fixRotation(); pg.text("AUDIO", 0, 0); pg.popMatrix(); pg.pushMatrix(); pg.translate(-gridGap, 3*gridGap, 0); fixRotation(); pg.text("VIDEO", 0, 0); pg.popMatrix(); } for (int dc = 0; dc < 11; dc++) { // draw dewey class labels if (showLabel) { String labeldc = Integer.toString(dc) + "00"; if (labeldc.equals("1000")) { labeldc = "NON"; } pg.textFont(fontLabels.font, fontLabels.size); pg.textAlign(CENTER, CENTER); pg.fill(0); pg.pushMatrix(); pg.translate(dc * gridGap, 4*gridGap, 0); fixRotation(); pg.text(labeldc, 0, 0); pg.popMatrix(); } for (int type = 0; type < 4; type++) { if (dc == 10) pg.fill(0, 0, 75); else pg.fill(dcHue[dc], 90, 90); noStroke(); int c = count[dc][type]; //*/ int over = c / 100; int under = c % 100; /*/ int over = (c - 50) / 100; int under = 50 + (c - 50) % 100; if (c < 150) { over = 0; under = c; } //*/ // draw rings if (over > 0) { for (int i = 0; i < over; i++) { pg.pushMatrix(); pg.translate(dc * gridGap, type * gridGap, ringStart + i * ringGap + ringHeight/2); switch(type) { case 0: pg.scale(2*12, 2*12, ringHeight); drawRing(3, ringInner, false, pg); break; case 1: pg.scale(2*12, 2*12, ringHeight); drawRing(4, ringInner, false, pg); break; case 2: pg.scale(2*9, 2*9, ringHeight); drawRing(24, ringInner, true, pg); break; case 3: pg.scale(2*10, 2*10, ringHeight); drawRing(6, ringInner, false, pg); break; } pg.popMatrix(); } } // draw stacks float z = under; if (z==0) z = 0.1f; pg.pushMatrix(); pg.translate(dc * gridGap, type * gridGap, z/2.0f); switch(type) { case 0: pg.scale(12,12,z); drawPrism(3, false, pg); break; case 1: pg.scale(16,16,z); pg.box(1); break; case 2: pg.scale(9,9,z); drawPrism(24, true, pg); break; case 3: pg.scale(10,10,z); drawPrism(6, false, pg); break; } pg.popMatrix(); // draw counters if (showCount) { pg.textAlign(CENTER, CENTER); pg.textFont(fontStackCounters.font, fontStackCounters.size); pg.fill(0, 0, 100); pg.pushMatrix(); if (abs(camRotation[0]) > HALF_PI) { pg.translate(dc * gridGap, type * gridGap, -0.1f); } else { pg.translate(dc * gridGap, type * gridGap, z+0.1f); } fixRotation(); pg.text(Integer.toString((int)c), 0, 0); pg.text(Integer.toString((int)c), 0, 0); pg.popMatrix(); } } } } void drawStacksSelection(PGraphics pg) { for (int dc = 0; dc < 11; dc++) { for (int type = 0; type < 4; type++) { pg.fill(10 + dc * 10 + type); int c = count[dc][type]; //*/ int over = c / 100; int under = c % 100; /*/ int over = (c - 50) / 100; int under = 50 + (c - 50) % 100; if (c < 150) { over = 0; under = c; } //*/ // draw rings if (over > 0) { for (int i = 0; i < over; i++) { pg.pushMatrix(); pg.translate(dc * gridGap, type * gridGap, ringStart + i * ringGap + ringHeight/2); switch(type) { case 0: pg.scale(2*12, 2*12, ringHeight); drawRing(3, ringInner, false, pg); break; case 1: pg.scale(2*12, 2*12, ringHeight); drawRing(4, ringInner, false, pg); break; case 2: pg.scale(2*9, 2*9, ringHeight); drawRing(24, ringInner, true, pg); break; case 3: pg.scale(2*10, 2*10, ringHeight); drawRing(6, ringInner, false, pg); break; } pg.popMatrix(); } } // draw stacks float z = under; if (z==0) z = 0.1f; pg.pushMatrix(); pg.translate(dc * gridGap, type * gridGap, z/2.0f); switch(type) { case 0: pg.scale(12,12,z); drawPrism(3, false, pg); break; case 1: pg.scale(16,16,z); pg.box(1); break; case 2: pg.scale(9,9,z); drawPrism(24, true, pg); break; case 3: pg.scale(10,10,z); drawPrism(6, false, pg); break; } pg.popMatrix(); } } } public void mouseClicked() { if (mouseY < controlHeight) return; selectionBuffer.beginDraw(); selectionBuffer.background(0); selectionBuffer.noStroke(); selectionBuffer.setMatrix(((PGraphics3D)g).camera); drawStacksSelection(selectionBuffer); selectionBuffer.endDraw(); long clr = selectionBuffer.get(mouseX, mouseY) - 0xff000000; int pick = (int)(clr % 256); selectedType = (pick - 10) % 10; selectedDewey = ((pick - 10) - selectedType) / 10; } public void mousePressed() { if (mouseEvent.getClickCount() == 2 && selectedDewey >=0) { cam.lookAt(selectedDewey * gridGap, selectedType * gridGap, count[selectedDewey][selectedType]%100); cam.setDistance(30); } } public void keyPressed() { switch(key) { case 32: if (selectedDewey >=0) { cam.lookAt(selectedDewey * gridGap, selectedType * gridGap, count[selectedDewey][selectedType]%100); cam.setDistance(30); } break; } } public void sliderTime(int time) { setTime(time); } public void sliderSpeed(float value) { speed = (int) pow(10, value); if (speed == 1) speedSlider.setValueLabel("real time"); else speedSlider.setValueLabel(speed + " x"); rate = (float)(speed * 10) / (float)framerate; } public void btnBack(float value) { setTime( (int)clock - speed ); } public void btnPlay(float value) { paused = !paused; if (paused) playButton.captionLabel().set(" >"); else playButton.captionLabel().set(" ||"); } public void btnForward(float value) { setTime( (int)clock + speed ); } }