What's
the 7 prevalently checked out items
about
the 7 deadly sins?
Weihao Qiu
This time, I mainly made updates to enhance the interaction. All interaction methods are now can be completed by mouse click.
- New Interactions
Interaction:
1. To add or minus the selected year you want to look, which range from 1990 to 2014.
2. To choose to see or not to see data of other year than the selected year.
3. To control the type of items to show.
4. The four buttons are used to :
(1).Control the mode of viewing
(2).Whether rotate automatically
(3).Whether show as black background or white background
(4).Whether to show the grahpic user interface.
5. To control the sin you want to see. Single click for choose single one sin. Double click for seeing all seven sins.
Additionally, to suit different taste, I made a black version of this visualization. These two version can be switched through a button on the bottom of the visualization.
- Black mode & White mode
The new processing code:
1.DataVis_3dVisualization_version2.pde:
Code: Select all
import controlP5.*;
import peasy.*;
import processing.opengl.*;
PeasyCam cam;
ControlP5 cp5;
ListBox l;
// Data parameters
Table dataTable;
int rowNums, colNums;
int[] MaxCheckoutCount, MinCheckoutCount;
String[] sevenDeadlySins;
// Drawing setting
float overallRadius, panelDistance;
PVector[] Points;
int presentYear;
float interpolatorYear;
Integrator[] interpolator;
float rotateX, rotateY, rotateZ;
// Color & fonts
color[] colors;
PFont[] fonts;
// Control parameters
int viewMode;
boolean showOtherYear, showBook, showVideo, showAudio, rotateFree, autoRotate, showGUI, onlyStillFrame, blackOrWhite;
boolean[] titleSelected;
boolean mouseOnBook, mouseOnVideo, mouseOnAudio, interactWithSin, interactWithButtons;
boolean[] mouseOnSins, showTheSin;
boolean mouseOnUpTriangle, mouseOnDownTriangle, mouseOnPresentYear;
RectButton[] rectButtons;
void setup() {
fullScreen(P3D);
//size(800, 800, P3D);
cam = new PeasyCam(this, 250);
cam.setWheelScale(0.3);
cam.setDistance(2000);
//Initialization for data
//
dataTable = loadTable("query_result.csv", "header");
rowNums = dataTable.getRowCount();
colNums = dataTable.getColumnCount();
MaxCheckoutCount = new int[7];
MinCheckoutCount = new int[7];
for (int i = 0; i< 7; i++) {
MaxCheckoutCount[i] = 0;
MinCheckoutCount[i] = 100000;
}
for (int i = 0; i<rowNums; i++) {
TableRow row = dataTable.getRow(i);
int index = whichDeadlySin(row.getString(2));
int checkoutCount = row.getInt(4);
if (checkoutCount > MaxCheckoutCount[index]) MaxCheckoutCount[index] = checkoutCount;
if (checkoutCount < MinCheckoutCount[index]) MinCheckoutCount[index] = checkoutCount;
}
sevenDeadlySins = new String[7];
sevenDeadlySins[0] = "Lust";
sevenDeadlySins[1] = "Gluttony";
sevenDeadlySins[2] = "Greed";
sevenDeadlySins[3] = "Wrath";
sevenDeadlySins[4] = "Sloth";
sevenDeadlySins[5] = "Envy";
sevenDeadlySins[6] = "Pride";
// Initializaiton for drawing
//
rotateX = 0;
rotateY = 0;
rotateZ = 0;
viewMode = 1;
showOtherYear = true;
showBook = true;
showVideo = true;
showAudio = true;
rotateFree = true;
showGUI = true;
autoRotate = false;
onlyStillFrame = false;
mouseOnBook = false;
mouseOnVideo = false;
mouseOnAudio = false;
interactWithSin = false;
blackOrWhite = true;
titleSelected = new boolean[rowNums];
for (int i = 0; i<rowNums; i++) {
titleSelected[i] = false;
}
mouseOnSins = new boolean[7];
showTheSin = new boolean[7];
for (int i= 0; i<7; i++) {
mouseOnSins[i] = false;
showTheSin[i] = true;
}
overallRadius = 400;
panelDistance = 70;
presentYear = 1990;
interpolator = new Integrator[4];
interpolator[0] = new Integrator(presentYear, 0.4, 0.08);
interpolator[1] = new Integrator(rotateX, 0.4, 0.16);
interpolator[2] = new Integrator(rotateY, 0.4, 0.16);
interpolator[3] = new Integrator(rotateZ, 0.4, 0.16);
Points = new PVector[7];
for (int i = 0; i<7; i++) {
float x, y;
x = overallRadius*cos(i*2*PI/7);
y = overallRadius*sin(i*2*PI/7);
Points[i] = new PVector(x, y, 0);
}
// Initialize colors & fonts
//
colors = new color[10];
colors[0] = #0A86D0;
colors[1] = #134ED5;
colors[2] = #6D5CAF;
colors[3] = #D2000D;
colors[4] = #EB6002;
colors[5] = #FFBC00;
colors[6] = #019C69;
colors[7] = #D42020;
if (blackOrWhite) {
colors[9] = #FFFFFF;
colors[8] = #101010;
} else {
colors[8] = #FFFFFF;
colors[9] = #000000;
}
fonts = new PFont[3];
fonts[0] = loadFont("OratorStd-48.vlw");
fonts[1] = loadFont("OratorStd-12.vlw");
controllerSetup();
noLights();
smooth();
}
void draw() {
background(colors[8]);
for (int i = 0; i < interpolator.length; i++) {
interpolator[i].update();
}
interpolatorYear = interpolator[0].value;
// When rotation during the viewmode changing is completed, rotation is restored to be free.
if (!rotateFree) {
if (abs(interpolator[1].value - interpolator[1].target)<0.0001
&& abs(interpolator[2].value - interpolator[2].target)<0.0001
&& abs(interpolator[3].value - interpolator[3].target)<0.0001) rotateFree = true;
else cam.setRotations(interpolator[1].value, interpolator[2].value, interpolator[3].value);
} else
setRotationInterpolator(cam.getRotations()[0], cam.getRotations()[1], cam.getRotations()[2]);
for (int i = 0; i<rowNums; i++) {
if ((l.getItem(i).get("state").toString().equals("true"))) {
titleSelected[i] = true;
} else
titleSelected[i] = false;
}
drawStillFrame();
if (!onlyStillFrame) {
for (int i= 0; i<rowNums; i++) {
drawTableRow(i);
}
}
if (autoRotate) {
if (viewMode == 1)cam.rotateZ(0.002);
if (viewMode == 2)cam.rotateX(0.002);
if (viewMode == 3)cam.rotateY(0.002);
}
mouseInteraction();
hint(DISABLE_DEPTH_TEST);
cam.beginHUD();
if (showGUI) gui();
drawButtons();
cam.endHUD();
hint(ENABLE_DEPTH_TEST);
}
void drawTableRow(int i) {
TableRow row = dataTable.getRow(i);
String type = row.getString(1);
String theSin = row.getString(2);
int year = row.getInt(3);
if (year<1990) year = 1990;
int index = whichDeadlySin(theSin);
// Judgement about itemType
// If selected, then show.
//
boolean shouldShow = false;
if (type.equals("Book") && showBook) shouldShow = true;
if (type.equals("Video") && showVideo) shouldShow = true;
if (type.equals("Audio") && showAudio) shouldShow = true;
if (shouldShow) {
float percent = sqrt(sqrt(map(row.getFloat(4), MinCheckoutCount[index], MaxCheckoutCount[index], 0, 1)));
float percent1 = map(percent, 0, 1, 0.1, 0.9);
PVector p = PVector.lerp(Points[index], Points[(index+1)%7], percent1);
float z = (year - interpolatorYear) * panelDistance;
beginShape(LINES);
colorMode(HSB, 255);
color theColor = color(hue(colors[index]), saturation(colors[index])*0.9, brightness(colors[index]));
// Judgement for each row's diplay mode
//
int mode = 0;
if (showTheSin[index])
mode = 2;
else
mode = 4;
if (titleSelected[i]==true) mode = 3;
else {
if ( mode != 4) {
if (presentYear == year) mode = 1;
else {
if (showOtherYear ) mode = 2;
else mode =4;
}
}
}
// Display according to its diplay mode
//
switch (mode) {
case 1:
// #1 YearSelected
stroke(theColor, 100);
strokeWeight(1.5);
vertex(p.x, p.y, p.z);
stroke(theColor, 250);
strokeWeight(max(10*percent, 1.5));
vertex(0, 0, z);
break;
case 2:
// #2 Visible
stroke(theColor, 40);
strokeWeight(1);
vertex(p.x, p.y, p.z);
stroke(theColor, 110);
strokeWeight(max(10*percent, 1));
vertex(0, 0, z);
break;
case 3:
// #3 TitleSelected
stroke(colors[9], 100);
strokeWeight(3);
vertex(p.x, p.y, p.z);
stroke(colors[9], 200);
strokeWeight(max(10*percent, 3));
vertex(0, 0, z);
break;
case 4 :
// #4 Invisible
stroke(hue(colors[index]), saturation(colors[index])*0.1, brightness(colors[index])*0.1, 50);
strokeWeight(0.5);
vertex(p.x, p.y, p.z);
strokeWeight(0.5);
vertex(0, 0, z);
}
endShape();
// Show titles of items published at selected year
//
if ((year == presentYear && mode != 4 )|| titleSelected[i]) {
pushMatrix();
translate(p.x, p.y, p.z);
rotateY(PI/2);
rotateX(((7-index)+0.5)*2*PI/7 - PI/4);
rotateZ(PI);
fill(colors[9], 200);
textFont(fonts[0], 8);
textAlign(LEFT);
String title = row.getString(0);
text(str(row.getInt(4))+" "+title, 0, 0, 0);
popMatrix();
}
}
}
// A mapping between index and sins
//
int whichDeadlySin(String theSin) {
int index=0;
if (theSin.equals("Lust")) index = 0;
if (theSin.equals("Gluttony")) index = 1;
if (theSin.equals("Greed")) index = 2;
if (theSin.equals("Wrath")) index = 3;
if (theSin.equals("Sloth")) index = 4;
if (theSin.equals("Envy")) index = 5;
if (theSin.equals("Pride")) index = 6;
return index;
}
2.Integrator.pde
Code: Select all
/*
Original Author: Ben Fry
From book Visualizing Data
*/
class Integrator {
final float DAMPING = 0.5f;
final float ATTRACTION = 0.2f;
float value;
float vel;
float accel;
float force;
float mass = 1;
float damping = DAMPING;
float attraction = ATTRACTION;
boolean targeting;
float target;
Integrator() { }
Integrator(float value) {
this.value = value;
}
Integrator(float value, float damping, float attraction) {
this.value = value;
this.damping = damping;
this.attraction = attraction;
}
void set(float v) {
value = v;
}
void update() {
if (targeting) {
force += attraction * (target - value);
}
accel = force / mass;
vel = (vel + accel) * damping;
value += vel;
force = 0;
}
void target(float t) {
targeting = true;
target = t;
}
void noTarget() {
targeting = false;
}
}
3.Button.pde
Code: Select all
class RectButton {
float x;
float y;
float w;
float h;
Boolean status;
color pressedColor;
color hoverColor;
color onColor;
color offColor;
RectButton(float _x,
float _y,
float _w,
float _h,
Boolean _status,
color _pressedColor,
color _hoverColor,
color _onColor,
color _offColor)
{
x = _x;
y = _y;
w = _w;
h = _h;
status = _status;
pressedColor = _pressedColor;
hoverColor = _hoverColor;
onColor = _onColor;
offColor = _offColor;
}
void draw() {
noStroke();
if (this.mouseOnButton()) {
if (mousePressed) fill(pressedColor);
else fill(hoverColor);
} else {
if (status) fill (onColor);
else fill(offColor);
}
rectMode(CENTER);
rect(x, y, w, h);
}
boolean mouseOnButton() {
return (between(mouseX, x-w/2, x+w/2) && between(mouseY, y-h/2, y+h/2));
}
void setStatus(boolean _status) {
status = _status;
}
void changeStatus() {
status = !status;
}
}
boolean between(float a, float b, float c ) {
return ((a>=b)&&(a<=c)) || ((a>=c)&&(a<=b));
}
4.Interaction.pde
Code: Select all
void mouseInteraction() {
// Judgement for which sin the mouse is hovering on
//
PVector center = new PVector(width*7/8, height*13/16);
PVector mouse = new PVector(mouseX, mouseY);
PVector mouseToCenterDistance = mouse.sub(center);
float angle = PVector.angleBetween(mouseToCenterDistance, new PVector(1.f, 0.f));
for (int i= 0; i<7; i++) {
mouseOnSins[i] = false;
}
if (mouseToCenterDistance.mag() < overallRadius/3) {
interactWithSin = true;
if (angle < 2*PI/7) {
if (mouseY > center.y) mouseOnSins[0] = true;
else mouseOnSins[6] = true;
} else if (angle < 4*PI/7) {
if (mouseY > center.y) mouseOnSins[1] = true;
else mouseOnSins[5] = true;
} else if (angle < 6*PI/7) {
if (mouseY > center.y) mouseOnSins[2] = true;
else mouseOnSins[4] = true;
} else mouseOnSins[3] = true;
//println(degrees(angle));
} else if (interactWithSin) {
interactWithSin = false;
}
// Judgement for wheter user is interacting with the 4 buttons
//
interactWithButtons = false;
for (int i = 0; i< 4; i++) {
if (rectButtons[i].mouseOnButton()) {
interactWithButtons = true;
}
}
}
void mousePressed() {
// ItemType interaction
//
if (mouseOnBook) showBook = !showBook;
if (mouseOnAudio) showAudio = !showAudio;
if (mouseOnVideo) showVideo = !showVideo;
// Show other years' items or just the present years'
//
if (mouseOnPresentYear) showOtherYear = !showOtherYear;
// Uptriangle and downtriangle interaction
//
if (mouseOnUpTriangle) {
presentYear ++;
interpolator[0].target(presentYear);
}
if (mouseOnDownTriangle) {
presentYear --;
interpolator[0].target(presentYear);
}
// seven short lines interaction for choosing the sin user want to see
//
for (int i= 0; i<7; i++) {
if (mouseOnSins[i]) {
if (showTheSin[i] == true && showTheSin[(i+1)%7]== false) {
for (int j= 0; j<7; j++) {
showTheSin[j] = true;
}
} else {
for (int j= 0; j<7; j++) {
if (j != i) showTheSin[j] = false;
else showTheSin[j] = true;
}
}
}
}
// Buttons' interaction
//
for (int i = 0; i <4; i++) {
if (rectButtons[i].mouseOnButton()) {
switch (i) {
case 0 :
viewMode++;
viewMode = viewMode % 3+1;
switch (viewMode) {
case 1:
cam.setDistance(2000);
setRotationInterpolator(0, 0, 0);
break;
case 2:
setRotationInterpolator(0, -PI/2, 0);
rotateFree = false;
break;
case 3:
viewMode = 3;
setRotationInterpolator(-PI/2, -PI/2, 0);
rotateFree = false;
}
break;
case 1 :
autoRotate = !autoRotate;
break;
case 2 :
blackOrWhite = !blackOrWhite;
if (blackOrWhite) {
colors[9] = #FFFFFF;
colors[8] = #101010;
} else {
colors[8] = #FFFFFF;
colors[9] = #000000;
}
controllerSetup();
break;
case 3 :
showGUI = !showGUI;
break;
}
}
}
}
void keyPressed() {
if (key == 'S' || key == 's') {
saveFrame("######.jpg");
}
if (key == '=' || key =='+') {
presentYear++;
interpolator[0].target(presentYear);
}
if (key == '-' || key =='_') {
presentYear--;
interpolator[0].target(presentYear);
}
if (key == 'F' || key =='f') {
rotateFree = !rotateFree;
}
if (key == 'T' || key == 't') {
autoRotate = !autoRotate;
}
if (key == 'B' || key =='b') {
showBook = !showBook;
}
if (key == 'V' || key =='v') {
showVideo = !showVideo;
}
if (key == 'A' || key =='a') {
showAudio = !showAudio;
}
if (key == 'Y' || key =='y') {
showOtherYear = !showOtherYear;
}
if (key == 'H' || key == 'h') {
showGUI = !showGUI;
}
if (key == 'C' || key == 'c') {
for (int i =0; i<rowNums; i++) {
l.getItem(i).put("state", false);
}
}
if (key == 'P' || key == 'p') {
onlyStillFrame = !onlyStillFrame;
}
if (key == '1') {
viewMode = 1;
cam.setDistance(1600);
setRotationInterpolator(0, 0, 0);
}
if (key == '2') {
viewMode = 2;
setRotationInterpolator(0, -PI/2, 0);
rotateFree = false;
}
if (key == '3') {
viewMode = 3;
setRotationInterpolator(-PI/2, -PI/2, 0);
rotateFree = false;
}
}
void setRotationInterpolator(float rx, float ry, float rz) {
interpolator[1].set(cam.getRotations()[0]);
interpolator[2].set(cam.getRotations()[1]);
interpolator[3].set(cam.getRotations()[2]);
interpolator[1].target(rx);
interpolator[2].target(ry);
interpolator[3].target(rz);
}
5.StillFrame.pde
Code: Select all
void drawStillFrame() {
for (int i = 0; i< 7; i++) {
// bar
PVector p1 = PVector.lerp(Points[i], Points[(i+1)%7], 0.1);
PVector p2 = PVector.lerp(Points[i], Points[(i+1)%7], 0.9);
//println(p1);
beginShape(LINES);
strokeWeight(3);
stroke(colors[i], 20);
vertex(p1.x, p1.y, p1.z);
strokeWeight(3);
if (showTheSin[i]) {
stroke(colors[i], 200);
} else {
stroke(colors[i], 20);
}
vertex(p2.x, p2.y, p2.z);
endShape();
fill(colors[9]);
// count label
pushMatrix();
translate(p2.x, p2.y, p2.z);
rotateZ((2*i+1)*PI/7+PI);
textAlign(RIGHT, TOP);
textFont(fonts[0], 20);
text(str(MaxCheckoutCount[i]), -10, 0, 0);
popMatrix();
pushMatrix();
translate(p1.x, p1.y, p1.z);
rotateZ((2*i+1)*PI/7+PI);
textAlign(RIGHT, BOTTOM);
text(str(MinCheckoutCount[i]), -5, 0, 0);
popMatrix();
}
beginShape(LINES);
for (int i = 0; i< 7; i++) {
PVector p1 = PVector.lerp(Points[i], Points[(i+1)%7], 0.1);
PVector p2 = PVector.lerp(Points[i], Points[(i+1)%7], 0.9);
//println(p1);
strokeWeight(3);
if (showTheSin[i]) {
stroke(colors[i], 255);
} else {
stroke(colors[i], 20);
}
vertex(p1.x*2, p1.y*2, p1.z);
vertex(p2.x*2, p2.y*2, p2.z);
}
endShape();
for (int i = 0; i< 7; i++) {
PVector p3 = PVector.lerp(Points[i], Points[(i+1)%7], 0.5);
pushMatrix();
translate(p3.x*1.5, p3.y*1.5, p3.z);
rotate((i+0.5)*2*PI/7+PI/2);
fill(colors[9], 200);
textAlign(CENTER, BOTTOM);
textFont(fonts[0], 48);
text(sevenDeadlySins[i], 0, 0, 0);
popMatrix();
}
drawYearLabel();
}
void drawYearLabel() {
for (int year = 1989; year<2015; year++) {
float z = (year - interpolatorYear) * panelDistance;
pushMatrix();
translate(0, 0, z+2);
rotateY(-PI/2);
fill(colors[9], 200);
textFont(fonts[0], 10);
textAlign(LEFT);
if (year== 1990)
text("<"+str(year+1), 0, 0, 0);
else if (year == 1989) {
textFont(fonts[0], 20);
text("Publish Year", -textWidth("Publish Year"), 0, 0);
textFont(fonts[0], 10);
} else
text(str(year), 0, 0, 0);
popMatrix();
}
}
6.controller.pde
Code: Select all
void controllerSetup() {
cp5 = new ControlP5(this);
ControlFont labelFont = new ControlFont(fonts[0], 16);
ControlFont valueFont = new ControlFont(fonts[1], 12);
l = cp5.addListBox("myList")
.setPosition(width/16, height/2 + 150)
.setSize(width/4-width/16, 3*height/8 - 150)
.setItemHeight(15)
.setBarHeight(15)
.setColorBackground(colors[8])
.setColorActive(colors[9])
.setColorForeground(0xFFEB6002)
;
l.getCaptionLabel().toUpperCase(true);
l.getCaptionLabel().set("Item Title");
l.getCaptionLabel().setColor(colors[9]);
l.getCaptionLabel().setFont(labelFont);
l.getCaptionLabel().getStyle().marginTop= 5;
l.getCaptionLabel().getStyle().marginLeft= -5;
l.getValueLabel().setColor(colors[9]);
l.getValueLabel().setFont(valueFont);
l.getValueLabel().getStyle().marginTop= 5;
l.getValueLabel().getStyle().marginLeft= -5;
for (int i=0; i<rowNums; i++) {
String title = nf(i, 3)+" "+ dataTable.getRow(i).getString(0);
int maxLength = int((width/4-width/16)/7);
if (title.length()>maxLength) title= title.substring(0, maxLength);
l.addItem(title, i);
CColor c = new CColor();
//c.setBackground(0x14141400);
c.setBackground(colors[8]);
//println(c);
l.getItem(title).put("color", c);
//println(l.getItem(title));
}
cp5.setAutoDraw(false);
// Initialize four buttons
//
float buttonSpacing = width/16;
float buttonwidth = width/16*7/8;
float buttonHeight = 5;
float pressedColor = 90;
float hoverColor = 128;
float onColor = 180;
float offColor = 40;
rectButtons = new RectButton[4];
rectButtons[0] = new RectButton(width/2-1.5*buttonSpacing, height*15/16-buttonHeight, buttonwidth, buttonHeight, true, color(pressedColor), color(hoverColor), color(onColor), color(offColor));
rectButtons[1] = new RectButton(width/2-0.5*buttonSpacing, height*15/16-buttonHeight, buttonwidth, buttonHeight, autoRotate, color(pressedColor), color(hoverColor), color(onColor), color(offColor));
rectButtons[2] = new RectButton(width/2+0.5*buttonSpacing, height*15/16-buttonHeight, buttonwidth, buttonHeight, blackOrWhite, color(pressedColor), color(hoverColor), color(onColor), color(offColor));
rectButtons[3] = new RectButton(width/2+1.5*buttonSpacing, height*15/16-buttonHeight, buttonwidth, buttonHeight, showGUI, color(pressedColor), color(hoverColor), color(onColor), color(offColor));
}
void gui() {
cp5.draw();
float horizonalLocation = width/16;
float verticalLocation = height/2;
float dist = 25;
fill(128, 30);
noStroke();
rectMode(CORNER);
//rect(width/32, height/16, width/4, height/8);
rect(width/32, height/2-height/16, width/4, height/2);
boolean isMouseInX = (mouseX > width/32) && (mouseX<(width/4+width/32));
boolean isMouseInY = (mouseY > (height/2-height/16)) && (mouseY <(height-height/16));
boolean isMouseIn = isMouseInX && isMouseInY;
if (isMouseIn||interactWithSin||interactWithButtons) cam.setActive(false);
else cam.setActive(true);
// -------------------------------------------------------------- //
// Big Title
//
fill(colors[9], 200);
textFont(fonts[1], 16);
textAlign(LEFT, BOTTOM);
text("The 7 prevalently checked out items of", horizonalLocation, height/8-20);
textFont(fonts[0], 48);
textAlign(LEFT, TOP);
text("7 DEADLY SINS", horizonalLocation, height/8);
// -------------------------------------------------------------- //
// Present year label
// (1). judgement
//
mouseOnPresentYear = between(mouseY, verticalLocation, verticalLocation+40) &&
between(mouseX, horizonalLocation, horizonalLocation + textWidth(str(presentYear)));
// (2). draw text
//
textAlign(LEFT, TOP);
if (mouseOnPresentYear) {
if (presentYear == 1990) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
textFont(fonts[0], 24);
text("BEFORE ", horizonalLocation, verticalLocation-40);
textFont(fonts[0], 48);
text(str(presentYear+1), horizonalLocation, verticalLocation);
} else if (presentYear > 1990 && presentYear < 2015) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
textFont(fonts[0], 48);
text(str(presentYear), horizonalLocation, verticalLocation);
} else {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
textFont(fonts[0], 48);
text(str(presentYear), horizonalLocation, verticalLocation);
}
} else {
if (presentYear == 1990) {
fill(colors[9], 200);
textFont(fonts[0], 24);
text("BEFORE ", horizonalLocation, verticalLocation-40);
textFont(fonts[0], 48);
text(str(presentYear+1), horizonalLocation, verticalLocation);
} else if (presentYear > 1990 && presentYear < 2015) {
fill(colors[9], 200);
textFont(fonts[0], 48);
text(str(presentYear), horizonalLocation, verticalLocation);
} else {
fill(colors[9], 40);
textFont(fonts[0], 48);
text(str(presentYear), horizonalLocation, verticalLocation);
}
}
// -------------------------------------------------------------- //
// Up and down Triangles for adjusting present year
// (1). intitialization
//
PVector[] upTrianglePoints = new PVector[3];
PVector[] downTrianglePoints = new PVector[3];
upTrianglePoints[0] = new PVector(horizonalLocation + textWidth(str(presentYear)) + 10, verticalLocation-6);
upTrianglePoints[1] = new PVector(horizonalLocation + textWidth(str(presentYear)) + 5, verticalLocation+10);
upTrianglePoints[2] = new PVector(horizonalLocation + textWidth(str(presentYear)) + 15, verticalLocation+10);
downTrianglePoints[0] = new PVector(horizonalLocation + textWidth(str(presentYear)) + 10, verticalLocation+24+6);
downTrianglePoints[1] = new PVector(horizonalLocation + textWidth(str(presentYear)) + 5, verticalLocation+24-10);
downTrianglePoints[2] = new PVector(horizonalLocation + textWidth(str(presentYear)) + 15, verticalLocation+24-10);
// (2). judgement
//
mouseOnUpTriangle = false;
if (between(mouseY, upTrianglePoints[0].y, upTrianglePoints[1].y)) {
float heightRatio = (mouseY - upTrianglePoints[0].y) / (upTrianglePoints[1].y-upTrianglePoints[0].y);
PVector p1 = PVector.lerp(upTrianglePoints[0], upTrianglePoints[1], heightRatio);
PVector p2 = PVector.lerp(upTrianglePoints[0], upTrianglePoints[2], heightRatio);
if (between(mouseX, p1.x, p2.x)) {
mouseOnUpTriangle = true;
}
}
mouseOnDownTriangle = false;
if (between(mouseY, downTrianglePoints[0].y, downTrianglePoints[1].y)) {
float heightRatio = (mouseY - downTrianglePoints[0].y) / (downTrianglePoints[1].y-downTrianglePoints[0].y);
PVector p1 = PVector.lerp(downTrianglePoints[0], downTrianglePoints[1], heightRatio);
PVector p2 = PVector.lerp(downTrianglePoints[0], downTrianglePoints[2], heightRatio);
if (between(mouseX, p1.x, p2.x)) {
mouseOnDownTriangle = true;
}
}
// (3). draw triagnles
//
if (mouseOnUpTriangle) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
} else fill(colors[9], 200);
triangle(upTrianglePoints[0].x, upTrianglePoints[0].y, upTrianglePoints[1].x, upTrianglePoints[1].y, upTrianglePoints[2].x, upTrianglePoints[2].y);
if (mouseOnDownTriangle) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
} else fill(colors[9], 200);
triangle(downTrianglePoints[0].x, downTrianglePoints[0].y, downTrianglePoints[1].x, downTrianglePoints[1].y, downTrianglePoints[2].x, downTrianglePoints[2].y);
// -------------------------------------------------------------- //
// The three item types
// 1. judgement
//
mouseOnBook = false;
mouseOnAudio = false;
mouseOnVideo = false;
if (between(mouseX, horizonalLocation, (horizonalLocation+textWidth("Video"))) && between(mouseY, (verticalLocation + 50), (verticalLocation + dist*3 + 50))) {
if (between (mouseY, (verticalLocation + 50), (verticalLocation + 50+dist))) mouseOnBook = true;
if (between (mouseY, (verticalLocation + dist + 50), (verticalLocation + dist*2 + 50))) mouseOnVideo = true;
if (between (mouseY, (verticalLocation + dist*2 + 50), (verticalLocation + dist*3 + 50))) mouseOnAudio = true;
}
// 2. draw text
//
textFont(fonts[0], 24);
if (mouseOnBook) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
} else {
if (showBook) fill(colors[9], 200);
else fill(colors[9], 40);
}
text("Book", horizonalLocation, verticalLocation + 50);
if (mouseOnVideo) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
} else {
if (showVideo) fill(colors[9], 200);
else fill(colors[9], 40);
}
text("Video", horizonalLocation, verticalLocation + dist + 50);
if (mouseOnAudio) {
if (mousePressed) fill(colors[9], 200);
else fill(colors[9], 120);
} else {
if (showAudio) fill(colors[9], 200);
else fill(colors[9], 40);
}
text("Audio", horizonalLocation, verticalLocation + dist*2 + 50);
// -------------------------------------------------------------- //
// Sevent lines representing seven deadly sins
// (Mouse location judgement is moved into Interaction.pde)
// (1). seven lines
//
pushMatrix();
translate(width*7/8, height*13/16);
beginShape(LINES);
for (int i = 0; i< 7; i++) {
PVector p1 = PVector.lerp(Points[i], Points[(i+1)%7], 0.1);
PVector p2 = PVector.lerp(Points[i], Points[(i+1)%7], 0.9);
//println(p1);
strokeWeight(3);
if (mouseOnSins[i]) {
if (mousePressed) stroke(colors[i], 255);
else stroke(colors[i], 100);
} else {
if (showTheSin[i]) stroke(colors[i], 255);
else stroke(colors[i], 20);
}
vertex(p1.x/5, p1.y/5, 0);
vertex(p2.x/5, p2.y/5, 0);
}
endShape();
// (2). seven text labels
//
for (int i = 0; i< 7; i++) {
PVector p3 = PVector.lerp(Points[i], Points[(i+1)%7], 0.5);
pushMatrix();
translate(p3.x/5*1.1, p3.y/5*1.1);
rotate((i+0.5)*2*PI/7+PI/2);
fill(colors[9], 200);
textAlign(CENTER, BOTTOM);
textFont(fonts[0], 12);
text(sevenDeadlySins[i], 0, 0);
popMatrix();
}
popMatrix();
}
void drawButtons() {
rectButtons[1].setStatus(autoRotate);
rectButtons[2].setStatus(blackOrWhite);
rectButtons[3].setStatus(showGUI);
rectButtons[0].draw();
rectButtons[1].draw();
rectButtons[2].draw();
rectButtons[3].draw();
textAlign(CENTER, BOTTOM);
textFont(fonts[1], 12);
fill(rectButtons[0].hoverColor);
text("viewmode", rectButtons[0].x, rectButtons[0].y-10);
text("auto rotate", rectButtons[1].x, rectButtons[1].y-10);
text("black/white", rectButtons[2].x, rectButtons[2].y-10);
text("interface", rectButtons[3].x, rectButtons[3].y-10);
}
Zip:
----------- EDITED ------------
I was thinking that the modern culture get teenagers exposed too much unhealthy stuff, such as violence and pornography. It reminded me of the seven deadly sins. I was worried that getting those children exposed to the words of seven deadly sins may have a negative effects. So I tried to find out answers to these questions.
1. Which word of sin appears in most items?
2. Which item is checked out for most times among all the items related to one sin?
3. What is the relationship between the publish year and the publications' checkout counts?
Code: Select all
select distinct
title.title,
(CASE
WHEN substring(itemType.itemType,3,3) = "bk" THEN "Book"
WHEN substring(itemType.itemType,3,3) = "cas" THEN "Audio"
WHEN substring(itemType.itemType,3,3) = "cd" THEN "Audio"
WHEN substring(itemType.itemType,3,3) = "vhs" THEN "Video"
WHEN substring(itemType.itemType,3,3) = "dvd" THEN "Video"
ELSE "OTHERS"
END) as MyType,
(CASE
WHEN keyword.keyword ='Lust' THEN "Lust"
WHEN keyword.keyword ='Gluttony' THEN "Gluttony"
WHEN keyword.keyword ='Greed' THEN "Greed"
WHEN keyword.keyword ='Sloth' THEN "Sloth"
WHEN keyword.keyword ='Wrath' THEN "Wrath"
WHEN keyword.keyword ='Envy' THEN "Envy"
WHEN keyword.keyword ='Pride' THEN "Pride"
ELSE "OTHERS"
END) as MykeyWord,
substring(callNumber,-4) as PubYear,
COUNT(_transactionsExploded.bibNumber) as Counts
from
itemToBib, itemType, keyword, callNumber, title, _transactionsExploded
where
itemToBib.itemNumber = itemType.itemNumber
AND itemToBib.itemNumber = callNumber.itemNumber
AND itemToBib.bibNumber = keyword.bibNumber
AND itemToBib.bibNumber = title.bibNumber
AND itemToBib.bibNumber = _transactionsExploded.bibNumber
AND itemType.itemNumber = itemToBib.itemNumber
AND (keyword.keyword = "Lust"
or keyword.keyword = "Gluttony"
or keyword.keyword = "Greed"
or keyword.keyword = "Sloth"
or keyword.keyword = "Wrath"
or keyword.keyword = "Envy"
or keyword.keyword = "Pride")
AND Cast(substring(callNumber,-4) as UNSIGNED) > 1900
AND Cast(substring(callNumber,-4) as UNSIGNED) < 2015
Group by itemToBib.bibNumber
Through he query code above, I got the query result as below:
- The query result
This table includes each item's publish year, type, the keyword of deadly sin it contains and the total checkout counts. This is a 480+ rows table, which means we have 480+ items to present.
To present it clearly, I adapted my model from a 2D model named "Time Wheel" (described as below and here:
http://survey.timeviz.net).
- time wheel
- time wheel details
Each arrow in this image represents a value of each category. The lowest value is at the end of the arrow, and the highest value is at the head of the arrow. The horizontal arrow in the center represent the axis of time, which grows from the left to the right. By connecting the time and the values with lines, the visualization was made.
I adapted it to a 3-dimentional one. I transformed the time axis from left-to-right to inside-to-outside.
- Sketches
Similarly, I use lines to represent items. So the value in each arrow is representing the total checkout times of the item, the time axis representing each item's publish year.
Therefore, the main frame of my visualization consists of 2 parts, the time axis and the 7 lines of the sins.
The time axis represents the publish years, ranging from 1990 to 2015.
Each line of a sin ranges from the maximum and minimum of the cumulative checkout counts of all items related to this sin. That why you can see the two number labels on the two sides of each line.
- Still Frame
When putting all the data into this frame, it looks like this:
- Overview
- Overview 2
Let me explain it step by step:
1. A single item can be represent by a line, which connected the time axis and one specific point on a sin's line.
The position of connecting point on the sin's line is determined by how big the checkout count of this item is, relative to the maximum checkout count of this sin.
Additionally, the lines for representing items differs from each other by their stroke weight, color transparency and saturation, which are also determined by how big the checkout count of this item is relative to the maximum checkout count of this sin.
- When single item is chosen
- When single item is chosen 2
2.When put all items published at a selected year into this frame. It looks like this.
This actually shows the distribution of items published at a specific year, which can be set by the user. The title and checkout counts of those items can be known from this perspective.
- All items published at selected year
- All items published at selected year 2
This is actually one of my display mode, in which data of years other than selected year are nearly invisible, in order to emphasize the selected year's data. Using +/- can change the year you want to look into.
3.When put all items that are related to a specific sin into this frame. It looks like this.
This shows the distribution of items related to a specific sin, which can be set by the user, too. We can know how popular each year's publications related to this sin are.
- All items related to the selected sin
- All items related to the selected sin
This is actually the other one display mode, in which data of sins other than the selected sin are nearly invisible, in order to emphasize the selected sin's data. Using 4 or 5 can change the sin you want to look into.
4.When put all together, the complete project is displayed as below.
- Put all together
- Put all together 2
Main interactions:
1. "+" and "-" to change the selected year to be displayed.
2. "4" and "5" to change the sin to be displayed.
Other interactions:
1. "B","A" and "V" are used to switch on and off the display of book items, audio items and video items separately.
2. "1","2" and "3" are three viewing perspectives.
3. "F" is to switch on and off the freedom of rotation. (After changing the viewing perspective, the freedom of rotation is automatically switched off)
4. "T" is to switch on and off the slowly self-rotation.
5. "H" is to hide or unhide the interface.
6. "C" is to clear the selected title.
7. "S" is to save screen shot.
8. "P" is to hide or unhide the data, which lets you see the still frame.
Future work:
1. I need to find a way eliminate the ambiguous items. Currently, the judgement of whether a item is related to a sin is merely based on keyword. It's ambiguous sometime. For example, sloth is a sin, but a kind of animal at the same time. Thus, the book like "Upside Down Sloth" that is about the animal should be eliminated, which is still in my data.
2. Some enhancements about interaction. The "4" or "5" to change the selected sin is not straightforward. I think clicking on the edges of the left-below 7-edge pentagon is better.
The processing code:
1.DataVis_3dVisualization.pde
Code: Select all
import controlP5.*;
import peasy.*;
import processing.opengl.*;
PeasyCam cam;
ControlP5 cp5;
ListBox l;
// Data parameters
Table dataTable;
int rowNums, colNums;
int[] MaxCheckoutCount, MinCheckoutCount;
String[] sevenDeadlySins;
// Drawing setting
float overallRadius, panelDistance;
PVector[] Points;
int presentYear;
float interpolatorYear;
Integrator[] interpolator;
float rotateX, rotateY, rotateZ;
// Color & fonts
color[] colors;
PFont[] fonts;
// Control parameters
int viewMode, whichSinToSee;
boolean showOtherYear, showBook, showVideo, showAudio, rotateFree, autoRotate, showGUI, onlyStillFrame;
boolean[] titleSelected;
void setup() {
fullScreen(P3D);
//size(800, 800, P3D);
cam = new PeasyCam(this, 250);
cam.setWheelScale(0.1);
cam.setDistance(1600);
//Initialization for data
//
dataTable = loadTable("query_result.csv", "header");
rowNums = dataTable.getRowCount();
colNums = dataTable.getColumnCount();
MaxCheckoutCount = new int[7];
MinCheckoutCount = new int[7];
for (int i = 0; i< 7; i++) {
MaxCheckoutCount[i] = 0;
MinCheckoutCount[i] = 100000;
}
for (int i = 0; i<rowNums; i++) {
TableRow row = dataTable.getRow(i);
int index = whichDeadlySin(row.getString(2));
int checkoutCount = row.getInt(4);
if (checkoutCount > MaxCheckoutCount[index]) MaxCheckoutCount[index] = checkoutCount;
if (checkoutCount < MinCheckoutCount[index]) MinCheckoutCount[index] = checkoutCount;
}
sevenDeadlySins = new String[7];
sevenDeadlySins[0] = "Lust";
sevenDeadlySins[1] = "Gluttony";
sevenDeadlySins[2] = "Greed";
sevenDeadlySins[3] = "Wrath";
sevenDeadlySins[4] = "Sloth";
sevenDeadlySins[5] = "Envy";
sevenDeadlySins[6] = "Pride";
// Initializaiton for drawing
//
rotateX = 0;
rotateY = 0;
rotateZ = 0;
viewMode = 0;
showOtherYear = true;
showBook = true;
showVideo = true;
showAudio = true;
rotateFree = true;
showGUI = true;
autoRotate = false;
onlyStillFrame = false;
titleSelected = new boolean[rowNums];
for (int i = 0; i<rowNums; i++) {
titleSelected[i] = false;
}
overallRadius = 400;
panelDistance = 60;
presentYear = 1990;
interpolator = new Integrator[4];
interpolator[0] = new Integrator(presentYear, 0.4, 0.08);
interpolator[1] = new Integrator(rotateX, 0.4, 0.16);
interpolator[2] = new Integrator(rotateY, 0.4, 0.16);
interpolator[3] = new Integrator(rotateZ, 0.4, 0.16);
Points = new PVector[7];
for (int i = 0; i<7; i++) {
float x, y;
x = overallRadius*cos(i*2*PI/7);
y = overallRadius*sin(i*2*PI/7);
Points[i] = new PVector(x, y, 0);
}
whichSinToSee = 0;
// Initialize colors & fonts
//
colors = new color[10];
colors[0] = #0A86D0;
colors[1] = #134ED5;
colors[2] = #6D5CAF;
colors[3] = #F2000D;
colors[4] = #EB6002;
colors[5] = #FFBC00;
colors[6] = #019C69;
colors[7] = #D42020;
colors[8] = #FFFFFF;
colors[9] = #000000;
fonts = new PFont[3];
fonts[0] = loadFont("OratorStd-48.vlw");
fonts[1] = loadFont("OratorStd-12.vlw");
controllerSetup();
noLights();
smooth();
}
void draw() {
background(255);
for (int i = 0; i < interpolator.length; i++) {
interpolator[i].update();
}
interpolatorYear = interpolator[0].value;
if (!rotateFree)
cam.setRotations(interpolator[1].value, interpolator[2].value, interpolator[3].value);
else
setRotationInterpolator(cam.getRotations()[0], cam.getRotations()[1], cam.getRotations()[2]);
for (int i = 0; i<rowNums; i++) {
if ((l.getItem(i).get("state").toString().equals("true"))) {
titleSelected[i] = true;
} else
titleSelected[i] = false;
}
drawStillFrame();
if (!onlyStillFrame) {
for (int i= 0; i<rowNums; i++) {
drawTableRow(i);
}
}
if (autoRotate) {
if (viewMode == 2)cam.rotateX(0.002);
if (viewMode == 3)cam.rotateY(0.002);
}
if (showGUI) gui();
}
void drawTableRow(int i) {
TableRow row = dataTable.getRow(i);
String type = row.getString(1);
String theSin = row.getString(2);
int year = row.getInt(3);
if (year<1990) year = 1990;
int index = whichDeadlySin(theSin);
// Judgement about itemType
// If selected, then show.
//
boolean shouldShow = false;
if (type.equals("Book") && showBook) shouldShow = true;
if (type.equals("Video") && showVideo) shouldShow = true;
if (type.equals("Audio") && showAudio) shouldShow = true;
if (shouldShow) {
float percent = sqrt(sqrt(map(row.getFloat(4), MinCheckoutCount[index], MaxCheckoutCount[index], 0, 1)));
float percent1 = map(percent, 0, 1, 0.1, 0.9);
PVector p = PVector.lerp(Points[index], Points[(index+1)%7], percent1);
float z = (year - interpolatorYear) * panelDistance;
beginShape(LINES);
colorMode(HSB, 255);
color theColor = color(hue(colors[index]), saturation(colors[index])*(percent*0.3+0.7), brightness(colors[index]));
// Judgement for each row's diplay mode
//
int mode = 0;
if (whichSinToSee == (index+1) || whichSinToSee == 0)
mode = 2;
else
mode = 4;
if (titleSelected[i]==true) mode = 3;
else {
if ( mode != 4) {
if (presentYear == year) mode = 1;
else {
if (showOtherYear ) mode = 2;
else mode =4;
}
}
}
// Display according to its diplay mode
//
switch (mode) {
case 1:
// #1 YearSelected
stroke(theColor, 100);
strokeWeight(1);
vertex(p.x, p.y, p.z);
stroke(theColor, 250);
strokeWeight(max(10*percent, 1));
vertex(0, 0, z);
break;
case 2:
// #2 Visible
stroke(theColor, 100*(percent*0.7+0.3));
strokeWeight(1);
vertex(p.x, p.y, p.z);
stroke(theColor, 250*(percent*0.7+0.3));
strokeWeight(max(10*percent, 1));
vertex(0, 0, z);
break;
case 3:
// #3 TitleSelected
stroke(colors[9], 100);
strokeWeight(3);
vertex(p.x, p.y, p.z);
stroke(colors[9], 250);
strokeWeight(max(10*percent, 3));
vertex(0, 0, z);
break;
case 4 :
// #4 Invisible
stroke(hue(colors[index]), saturation(colors[index])*0.1, brightness(colors[index])*0.1, 15);
strokeWeight(0.5);
vertex(p.x, p.y, p.z);
strokeWeight(0.5);
vertex(0, 0, z);
}
endShape();
// Show titles of items published at selected year
//
if ((year == presentYear && mode != 4 )|| titleSelected[i]) {
pushMatrix();
translate(p.x, p.y, p.z);
rotateY(PI/2);
rotateX(((7-index)+0.5)*2*PI/7 - PI/4);
rotateZ(PI);
fill(colors[9], 200);
textFont(fonts[0], 8);
textAlign(LEFT);
String title = row.getString(0);
text(str(row.getInt(4))+" "+title, 0, 0, 0);
popMatrix();
}
}
}
// A mapping between index and sins
//
int whichDeadlySin(String theSin) {
int index=0;
if (theSin.equals("Lust")) index = 0;
if (theSin.equals("Gluttony")) index = 1;
if (theSin.equals("Greed")) index = 2;
if (theSin.equals("Wrath")) index = 3;
if (theSin.equals("Sloth")) index = 4;
if (theSin.equals("Envy")) index = 5;
if (theSin.equals("Pride")) index = 6;
return index;
}
2.Integrator.pde
Code: Select all
/*
Original Author: Ben Fry
From book Visualizing Data
*/
class Integrator {
final float DAMPING = 0.5f;
final float ATTRACTION = 0.2f;
float value;
float vel;
float accel;
float force;
float mass = 1;
float damping = DAMPING;
float attraction = ATTRACTION;
boolean targeting;
float target;
Integrator() { }
Integrator(float value) {
this.value = value;
}
Integrator(float value, float damping, float attraction) {
this.value = value;
this.damping = damping;
this.attraction = attraction;
}
void set(float v) {
value = v;
}
void update() {
if (targeting) {
force += attraction * (target - value);
}
accel = force / mass;
vel = (vel + accel) * damping;
value += vel;
force = 0;
}
void target(float t) {
targeting = true;
target = t;
}
void noTarget() {
targeting = false;
}
}
3.controller.pde
Code: Select all
void controllerSetup() {
cp5 = new ControlP5(this);
ControlFont labelFont = new ControlFont(fonts[0], 16);
ControlFont valueFont = new ControlFont(fonts[1], 12);
l = cp5.addListBox("myList")
.setPosition(width/16, height/2 + 150)
.setSize(width/4-width/16, 3*height/8 - 150)
.setItemHeight(15)
.setBarHeight(15)
.setColorBackground(0xFFFFFFFF)
.setColorActive(color(0))
.setColorForeground(0xFFEB6002)
;
l.getCaptionLabel().toUpperCase(true);
l.getCaptionLabel().set("Item Title");
l.getCaptionLabel().setColor(colors[9]);
l.getCaptionLabel().setFont(labelFont);
l.getCaptionLabel().getStyle().marginTop= 5;
l.getCaptionLabel().getStyle().marginLeft= -5;
l.getValueLabel().setColor(colors[9]);
l.getValueLabel().setFont(valueFont);
l.getValueLabel().getStyle().marginTop= 5;
l.getValueLabel().getStyle().marginLeft= -5;
for (int i=0; i<rowNums; i++) {
String title = nf(i, 3)+" "+ dataTable.getRow(i).getString(0);
int maxLength = int((width/4-width/16)/7);
if (title.length()>maxLength) title= title.substring(0, maxLength);
l.addItem(title, i);
CColor c = new CColor();
//c.setBackground(0x14141400);
c.setBackground(0xFFFFFFFF);
//println(c);
l.getItem(title).put("color", c);
println(l.getItem(title));
}
cp5.setAutoDraw(false);
}
void controlEvent(ControlEvent theEvent) {
//println(theEvent.getController().getId());
}
void gui() {
hint(DISABLE_DEPTH_TEST);
cam.beginHUD();
cp5.draw();
float horizonalLocation = width/16;
float verticalLocation = height/2;
float dist = 25;
fill(0, 15);
noStroke();
//rect(width/32, height/16, width/4, height/8);
rect(width/32, height/2-height/16, width/4, height/2);
boolean isMouseInX = (mouseX > width/32) && (mouseX<(width/4+width/32));
boolean isMouseInY = (mouseY > (height/2-height/16)) && (mouseY <(height-height/16));
boolean isMouseIn = isMouseInX && isMouseInY;
if (isMouseIn)cam.setActive(false);
else cam.setActive(true);
fill(colors[9], 200);
textFont(fonts[1], 16);
textAlign(LEFT, BOTTOM);
text("The 7 prevalently checked out items of", horizonalLocation, height/8-20);
textFont(fonts[0], 48);
textAlign(LEFT, TOP);
text("7 DEADLY SINS", horizonalLocation, height/8);
textAlign(LEFT, TOP);
if (presentYear == 1990) {
fill(colors[9], 200);
textFont(fonts[0], 24);
text("BEFORE ", horizonalLocation, verticalLocation-40);
textFont(fonts[0], 48);
text(str(presentYear+1), horizonalLocation, verticalLocation);
} else if (presentYear > 1990 && presentYear < 2015) {
fill(colors[9], 200);
textFont(fonts[0], 48);
text(str(presentYear), horizonalLocation, verticalLocation);
} else {
fill(50, 200);
textFont(fonts[0], 48);
text(str(presentYear), horizonalLocation, verticalLocation);
}
textFont(fonts[0], 24);
if (showBook) fill(colors[9], 200);
else fill(100, 200);
text("Book", horizonalLocation, verticalLocation + 50);
if (showVideo) fill(colors[9], 200);
else fill(100, 200);
text("Video", horizonalLocation, verticalLocation + dist + 50);
if (showAudio) fill(colors[9], 200);
else fill(100, 200);
text("Audio", horizonalLocation, verticalLocation + dist*2 + 50);
pushMatrix();
translate(width*7/8,height*13/16);
beginShape(LINES);
for (int i = 0; i< 7; i++) {
PVector p1 = PVector.lerp(Points[i], Points[(i+1)%7], 0.1);
PVector p2 = PVector.lerp(Points[i], Points[(i+1)%7], 0.9);
//println(p1);
strokeWeight(3);
if (i==whichSinToSee-1 || whichSinToSee ==0) {
stroke(colors[i], 255);
} else {
stroke(colors[i], 20);
}
vertex(p1.x/5, p1.y/5, 0);
vertex(p2.x/5, p2.y/5, 0);
}
endShape();
for (int i = 0; i< 7; i++) {
PVector p3 = PVector.lerp(Points[i], Points[(i+1)%7], 0.5);
pushMatrix();
translate(p3.x/5*1.1, p3.y/5*1.1);
rotate((i+0.5)*2*PI/7+PI/2);
fill(colors[9], 200);
textAlign(CENTER, BOTTOM);
textFont(fonts[0], 12);
text(sevenDeadlySins[i], 0, 0);
popMatrix();
}
popMatrix();
cam.endHUD();
hint(ENABLE_DEPTH_TEST);
}
4.Interaction.pde
Code: Select all
void keyPressed() {
if (key == 'S' || key == 's') {
saveFrame("######.jpg");
}
if (key == '=' || key =='+') {
presentYear++;
interpolator[0].target(presentYear);
}
if (key == '-' || key =='_') {
presentYear--;
interpolator[0].target(presentYear);
}
if (key == 'F' || key =='f') {
rotateFree = !rotateFree;
}
if (key == 'T' || key == 't') {
autoRotate = !autoRotate;
}
if (key == '1') {
viewMode = 1;
cam.setDistance(1600);
setRotationInterpolator(0, 0, 0);
}
if (key == '2') {
viewMode = 2;
setRotationInterpolator(0, -PI/2, 0);
rotateFree = false;
}
if (key == '3') {
viewMode = 3;
setRotationInterpolator(-PI/2, -PI/2, 0);
rotateFree = false;
}
if (key == '4') {
whichSinToSee++;
whichSinToSee = whichSinToSee % 8;
}
if (key == '5') {
if (whichSinToSee == 0) whichSinToSee+=7;
whichSinToSee--;
}
if (key == 'B' || key =='b') {
showBook = !showBook;
}
if (key == 'V' || key =='v') {
showVideo = !showVideo;
}
if (key == 'A' || key =='a') {
showAudio = !showAudio;
}
if (key == 'Y' || key =='y') {
showOtherYear = !showOtherYear;
}
if (key == 'H' || key == 'h') {
showGUI = !showGUI;
}
if (key == 'C' || key == 'c') {
for (int i =0; i<rowNums; i++) {
l.getItem(i).put("state", false);
}
}
if (key == 'P' || key == 'p') {
onlyStillFrame = !onlyStillFrame;
}
}
void setRotationInterpolator(float rx, float ry, float rz) {
interpolator[1].set(cam.getRotations()[0]);
interpolator[2].set(cam.getRotations()[1]);
interpolator[3].set(cam.getRotations()[2]);
interpolator[1].target(rx);
interpolator[2].target(ry);
interpolator[3].target(rz);
}
5.StillFrame.pde
Code: Select all
void drawStillFrame() {
for (int i = 0; i< 7; i++) {
// bar
PVector p1 = PVector.lerp(Points[i], Points[(i+1)%7], 0.1);
PVector p2 = PVector.lerp(Points[i], Points[(i+1)%7], 0.9);
//println(p1);
beginShape(LINES);
strokeWeight(3);
stroke(colors[i], 20);
vertex(p1.x, p1.y, p1.z);
strokeWeight(3);
if (i==whichSinToSee-1 || whichSinToSee ==0) {
stroke(colors[i], 200);
} else {
stroke(colors[i], 20);
}
vertex(p2.x, p2.y, p2.z);
endShape();
// count label
pushMatrix();
translate(p2.x, p2.y, p2.z);
rotateZ((2*i+1)*PI/7+PI);
textAlign(RIGHT, TOP);
textFont(fonts[0], 20);
text(str(MaxCheckoutCount[i]), -10, 0, 0);
popMatrix();
pushMatrix();
translate(p1.x, p1.y, p1.z);
rotateZ((2*i+1)*PI/7+PI);
textAlign(RIGHT, BOTTOM);
text(str(MinCheckoutCount[i]), -5, 0, 0);
popMatrix();
}
beginShape(LINES);
for (int i = 0; i< 7; i++) {
PVector p1 = PVector.lerp(Points[i], Points[(i+1)%7], 0.1);
PVector p2 = PVector.lerp(Points[i], Points[(i+1)%7], 0.9);
//println(p1);
strokeWeight(3);
if (i==whichSinToSee-1 || whichSinToSee ==0) {
stroke(colors[i], 255);
} else {
stroke(colors[i], 20);
}
vertex(p1.x*2, p1.y*2, p1.z);
vertex(p2.x*2, p2.y*2, p2.z);
}
endShape();
for (int i = 0; i< 7; i++) {
PVector p3 = PVector.lerp(Points[i], Points[(i+1)%7], 0.5);
pushMatrix();
translate(p3.x*1.5, p3.y*1.5, p3.z);
rotate((i+0.5)*2*PI/7+PI/2);
fill(colors[9], 200);
textAlign(CENTER, BOTTOM);
textFont(fonts[0], 48);
text(sevenDeadlySins[i], 0, 0, 0);
popMatrix();
}
drawYearLabel();
}
void drawYearLabel() {
for (int year = 1989; year<2015; year++) {
float z = (year - interpolatorYear) * panelDistance;
pushMatrix();
translate(0, 0, z+2);
rotateY(-PI/2);
fill(colors[9], 200);
textFont(fonts[0], 10);
textAlign(LEFT);
if (year== 1990)
text("<"+str(year+1), 0, 0, 0);
else if (year == 1989) {
textFont(fonts[0], 20);
//textWidth("Publish Year");
text("Publish Year", -textWidth("Publish Year"), 0, 0);
textFont(fonts[0], 10);
} else
text(str(year), 0, 0, 0);
popMatrix();
}
}
The old source code: (
Please download the new one)
The new source code: