/*
* Daniel King
* gtg351x
* 901856535
*
* CS 4496 Project 4
* Professor Jarek Rossignac
*/
/**
* Made by:
* Daniel King
* gtg351x
* 901856535
*
* For:
* Jarek Rossignac's CS 4496 Course
* at Georgia Tech
*
* Go back for instructions and more information.
*/
import processing.opengl.*;
import java.util.ArrayList;
/* Filename of start image. */
String filenameA = "1.jpg";
/* Filename of final image. */
String filenameB = "2.jpg";
/* Current mouse position. */
pt mouse = new pt(0, 0, 0);
/* Mouse coordinates relative to the center of the window. */
pt mouseRel = new pt(0, 0, 0);
/* Coordinate of the mouse when last pressed. */
pt lastPressed = new pt(0, 0, 0);
/* Currently selected corner. */
int selectedCorner = 0;
/* True if a corner is selected and being dragged. */
boolean selected = false;
/* Flag for showing vertices. */
boolean showVertices = true;
/* Flag for showing edges. */
boolean showEdges = true;
/* Flag for showing control point numbers. */
boolean showPNumbers = false;
/* Size of the mesh grid. */
int size = 25;
/* Number of parts in the morph sequence. */
int numGrids = 4;
/* Morph sequence. */
Grid animation[] = new Grid[numGrids];
/* Currently Selected grid. */
Grid currentGrid;
/* True if animating the morph. */
boolean animate = false;
/* Time in the animation. */
int time = 0;
/* True if the animation is paused. */
boolean pause = false;
/* Font used for labels. */
PFont font;
/* Current editing mode. */
int currentMode = 0;
/* GUI BUTTONS AND LABELS. */
Button saveConstraints[] = new Button[9];
Label saveConstLabel;
Button loadConstraints[] = new Button[9];
Label loadConstLabel;
Button loadSequence[] = new Button[9];
Label loadSequenceLabel;
Button saveSequence[] = new Button[9];
Label saveSequenceLabel;
Button resetConstraints;
Button loadImages[] = new Button[9];
Label loadImageLabel;
Button resetTexture;
Button currentSegment[] = new Button[numGrids];
Label currentSegmentLabel;
Button modeButtons[] = new Button[8];
Label modeLabel;
Button showControls[] = new Button[3];
Label controlsLabel;
Scrollbar alphaBar;
Label alphaLabel;
Button smoothButton[] = new Button[3];
/* Alpha transparency amounts for each grid. */
int alpha[] = new int[numGrids];
/* Time for a single segment. */
int segmentTime = 20;
/* Current smooth mode. */
int smoothMode = 0;
/* List of shapes. */
ArrayList shapes;
/* Shape currently being created. */
ArrayList currentShape;
/* Number of times to smooth while editing. */
int editSmoothTimes = 1;
/* Number of times to smooth while animating. */
int animateSmoothTimes = 25;
/* Sets up the morpher. */
void setup() {
// Create an OpenGL window.
size(920, 700, OPENGL);
framerate(30);
font = loadFont("Courier-14.vlw");
setupGUI();
shapes = new ArrayList();
for (int i = 0; i < numGrids; i++) {
if (i < 2) {
animation[i] = new Grid(filenameA, 600, 700, 0, 0, 0, i);
}
else {
animation[i] = new Grid(filenameB, 600, 700, 0, 0, 0, i);
}
alpha[i] = 255;
}
currentGrid = animation[0];
}
/* Sets up the user interface. */
void setupGUI() {
int savePos = 30;
saveConstLabel = new Label(620, savePos, 0, black, "Save Constraints:");
for (int i = 0; i < saveConstraints.length; i++) {
saveConstraints[i] = new Button(20, 20, 630 + 29 * i, savePos + 5, 0, false, blue, yellow, lightblue, str(i + 1));
}
int loadPos = 80;
loadConstLabel = new Label(620, loadPos, 0, black, "Load Constraints:");
for (int i = 0; i < loadConstraints.length; i++) {
loadConstraints[i] = new Button(20, 20, 630 + 29 * i, loadPos + 5, 0, false, blue, yellow, lightblue, str(i + 1));
}
int resetPos = 110;
resetConstraints = new Button(185, 20, 630, resetPos, 0, false, blue, yellow, lightblue, "Reset Constraints");
int loadImagePos = 165;
loadImageLabel = new Label(620, loadImagePos, 0, black, "Load Image:");
for (int i = 0; i < loadImages.length; i++) {
loadImages[i] = new Button(20, 20, 630 + 29 * i, loadImagePos + 5, 0, false, blue, yellow, lightblue, str(i + 1));
}
int resetTextPos = 195;
resetTexture = new Button(245, 20, 630, resetTextPos, 0, false, blue, yellow, lightblue, "Set Texture Coordinates");
int curSegPos = 250;
currentSegmentLabel = new Label(620, curSegPos, 0, black, "Current Segment in Morph:");
for (int i = 0; i < currentSegment.length; i++) {
currentSegment[i] = new Button(20, 20, 630 + 29 * i, curSegPos + 5, 0, false, blue, yellow, lightblue, str(i + 1));
}
currentSegment[0].selected = true;
int alphaPos = 295;
alphaLabel = new Label(620, alphaPos, 0, black, "Segment Transparency:");
alphaBar = new Scrollbar(260, 20, 25, 630, alphaPos + 5, 0, 1.0, blue, yellow, lightblue);
int saveMorphPos = 340;
saveSequenceLabel = new Label(620, saveMorphPos, 0, black, "Save Morph Sequence:");
for (int i = 0; i < saveSequence.length; i++) {
saveSequence[i] = new Button(20, 20, 630 + 29 * i, saveMorphPos + 5, 0, false, blue, yellow, lightblue, str(i + 1));
}
int loadMorphPos = 395;
loadSequenceLabel = new Label(620, loadMorphPos, 0, black, "Load Morph Sequence:");
for (int i = 0; i < loadSequence.length; i++) {
loadSequence[i] = new Button(20, 20, 630 + 29 * i, loadMorphPos + 5, 0, false, blue, yellow, lightblue, str(i + 1));
}
int modePos = 450;
modeLabel = new Label(620, modePos, 0, black, "Mode:");
modeButtons[0] = new Button(163, 20, 630, modePos + 5, 0, true, blue, yellow, lightblue, "Define Features");
modeButtons[6] = new Button(101, 20, 798, modePos + 5, 0, false, blue, yellow, lightblue, "New Shape");
modeButtons[1] = new Button(163, 20, 630, modePos + 30, 0, false, blue, yellow, lightblue, "Delete Features");
modeButtons[7] = new Button(101, 20, 798, modePos + 30, 0, false, blue, yellow, lightblue, "Del Shape");
modeButtons[2] = new Button(153, 20, 630, modePos + 55, 0, false, blue, yellow, lightblue, "Morph Features");
modeButtons[3] = new Button(113, 20, 630, modePos + 80, 0, false, blue, yellow, lightblue, "Play Morph");
modeButtons[4] = new Button(60, 20, 748, modePos + 80, 0, false, blue, yellow, lightblue, "Pause");
modeButtons[5] = new Button(70, 20, 813, modePos + 80, 0, false, blue, yellow, lightblue, "Rewind");
int smoothPos = 560;
smoothButton[0] = new Button(100, 20, 630, smoothPos, 0, true, blue, yellow, lightblue, "Bilaplace");
smoothButton[1] = new Button(100, 20, 735, smoothPos, 0, false, blue, yellow, lightblue, "Tuck-Tuck");
smoothButton[2] = new Button(45, 20, 840, smoothPos, 0, false, blue, yellow, lightblue, "None");
int showPos = 620;
controlsLabel = new Label(620, showPos, 0, black, "Show:");
showControls[0] = new Button(155, 20, 630, showPos + 5, 0, true, blue, yellow, lightblue, "Control Points");
showControls[1] = new Button(155, 20, 630, showPos + 30, 0, true, blue, yellow, lightblue, "Triangle Edges");
showControls[2] = new Button(140, 20, 630, showPos + 55, 0, false, blue, yellow, lightblue, "Point Numbers");
}
/* Draws the user interface. */
void drawGUI() {
alpha[currentGrid.id] = (int)(alphaBar.update() * 255);
saveConstLabel.drawLabel();
for (int i = 0; i < saveConstraints.length; i++) {
saveConstraints[i].drawButton();
}
loadConstLabel.drawLabel();
for (int i = 0; i < loadConstraints.length; i++) {
loadConstraints[i].drawButton();
}
resetConstraints.drawButton();
loadImageLabel.drawLabel();
for (int i = 0; i < loadImages.length; i++) {
loadImages[i].drawButton();
}
alphaLabel.drawLabel();
alphaBar.drawScrollbar();
resetTexture.drawButton();
currentSegmentLabel.drawLabel();
for (int i = 0; i < currentSegment.length; i++) {
currentSegment[i].drawButton();
}
saveSequenceLabel.drawLabel();
for (int i = 0; i < saveSequence.length; i++) {
saveSequence[i].drawButton();
}
loadSequenceLabel.drawLabel();
for (int i = 0; i < loadSequence.length; i++) {
loadSequence[i].drawButton();
}
modeLabel.drawLabel();
for (int i = 0; i < modeButtons.length; i++) {
modeButtons[i].drawButton();
}
controlsLabel.drawLabel();
for (int i = 0; i < showControls.length; i++) {
showControls[i].drawButton();
}
for (int i = 0; i < smoothButton.length; i++) {
smoothButton[i].drawButton();
}
}
/* Handles mouse presses on the user interface. */
void pressedGUI() {
for (int i = 0; i < saveConstraints.length; i++) {
if (saveConstraints[i].mouseOver()) {
saveConstraints[i].selected = true;
}
}
for (int i = 0; i < loadConstraints.length; i++) {
if (loadConstraints[i].mouseOver()) {
loadConstraints[i].selected = true;
}
}
if (resetConstraints.mouseOver()) {
resetConstraints.selected = true;
}
for (int i = 0; i < loadImages.length; i++) {
if (loadImages[i].mouseOver()) {
loadImages[i].selected = true;
}
}
if (alphaBar.mouseOver()) {
alphaBar.selected = true;
}
if (resetTexture.mouseOver()) {
resetTexture.selected = true;
}
for (int i = 0; i < currentSegment.length; i++) {
if (currentSegment[i].mouseOver()) {
currentSegment[i].selected = true;
}
}
for (int i = 0; i < saveSequence.length; i++) {
if (saveSequence[i].mouseOver()) {
saveSequence[i].selected = true;
}
}
for (int i = 0; i < loadSequence.length; i++) {
if (loadSequence[i].mouseOver()) {
loadSequence[i].selected = true;
}
}
for (int i = 0; i < smoothButton.length; i++) {
if (smoothButton[i].mouseOver()) {
smoothButton[i].selected = true;
}
}
}
/* Handles mouse releases on the user interface. */
void mouseGUI() {
for (int i = 0; i < saveConstraints.length; i++) {
if (saveConstraints[i].mouseOver()) {
currentGrid.saveConstraints(str(i + 1) + ".m");
saveConstraints[i].selected = false;
}
}
for (int i = 0; i < loadConstraints.length; i++) {
if (loadConstraints[i].mouseOver()) {
currentGrid.loadConstraints(str(i + 1) + ".m");
loadConstraints[i].selected = false;
}
}
if (resetConstraints.mouseOver()) {
currentGrid.setupGrid();
resetConstraints.selected = false;
}
for (int i = 0; i < loadImages.length; i++) {
if (loadImages[i].mouseOver()) {
currentGrid.image = loadImage(str(i + 1) + ".jpg");
loadImages[i].selected = false;
}
}
if (alphaBar.selected) {
alphaBar.selected = false;
}
if (resetTexture.mouseOver()) {
currentGrid.setTextureCoords();
resetTexture.selected = false;
}
for (int i = 0; i < currentSegment.length; i++) {
if (currentSegment[i].mouseOver()) {
currentGrid = animation[i];
alphaBar.percent = alpha[i] / 255.0;
for (int j = 0; j < currentSegment.length; j++) {
currentSegment[j].selected = false;
}
currentSegment[i].selected = true;
}
}
for (int i = 0; i < saveSequence.length; i++) {
if (saveSequence[i].mouseOver()) {
saveMorphSequence(i + 1);
saveSequence[i].selected = false;
}
}
for (int i = 0; i < loadSequence.length; i++) {
if (loadSequence[i].mouseOver()) {
loadMorphSequence(i + 1);
loadSequence[i].selected = false;
}
}
for (int i = 0; i < modeButtons.length; i++) {
if (modeButtons[i].mouseOver()) {
currentMode = i;
if (i == 6) {
currentShape = new ArrayList();
shapes.add(currentShape);
}
if ((modeButtons[0].selected && i != 0) ||
(!modeButtons[0].selected && i == 0)) {
for (int j = 0; j < numGrids; j++) {
animation[j].swapVertices();
}
}
for (int j = 0; j < modeButtons.length; j++) {
modeButtons[j].selected = false;
}
modeButtons[i].selected = true;
}
}
for (int i = 0; i < showControls.length; i++) {
if (showControls[i].mouseOver()) {
showControls[i].selected = !showControls[i].selected;
switch (i) {
case 0:
showVertices = showControls[i].selected;
break;
case 1:
showEdges = showControls[i].selected;
break;
case 2:
showPNumbers = showControls[i].selected;
break;
}
}
}
for (int i = 0; i < smoothButton.length; i++) {
if (smoothButton[i].mouseOver()) {
smoothMode = i;
for (int j = 0; j < smoothButton.length; j++) {
smoothButton[j].selected = false;
}
smoothButton[i].selected = true;
}
}
}
/* Draws the contents of the morpher window. */
void draw() {
background(255);
sphereDetail(4);
drawGUI();
if (currentMode == 3) {
animate = true;
pause = false;
}
else if (currentMode == 4) {
animate = true;
pause = true;
}
else if (currentMode == 5) {
setupAnimation();
animate = true;
pause = true;
}
else {
animate = false;
pause = false;
}
/* If animating, interpolate between the parts of the morph sequence. */
if (animate) {
int seg = time / segmentTime;
int nextseg = seg + 1;
int segTime = time % segmentTime;
if (seg >= numGrids - 1) {
animation[numGrids - 1].drawGrid(255);
}
else {
animation[nextseg].interpolateTo(animation[seg], 1 - (float)segTime / segmentTime, 1.0, animateSmoothTimes);
animation[seg].interpolateTo(animation[seg + 1], (float) segTime / segmentTime, 1 - (float)segTime / segmentTime, animateSmoothTimes);
}
if (!pause) {
time++;
}
}
/* Otherwise, it is in editing mode; draw all the segments on top of eachother for editing. */
else {
if (selected) {
if (currentMode == 6) {
currentShape.add(new Integer(currentGrid.v(selectedCorner)));
}
else if (currentMode == 7) {
removeShape(currentGrid.v(selectedCorner));
}
else {
currentGrid.g(selectedCorner).setToMouse();
}
}
for (int i = 0; i < animation.length; i++) {
animation[i].smooth(editSmoothTimes);
}
if (currentMode == 0) {
currentGrid.setTextureCoords();
}
for (int i = 0; i < numGrids; i++) {
animation[numGrids - i - 1].drawGrid(alpha[numGrids - i - 1]);
}
if (showEdges) {
currentGrid.drawEdges(black);
}
if (showVertices) {
currentGrid.drawVertices(red);
drawShapes();
}
}
}
/* Draw user defined shapes. */
void drawShapes() {
for (int i = 0; i < shapes.size(); i++) {
ArrayList shape = (ArrayList)(shapes.get(i));
strokeWeight(3);
if (i < 5) {
stroke( color( 51 * i, 255 - 51 * i, 125 ) );
}
else {
stroke( color( (51 * (i - 5)) % 256, 125, (255 - 51 * (i - 5)) % 256) );
}
noFill();
for (int j = 0; j < shape.size() - 1; j++) {
currentGrid.vertices[ ( (Integer)(shape.get(j)) ).intValue() ].showLineTo( currentGrid.vertices[ ((Integer)(shape.get(j + 1))).intValue() ] );
}
strokeWeight(1);
}
}
/* Remove a user defined shape. */
void removeShape(int corner) {
boolean found = false;
for (int i = 0; i < shapes.size() && !found; i++) {
ArrayList shape = (ArrayList)(shapes.get(i));
for (int j = 0; j < shape.size() && !found; j++) {
if ( ( (Integer)( shape.get(j) ) ).intValue() == corner ) {
shapes.remove(i);
found = true;
}
}
}
}
/* Setup the animation. */
void setupAnimation() {
time = 0;
}
/* Handles mouse presses. */
void mousePressed() {
currentGrid.mousePressed();
pressedGUI();
}
/* Handles mouse releases. */
void mouseReleased() {
currentGrid.mouseReleased();
mouseGUI();
}
/* Saves a morph sequence from file. */
void saveMorphSequence(int m) {
String[] output = new String [1 + numGrids * 2];
int s = 0;
output[s++] = str(numGrids);
for (int i = 0; i < numGrids; i++) {
String morphName = str(m) + "-" + str(i) + "-m.m";
String featureName = str(m) + "-" + str(i) + "-f.m";
if (currentMode != 0) {
animation[i].swapVertices();
}
animation[i].saveConstraints(featureName);
animation[i].swapVertices();
animation[i].saveConstraints(morphName);
if (currentMode == 0) {
animation[i].swapVertices();
}
output[s++] = featureName;
output[s++] = morphName;
}
saveStrings(str(m) + ".ms", output);
}
/* Loads a morph sequence from file. */
void loadMorphSequence(int m) {
String[] input = loadStrings(str(m) + ".ms");
int s = 0;
numGrids = int(input[s++]);
// Commented out for speed - only needed if number of grids varies.
//for (int i = 0; i < numGrids; i++) {
// animation[i] = new Grid(filenameA, 600, 700, 0, 0, 0, i);
//}
currentGrid = animation[0];
for (int j = 0; j < currentSegment.length; j++) {
currentSegment[j].selected = false;
}
currentSegment[0].selected = true;
for (int i = 0; i < numGrids; i++) {
if (currentMode != 0) {
animation[i].swapVertices();
}
animation[i].loadConstraints(input[s++]);
animation[i].smooth(100);
animation[i].setTextureCoords();
animation[i].swapVertices();
animation[i].loadConstraints(input[s++]);
if (currentMode == 0) {
animation[i].swapVertices();
}
}
}
/* Handles keypresses, which probably should not be used
* since the GUI is available. */
void keyPressed() {
if (key=='+') {
if ((alpha[currentGrid.id] += 4) > 255) {
alpha[currentGrid.id] = 255;
}
}
if (key == '-') {
if ((alpha[currentGrid.id] -= 4) < 0) {
alpha[currentGrid.id] = 0;
}
}
if (key == 'e') {
showEdges = !showEdges;
}
if (key == 'v') {
showVertices = !showVertices;
}
if (key == '!') {
currentGrid.saveConstraints("1.m");
}
if (key == '@') {
currentGrid.saveConstraints("2.m");
}
if (key == '#') {
currentGrid.saveConstraints("3.m");
}
if (key == '$') {
currentGrid.saveConstraints("4.m");
}
if (key == '1') {
currentGrid.loadConstraints("1.m");
}
if (key == '2') {
currentGrid.loadConstraints("2.m");
}
if (key == '3') {
currentGrid.loadConstraints("3.m");
}
if (key == '4') {
currentGrid.loadConstraints("4.m");
}
if (key == ' ') {
animate = !animate;
}
if (key == 'r') {
setupAnimation();
}
if (key == 'p') {
pause = !pause;
}
}