/* Filename: MouseInput.cpp * * Author: Br. Isidore Minerd * * Date: July 17, 2009 * * Description: This is our capstone project for the beginners' section of these * tutorials. In brief this program: * Loads any available "drawing.txt" file and renders * an image based on that * * Allows the user to create circles / rectangles of random * size on the screen, appending those to "drawing.txt" * * Allows the user to clear the drawing * * Key configuration: * Control Button: Fill shape to be drawn * Space Bar: Clear Screen * Left Mouse Click: Draw Rectangle * Right Mouse Click: Draw Circle * Escape: Close program * */ #include "DarkGDK.h" #include using namespace std; // Initial Definitions #define RECTANGLE_TYPE 1 #define CIRCLE_TYPE 2 #define DRAWING_FILE_NAME "drawing.txt" // Function Prototypes int rand(int low, int high); void DrawEmptyBox(int ulx,int uly,int lrx, int lry); void DrawEmptyBoxWH(int ulx,int uly,int width,int height); void DrawBall(int CenterX, int CenterY, int Radius); bool loadFileContents(); void performRectangle(int centerX, int centerY, bool filled, fstream &OutFile); void performCircle(int centerX, int centerY, bool filled, fstream &OutFile); // Main entry point void DarkGDK ( void ) { int mouseButtonNum,mouseX,mouseY; int filled; fstream OutFile; dbSetWindowTitle("Simple Random Shapes"); // Perform necessary start files if(!loadFileContents()) { dbPrint("Failed to load save-state file!\nPress any key to terminate program."); dbWaitKey(); return; } OutFile.open(DRAWING_FILE_NAME,ios::app); if(OutFile.fail()) { dbPrint("Failed to open save-state file!\nPress any key to terminate program."); dbWaitKey(); return; } // Main event loop while(LoopGDK()) { // Check to see if the space bar has been pressed (to indicate a screen clear) if(dbSpaceKey()) { dbCLS(); OutFile.close(); OutFile.open(DRAWING_FILE_NAME,ios::out); if(OutFile.fail()) { dbPrint("File Error! Aborting!\nPress any key to terminate program."); dbWaitKey(); return; } } // Get mouse information mouseButtonNum = dbMouseClick(); mouseX = dbMouseX(); mouseY = dbMouseY(); // Decide if the shape will be filled filled = dbControlKey(); // Check to see if any buttons are down if(mouseButtonNum == 1) { performRectangle(mouseX,mouseY,filled,OutFile); // A rather primitive way to wait for the left button to come up // without using threads while(dbMouseClick() == 1); } else if(mouseButtonNum == 2) { performCircle(mouseX,mouseY,filled,OutFile); // A rather primitive way to wait for the left button to come up // without using threads while(dbMouseClick() == 2); } // Wait a millisecond to keep from locking up with a fast-executing loop dbWait(1); } OutFile.close(); if(OutFile.fail()) { dbCLS(); dbPrint("Error closing status file!\nPress any key to terminate program."); dbWaitKey(); } return; } /* Given: low Lowest possible random number high Highest possible random number Task: Generate a random number between low and high inclusive Return: Random number between low and high inclusive -1 if parameters are not valid */ int rand(int low, int high) { int randomNum; if(low < 0 || high < 0 || low >= high) randomNum = -1; else randomNum = rand() % (high - low + 1) + low; return randomNum; } /* Given: ulx The upper-left corner's x coordinate uly The upper-left corner's y coordinate lrx The lower-right corner's x coordinate lry The lower-right corner's y coordinate Task: Draw an empty rectangle, using the coordinates specified Note that this function does not check the validity of the coordinates' values (since we have not studied conditionals yet) Return: Nothing */ void DrawEmptyBox(int ulx,int uly,int lrx, int lry) { // Draw line from upper left to upper right (NB: urx = lrx and ury = uly) dbLine(ulx,uly,lrx,uly); // Draw line from upper right to lower right dbLine(lrx,uly,lrx,lry); // Draw line from lower right to lower left (NB: llx = ulx and lly = lry) dbLine(lrx,lry,ulx,lry); // Draw line from lower left to upper left dbLine(ulx,lry,ulx,uly); } /* Given: CenterX the x-coordinate of the center of the ball. * CenterY the y-coordinate of the center of the ball. * Radius the radius of the desired ball. * Task: To draw a filled-in ball with the given center and radius, at least as much of the ball as fits inside of * the bounding rectangle with upper left corner UpperLeftX, UpperLeftY and lower right corner LowerRightX, * LowerRightY. * Return: Nothing. */ void DrawBall(int CenterX, int CenterY, int Radius) { int RadiusSquared, XDiff, YDiff, UpperLeftX, UpperLeftY, LowerRightX, LowerRightY,rightX,rightY; // One of several ways to implement this UpperLeftX = CenterX - Radius; UpperLeftY = CenterY - Radius; RadiusSquared = Radius * Radius; for(int row = UpperLeftY; row <= CenterY; row++) { for(int col = UpperLeftX; col <= CenterX; col++) { XDiff = CenterX - col; YDiff = CenterY - row; if ((XDiff * XDiff + YDiff * YDiff) <= RadiusSquared) { for(int i=col,max = 2 * CenterX - col; i <= max; i++) { dbDot(i,row); dbDot(i,2*CenterY - row); } // We need not go any further to right, so we can break from this for loop break; } } } } /* Given: Nothing. * Task: Loads the file contents for DRAWING_FILE_NAME indicating the initial screen * configuration * Return: true if successful; false if not successful; NB that this is not rigorous in its * error checking */ bool loadFileContents() { int type,filled,ulx,uly,lrx,lry,cx,cy,r; fstream InFile; // Read in the file and draw appropriately InFile.open(DRAWING_FILE_NAME, ios::in); if(InFile.fail()) { return false; } while(! InFile.fail()) { InFile >> type; switch(type) { case RECTANGLE_TYPE: InFile >> filled >> ulx >> uly >> lrx >> lry; if(filled) { dbBox(ulx,uly,lrx,lry); } else { DrawEmptyBox(ulx,uly,lrx,lry); } break; case CIRCLE_TYPE: InFile >> filled >> cx >> cy >> r; if(filled) { DrawBall(cx,cy,r); } else { dbCircle(cx,cy,r); } break; } } InFile.close(); // Indicate on return if the closing was successful or not. // This would be the final check for proper file handling return InFile.fail(); } /* Given: centerX the x-coordinate of the center of the rectangle. * centerY the y-coordinate of the center of the rectangle. * filled indicator of whether or not to fill the given rectangle * Task: Draws a rectangle with center at (centerX,centerY), filled if filled = true * with a random width; this is also output to the status file * Return: Nothing. */ void performRectangle(int centerX, int centerY, bool filled, fstream &OutFile) { // Determine dimensions int width = rand(10,75), height = rand(10,75), ulx = centerX - width / 2, uly = centerY - height / 2,lrx = ulx + width, lry = uly + height; // Perform drawing if(filled) { dbBox(ulx,uly,lrx,lry); } else { DrawEmptyBox(ulx,uly,lrx,lry); } // Output to status file OutFile << RECTANGLE_TYPE << " " << filled << " " << ulx << " " << uly << " " << lrx << " " << lry << "\n"; } /* Given: centerX the x-coordinate of the center of the circle. * centerY the y-coordinate of the center of the circle. * filled indicator of whether or not to fill the given circle * Task: Draws a circle with center at (centerX,centerY), filled if filled = true * with a random radius; this is also output to the status file * Return: Nothing. */ void performCircle(int mouseX, int mouseY,bool filled, fstream &OutFile) { int r = rand(10,50); // Get random radius if(filled) { DrawBall(mouseX,mouseY,r); } else { dbCircle(mouseX,mouseY,r); } OutFile << CIRCLE_TYPE << " " << filled << " " << mouseX << " " << mouseY << " " << r << "\n"; }