// File: cube.cpp // Written by Grant Macklem // Grant.Macklem@Colorado.EDU // 2:00 PM Recitation // Displays a cube in perspective projection which the user can then rotate. The // cube is drawn so that it's center is at the origin of the axes. The length of // each side is given by the variable in the main function. The program calculates // the perspective projection of the cube onto a plane by using 3-dimentional vector // equations for lines and planes. A projection point located below the cube is // where all rays begin, travelling through each vertex of the cube, and finally // intersecting the plane which is above the axes. Because of this method, the // sides of the cube closest to to the projection point are drawn larger than // the sides further away, even though the sides further away are actually closer // to the projection plane (the computer screen). // The projection plane has the z-axis coming out of the screen towards us. // The perspective projection was created using the following equations where // P_0 means "P sub 0": // P_0 = // P_i = point of a vertex = // V = P_i - P_0 = = // The equations for a line are // r = r_0 + tv where r = ; r_0 = , and v = // In this program, r_0 = P_0, v = V // So, = + // or // x = x_0 + tA; y = y_0 + tB; z = z_0 + tC // which can be represented as (x - x_0) / A = (y - y_0) / B = (z - z_0) / C // Thus, to find the corresponding points where the line intersects the plane // with a given z_0, I used only that component and set it equal to the last term. // x component: x = [A * (z - z_0)] / C + x_0 // or x = { [ (P_ix - P_0x) * (z - z_0) ] / (P_iz - P_0z) } + x_0 // A similar calculation results in the y value. /******************************************************************************* * cube.exe A program to manipulate a perspective cube projection. * Copyright (C) 1998 Grant Macklem * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * You may contact me, the author by emailing Grant.Macklem@Colorado.EDU *******************************************************************************/ #include // Provides init_window #include // Provides cin, cout #include // Provides sqrt, sin, cos const double PI = 3.141592653589; // Sets the initial points of the cube verticies void set_initial(double cube [][3], double length); // Rotate the verticies of the cube corresponding to each angle xa, ya, and za void rotate_cube (double cube[][3], // Cube verticies in 3-space int xa, // Angle of rotation w/r/t x-axis int ya, int za); // Draw the cube projection onto the plane void draw_cube (double cube[][3], // Cube verticies in 3-space double point[][2], // Cube verticies in perspective 2-space double plane[], // Coordinates of viewing plane double pro[]); // Coordinates of projection point // Returns the ascii value of a key pressed int GetKey(); // Actually works with the user interaction and moves the cube. The main funciton // calls this function every time it is to work. Return value is whether or not // to keep running the program. bool user (double cube[][3], // Cube verticies in 3-space double point[][2], // Cube verticies in perspective 2-space double plane[], // Coordinates of viewing plane double pro[], // Coordinates of projection point double length, // Length of cube side double scale, // Scale factor used when zooming. double pro_z, // Z-coordinate of projection point. double plane_z); // Z-coordinate of projection plane. int APIENTRY WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { const double pro_z = -50; // Z-coordinate of projection point. const double plane_z = 120; // Z-coordinate of projection plane. double cube[8][3]; // Array to hold cube points. First index is the // vertex point, second is the x,y,z coordinates. double length = 20; // Length of cube side double scale = 1.1; // Scale factor used when zooming. double pro[3] = {0, 0, pro_z}; // x,y,z coordinates of the projection point double plane[3] = {0, 0, plane_z}; // Coordinates of the plane projected onto. double point [8][2]; // Coordinates where ray intersects projection plane bool run; // Value to keep running the program. initwindow (400,400); // Initialize graphics window set_initial(cube, length); // Set initial cube verticies draw_cube(cube, point, plane, pro); // Draw the cube cout << "Rotating Cube, Copyright (C) 1998 Grant Macklem.\n" << endl; cout << "Press " << char(34) << "Q" << char(34) << " at any time to quit." << endl; cout << "Press " << char(34) << "R" << char(34) << " to reset the cube to" << " the initial position." << endl; cout << "Press (up) to move the viewpoint above the cube." << endl; cout << "Press (down) to move the viewpoint below the cube." << endl; cout << "Press (left) to move the viewpoint to the left of the cube" << endl; cout << "Press (right) to move the viewpoint to the right of the cube" << endl; cout << "Press " << char(34) << "\\" << char(34) << " to rotate" << " counterclockwise." << endl; cout << "Press " << char(34) << "/" << char(34) << " to rotate" << " clockwise." << endl; cout << "Press " << char(34) << "z" << char(34) << " to zoom out." << endl; cout << "Press " << char(34) << "Z" << char(34) << " to zoom in." << endl; do { run = user (cube, point, plane, pro, length, scale, pro_z, plane_z); } while(run == true); } void set_initial(double cube [][3], double length) { double side = length / 2.0; // Point 1 cube[0][0]=-side; cube[0][1]=-side; cube[0][2]=side; // Point 2 cube[1][0]=-side; cube[1][1]=side; cube[1][2]=side; // Point 3 cube[2][0]=side; cube[2][1]=-side; cube[2][2]=side; // Point 4 cube[3][0]=side; cube[3][1]=side; cube[3][2]=side; // Point 5 cube[4][0]=-side; cube[4][1]=-side; cube[4][2]=-side; // Point 6 cube[5][0]=-side; cube[5][1]=side; cube[5][2]=-side; // Point 7 cube[6][0]=side; cube[6][1]=-side; cube[6][2]=-side; // Point 8 cube[7][0]=side; cube[7][1]=side; cube[7][2]=-side; } // Draws the cube projection onto the plane. void draw_cube (double cube[][3], // Cube verticies in 3-space double point[][2], // Cube verticies in perspective 2-space double plane[], // Coordinates of viewing plane double pro[]) // Coordinates of projection point { // Calculate two-dimentional projections of the points. for (int i=0; i<8; i++) { for (int j=0; j<2; j++) { point[i][j] = (plane[2]-cube[i][2]) / (cube[i][2]-pro[2]) * (cube[i][j]-pro[j]) + cube[i][j]; } } // put coordinates in center of window for (int i=0; i<8; i++) { for (int j=0; j<2; j++) point[i][j]+=200; } line(point[6][0],point[6][1],point[4][0],point[4][1]); // bottom left line(point[6][0],point[6][1],point[7][0],point[7][1]); // bottom front line(point[6][0],point[6][1],point[2][0],point[2][1]); // front left line(point[0][0],point[0][1],point[2][0],point[2][1]); // top left line(point[0][0],point[0][1],point[4][0],point[4][1]); // back left line(point[0][0],point[0][1],point[1][0],point[1][1]); // top back line(point[3][0],point[3][1],point[2][0],point[2][1]); // top front line(point[3][0],point[3][1],point[1][0],point[1][1]); // top right line(point[3][0],point[3][1],point[7][0],point[7][1]); // front right line(point[5][0],point[5][1],point[4][0],point[4][1]); // bottom back line(point[5][0],point[5][1],point[1][0],point[1][1]); // back right line(point[5][0],point[5][1],point[7][0],point[7][1]); // bottom right } void rotate_cube (double cube[][3], // Cube verticies in 3-space int xa, // Angle of rotation w/r/t x-axis int ya, int za) { double rad = PI / 180.0; double txa, tya, tza; double tSin, tCos; double tx, ty, tz; txa = xa * rad; tya = ya * rad; tza = za * rad; // Rotate along Z-axis tSin = sin(tza); tCos = cos(tza); for (int i = 0; i < 8; i++) { tx = cube[i][0] * tCos - cube[i][1] * tSin; ty = cube[i][0] * tSin + cube[i][1] * tCos; cube[i][0] = tx; cube[i][1] = ty; } // Rotate along Y-axis tSin = sin(tya); tCos = cos(tya); for (int i = 0; i < 8; i++) { tx = cube[i][0] * tCos + cube[i][2] * tSin; tz = -cube[i][0] * tSin + cube[i][2] * tCos; cube[i][0] = tx; cube[i][2] = tz; } // Rotate along X-axis tSin = sin(txa); tCos = cos(txa); for (int i = 0; i < 8; i++) { ty = cube[i][1] * tCos - cube[i][2] * tSin; tz = cube[i][1] * tSin + cube[i][2] * tCos; cube[i][1] = ty; cube[i][2] = tz; } } int GetKey( void ) { int c; c = getch(); if(c == 0) { c = getch(); c += 300; } return c; } bool user (double cube[][3], // Cube verticies in 3-space double point[][2], // Cube verticies in perspective 2-space double plane[], // Coordinates of viewing plane double pro[], // Coordinates of projection point double length, // Length of cube side double scale, // Scale factor used when zooming. double pro_z, // Z-coordinate of projection point. double plane_z) // Z-coordinate of projection plane. { int command; // Key pressed for next action int za=0; // Angle that the cube has rotated around the // z-axis ([counter]clockwise motion) int ya=0; // Angle that the cube has rotated around the // y-axis (left and right motion) int xa=0; // Angle that the cube has rotated arond the // x-axis (up and down motion) command = getch(); if (command == 0) command = getch(); switch (command) { case int('4'): case KEY_LEFT: //Observer moves to left, rotate cube to right //Actually rotate cube forward (ya) ya=-5; rotate_cube(cube, xa, ya, za); break; case int('6'): case KEY_RIGHT: //Observer moves to right, rotate cube to left //Acutally rotate cube back (ya) ya=5; rotate_cube(cube, xa, ya, za); break; case int('8'): case KEY_UP: // Observer moves up, rotate cube down // Actually rotate cube clockwise (xa) xa=5; rotate_cube(cube, xa, ya, za); break; case int('2'): case KEY_DOWN: // Observer moves down, rotate cube up // Actually rotate cube counterclockwise (xa) xa=-5; rotate_cube(cube, xa, ya, za); break; case int('/'): // Observer tilts left, rotate cube clockwise // Actually rotate cube along z-azis (za) za=5; rotate_cube(cube, xa, ya, za); break; case int('\\'): // Observer tilts right, rotate cube counterclockwise // Actually rotate cube along z-axis (za) za=-5; rotate_cube(cube, xa, ya, za); break; case int('Z'): // Zoom in. Actually move the projection point closer // until it is at its default value. Then start moving // the plane further away so the image is larger. // Since pro and pro_z are double numbers, see if the projection point // is very close to the original number. // Will move the plane up 10% further than it was previously. // If the plane is up so far that no more zooming will occur, do nothing. // Constant 5.7 MAY need adjusting if pro_z and plane_z are changed.. if ( plane[2] > ( (abs(pro_z * plane_z)) / 5.7 ) ) break; if (abs(pro[2] - pro_z) > .01) { pro[2] /= scale; } else { plane[2] *= scale; } break; case int ('z'): // Zoom out. Acutally move the projection plane closer // until it is at its default value. Then start moving // the projection point further away so the image is smaller. // Since plane and plane_z are double numbers, see if the projection plane // is very close to the original number. // Will move the point back 10% further than it was before. // If the point is back so that hardly any more zooming out will occur, // do nothing. // Constant 4.5 MAY need adjusting if pro_z and plane_z are changed. if ( pro[2] < ((pro_z * plane_z) / 4.5) ) break; if (abs(plane[2] - plane_z) > .01) { plane[2] /= scale; } else { pro[2] *= scale; } break; // Quit the program case int ('Q'): case int ('q'): return false; // Reset the initial values of the program case int ('R'): case int('r'): pro[0] = 0; pro[1] = 0; pro[2] = pro_z; plane[0] = 0; plane[1] = 0; plane[2] = plane_z; set_initial (cube, length); break; default: return true; } // end switch cleardevice(); draw_cube(cube, point, plane, pro); return true; }