Beruflich Dokumente
Kultur Dokumente
Towards the end of EECS110 we combine pointers, structures, dynamically-allocated arrays, and pass-byreference functions into good habits for object-oriented programming. While the PPT lecture notes may help explain, youll learn best by writing code to practice the methods yourself. In addition to this exercise, I encourage you to experiment -- extend the code we wrote in class (see instructions on Blackboard Documents In-Class Starter Code), and in these last graded exercises into object-oriented forms. Work on EX-07, EX-08, and EX-09 in order. Each exercise can build on your work for the previous one.
Your goal is to reorganize this code in two ways, each one encapsulating all robot drawing code to make it re-usable for many robots. You must use these two required function prototypes:
void drawRobotVal(double xpos, double ypos, double tilt, double size, double shoulderL, double shoulderR, double elbowL, double elbowR, char *pName, double bodyColorR, double bodyColorG, double bodyColorB, double armLcolorR, double armLcolorG, double armLcolorB, double armRcolorR, double armRcolorG, double armRcolorB ); void drawRobotRef(double *pX, double *pY, double *pTilt, double *pSize, double *pShoulderL, double *pShoulderR, double *pElbowL, double *pElbowR, char *pName, double *bodyColr, double *armLColr, double *armRColr );
2/27/2013: SIMPLIFIED ---changed *bodyColr[] *bodyColr, *armLColr[] *armLColr, *armRColr[] *armRColr CAREFUL! Each new formal parameter expects arguments whose value is an address in memory at the start of a 3-element array of double. That array holds the colors: red in element 0, green in element 1, blue in element 2.
Your task is: STEP 1. Convert the starter codes long and messy sequence of robot-drawing statements found in myDisplay() into just one robot-drawing function named DrawRobotVal(). This pass-by-value function will be a bit messy and tedious, but will group together all the tasks of drawing a robot into one reusable function. Your function must (mandatory!) use the exact function prototype given above. Replace the mass of robot-drawing-related statements in myDisplay() with just one call to your DrawRobotVal() function, using global variables as arguments. Unlike the callback functions, your DrawRobotVal() function body must not contain ANY global variables! None! (Why? Global variables are a bad idea; any function can change them, and functions that use them cant do anything else). Instead, the function must receive all its changeable drawing values as arguments, and its formal parameters must have names different from the arguments used to call the function. Once complete, you will have a single robot-drawing function that can be called with different argument values to draw different robots at different on-screen locations. This will be your first of several jointedobject drawing functions; it will have many formal parameters (too many!), and void return type. STEP 2. Make a SECOND set of global variables (similar to the existing set) to describe a SECOND, visibly different robot that you could draw with a call to your drawRobotVal() function. Just as we did for the first robot, in main() you should set the initial values for these global variables that define this second robot. This second robot must have different color values, different values for position, tilt, size, arm angles, elbow angles, names, etc. Test your second set of robot-describing variables by using them as arguments in a call to drawRobotVal() within the myDisplay() callback function. STEP 3. Make a SECOND robot-drawing function named DrawRobotRef(). This pass-by-reference function resembles your existing DrawRobotVal() function, but accepts only pointers (or address values) as its arguments. Your function must use (mandatory) the exact function prototype given above STEP 1. (WARNING! Your DrawRobotRef() function must not call DrawRobotVal(). Both functions must work independently; you must be able to delete either one without affecting the other.) STEP 4. Modify your code to draw the first robot with DrawRobotVal(), and draw the second, different robot with DrawRobotRef(). Make sure your code compiles and runs. On CodeCritic, submit only the contents of your jointedRobot.c file dont submit any other files contents. STEP 5. OPTIONAL: Create your OWN pass-by-reference jointed object drawing function. Draw something substantially different that the robot supplied: some other animal, machine, or jointed shape, from octopus to tarantula to eagle or helicopter to fanciful space-ship with robot arms. Your function must use pass-byreference to supply all controllable drawing features, and must include the ability to change x,y position, size, rotation, joint angles, and color(s). Your program must also allow users to adjust those values using the keyboard and/or mouse.
From here, we can draw the right arm just as easily as we drew the left arm: move drawing axes from robot-spine to the right-shoulder location and rotate these new drawing axes by the rightarm angle rob_ shoulderR. Now the our drawing axes are located at the robots right shoulder, and rotated so that the robots right arm stretches from the origin point (the shoulder) outwards along the +x direction. We can now draw the right arm using a simple call to DrawBox(), and drawn the right elbow and lower arm by following the same methods we used for the left arm. The starter code has finished the drawing here we need only to remove the two plates from the stack to avoid overflowing the stack as the program runs, and then we could safely leave myDisplay(). But suppose you wished to attach a 2-fingered hand at the end of the right arm: how would you do it? How would you add pivoting legs to the robot? Could you make knees in those legs? Hands at the ends of the arms? Fingers at the ends of the hands? Feet and toes at the ends of the legs? Lobster claws? A head with eyes on stalks and moth-like antennae? Briefly, lets trace through the process of making a 2-fingered right hand. Ask first: will you need to return to the current right shoulder drawing axes to complete your drawing? Put another way, will any other shapes attach to this current joint, to this shoulder?) If yes, then save your current drawing axes on the push-down stack. Next, create wrist drawing axes: move the drawing axes to the end of the arm and rotate by a palm angle variable, then draw the hands palm shape (perhaps a triangle) extending from the wrists origin. We will need to return to the wrist coordinates to draw each finger, so you must save its drawing axes on the stack before you further modify your drawing axes. Next, move the drawing axes from the wrist to the base of the first finger and rotate by a fingerangle variable to make the finger1 drawing axes. Draw the finger extending from the origin. Next, retrieve the wrist coordinates, save them again (in case we want to make more fingers), and move the drawing axes outwards to the pivot point for the second finger; draw finger. Continue the process for all fingers on that hand. Thus with sensible use of a push-down stack for intermediate drawing axes, you can make ANY jointed object and animate it on-screen!
HINTS: Always begin the myDrawing() callback function with ResetDrawingAxes(), to ensure that the drawing-axes are at a known location and that the drawing-axes stack is empty. Always call SaveDrawingAxes() and RetrieveDrawingAxes() in pairs in your code: this ensures you wont try to remove any axes from an empty stack. I always indent the statements between them too (see starter code), to help better illustrate the stack contents. Overflow/Underflow: The introGlutLib library keeps only the 32 most-recent drawing axes that were stored by SaveDrawingAxes(); if you save more than 32, introGlutlib discards the oldest stored values. If you call RetrieveDrawingAxes() too many times, you only get another copy of the most-recently-saved drawing axes.
BACKGROUND:
The goal of this and the next project is to give you some practice with structure types (struct), and you will draw whole families of responsive moving objects on-screen to practice some object-oriented programming principles. Recall that a structure is NOT a variable, but a data type; a new kind of variable, just as int, char, float, and double are each a kind of variable. A variable whose data-type is a struct should be considered a packet that contains several member variables. and each member has its own value. As described in Chapter 12, in class, and in the lecture notes, using structures always requires these three steps in your program: 1. Define the structure data type. Specify the set of member variables that your structure data type will contain; you must give them names and specify the data type of each member. For example, in Lecture Notes 7C we define a new structure data type called workerT that contained a pointer-tochar member variable for the name string, a double member variable for salary, an array of char for the ssn number, and an int member variable ded for deductions. 2. Declare Variables using the newly-defined structure data type. In the lecture notes we declared variables emp1, staff[30]. Note that emp1 is a single variable of type workerT, and staff[30]; is an array of 30 elements, each one a single variable of type workerT. 3. Initialize and Use the struct Variables. As with any variable, we must set the initial values; here, we must set each member variables value individually.
rather than a scattered mess of different variables. As for any structure in C, we defined the new data type first (see typedef statement), then used the new type to declare two new (global) fish-like variables named nemo and mack. In main() we set all the initial values for the nemo and mack variables to define the nemo object as a small, slow-moving green fish and the mack object as a large, fast-moving blue fish. The myDisplay() callback function contains calls to DrawSwimmingFishT() for both the nemo and the mack objects. In addition to bundling together fish-related variables, we can also bundle together the behaviors of fishT objects by introducing efficient pass-by-reference functions with fish-related names. Whenever possible, these functions should accept ONLY a fishT object, and perform that objects behavior using only members of the fishT struct, as we did in DrawSwimmingFishT(). These functions are efficient because they only need to accept one struct argument using pass-byreference: as the functions argument is just the address of one, unified, great big fishT variable, it copies only that address to the functions formal parameter, instead of the structure itself. In computer-science terminology, the nemo object is not just the nemo variableit is the COMBINATION of the nemo variable AND the fishT-related functions that implement all of nemos behaviors. Thus an object is a bundle of attributes and behaviors that act together to define one particular fishs participation in our program. In C++, we extend the notion of a structure to include both member variables and member functions this struct-and-its-functions grouping is called a class. In programs written in C, we can imitate this grouping by a) using structures and by b) giving fish-related names to functions that accept a fishT struct to carry out fish-related behaviors for a single fishT object.
Exercise 08 Instructions:
Write a newer, object-oriented version of the JointedRobot program you made for EX 07. In this newer version, you must: 1. Create your own RobotT structure data type; define it above main(), and make sure it contains all the member variables it needs to completely describe a robot object. 2. Declare two new global variables of type RobotT named jim and pam (e.g. above main(), write this C statement: RobotT jim,pam; ) 3. Initialize all member variables of jim and pam in main().
4. In the myDisplay() callback, call your drawRobotVal() function that you wrote in Exercise
07 to draw the Jim and Pam objects on-screen. (never use any global variables inside either the drawRobotVal() or drawRobotRef() function bodies!). You will need to use the Jim and Pams member variables somehow as the arguments to drawRobotVal() see the FishFancy02 starter code if youre puzzled about how to do this. \
Now that your program draws jim and pam on-screen correctly, your next step is to write a neat, clean, object-oriented drawing function for RobotT. This pass-by-reference function accepts just one argumentthe memory location of the RobotT that you wish to draw on-screen. 5. Create your own RobotT drawing function. Use this (REQUIRED) prototype: void drawRobotT( RobotT *pSrc); Write the function and test it; you may wish to copy and modify code from your earlier robotdrawng functions. In your myDisplay() callback function, call drawRobotT() to draw the jim and pam RobotT objects. Once this works properly, 6. Remove/Delete all the old, now-unused, unstructured robot-drawing variables, so that jim and pam are the ONLY robot-describing global variables that remain. (e.g. delete the separate global variables rob_xpos, rob_ypos, rob_tilt, rob_size, etc. used to draw any and all non-jim, non-pam robots. Next, 7. Remove/Delete all the old, unstructured robot-drawing functions you wrote in Exercise 7. Yes, thats rightyou must now delete drawRobotVal() and drawRobotRef(). No, you cant use the trick shown in FishFancy02 of calling the old messy functions from the new, clean functions. This forces you to make a drawRobotT() that is clean, simple, self-contained and complete. It becomes your only available robot-drawing function, and it does everything you need. With this cleaned up object-oriented way to describe and draw robots, your program is simpler and far more flexible/expandable. Lets expand it: lets write more object-oriented, pass-by-reference functions to create more robot behaviors and activities: 8. Write a new RobotT-initializing function using this (REQUIRED) prototype: void initRandomRobotT( RobotT *pSrc); This pass-by-reference function sets the values of all the RobotT member variables to sensible but randomly-chosen values. (unsure how? Look at FishFancy03 for an example). Test it on jim and pam to be sure it works correctly. Every time you start the program, every time you apply initRandomRobotT() to any existing RobotT object and display it, you should see a different, randomly-chosen robot. 9. Create a fixed array of at least 20 RobotT objects (e.g. RobotT danceTeam[25]; ) --initialize all the robot objects in this array using repeated calls to initRandomRobot(), --draw all the robot objects in this array using repeated calls to drawRobotT(); --make sure all members of this array respond to keyboard inputs in the same way as the original Limbo robot did, before we created jim and pam. With all robots responding to the 1,2,3,4,5,6 and 7 keys, all members of your crowd of robot objects should appear to dance in unison! 10. On CodeCritic, submit the contents of your modified jointedRobot.c program file do not submit any other files contents, or any earlier versions of your code.
Exercise 09 Instructions:
Make a copy of the final version of your Exercise08 code, re-name it, and make the modifications described below. Again, the FishFancy03 starter code can act as your guide, as it accomplishes most of the goals described here using fishT and schoolT objects instead of RobotT and crowdT objects. 1. First, define your own CrowdT data structure that will hold a dynamically-allocated collection of RobotT objects, along with anything else we may need to describe and control a crowd of dancing RobotTs. As shown in the FishFancy03 example project, this new datatype can be very simple; its members could be just a pointer-to-RobotT, used to retain the location of a dynamicallyallocated array of RobotT objects, and an integer to keep track of the number of RobotT objects in our crowd. Add more members for additional features if you wish. 2. Next, make two global variables of type CrowdT named jets and sharks (Names of competing dancing street-gangs in the old Broadway musical & movie West-Side Story) 3. Using the FishFancy03 code as your guide and example, write a set of object-oriented pass-by-reference functions that: a)--safely initialize a CrowdT object (in FishFancy03, look at initSchoolT() function) b)--lets users safely change the number of RobotT objects held within a CrowdT object (in FishFancy03, look at the ResizeSchoolT() function. Note that it combines the tasks of initializing and re-sizing a dynamically allocated array. c)--draws on-screen all the RobotT objects contained in any given CrowdT object. For this function, you must use this (REQUIRED) function prototype: void drawCrowdT( CrowdT *pSrc); 4. As demonstrated in the FancyFish03 project, when users press the J or j keys, your program should ask for the number of RobotT objects you want in the CrowdT object named jets, make that change immediately on-screen. Similarly when users press the S or s key, your program should ask for the numberof RobotT objects to use in the CrowdT object named sharks, and then make that change immediately on-screen. Note that this use of scanf() causes your programs graphics window to freeze until you: a) select the console window, and then b) enter a number and hit ENTER. After that, the graphics display window starts working again, and your program no longer responds to the console window. To get another keystroke response, you must select the graphics window
again. 5. ENTIRELY ELIMINATE any and all previous individual RobotT object (no jim, no pam), and/or any fixed arrays of any other RobotT objects. 6. Write additional pass-by-reference functions that accept only (the address of) a CrowdT objects that will cause all RobotT objects in that crowdT object to respond to keyboard inputs (arrow keys, PageUp/Dn,1,2,3,4,5,6,7) in the same way as the original Limbo robot in the original JointedRobot04 starter code you used in Exercise 07.
You are welcome to follow the fancyFish03 method as closely as you wish (guaranteed to work, guaranteed to earn full credit if you do it that way) to accomplish step 4, but I hope you will recognize that you could make a myriad of other choices here. For example, you could write separate initialization and size-adjusting functions, or decide to perform your own buffer management (keep a dynamic array larger than the requested number of RobotT objects, show only the number of objects the user requested, and call malloc() ONLY when users ask for more memory than you hold in your current array), etc. 7. On CodeCritic, submit the contents of your modified jointedRobot.c program file do not submit any other files contents, or any previous versions of this file.