CSC 155 Assignment 2 : Gravity Simulation Due Thursday 2/17 **** Please Note: these instructions allow may not be enough. You need to have taken good notes in class in order to know what to do. In this assignment we will: 1. Review principles of object-oriented data representation 2. Introduce the use of the Gtkmm API 3. Make some basic use of animation and graphical rendering 4. Use a two-dimensional array 5. Exercise "scientific computation" The individual programming elements of this assignment are rudimentary, but when combined together, they should make a challenging and hopefully exciting project. The basic goal of the project is to simulate a gravity well similar to the one demonstrated in class. We will apply Newton's laws of gravity: Given two objects with masses M1 and M2, the gravitationl force between these objects is F = (G*M1*M2)/(d*d), where d is the distance between the centers of gravity of the two objects. Newton also says that F=MA, so A1 = G*M2/(d*d) is the accelartion caused by the pull of M2 on M1, and A2 = G*M1/(d*d) is that caused by M1 on M2. The gravitational constant G is 6.673e-11 meters^3/(kg*sec^2) in our universe (yours may be different :-) Acceleration is given in meters/sec^2. Distances are in meters and mass in kg. To represent a physical particle or "body", we will create a class with the following variables: double mass; // in kg int radius; // radius in screen pixels double xcord, ycord, zcord; // position coordinates double xvel, yvel, zvel; // velocity vector // acceleration vector can be calculated dynamically The "radius" of an object in this project is actually only significant for visually pleasing simulation purposes, and isn't really that important. To calculate the effect of gravity of one body on another, we'll also need (in the body class): bool calcforce(body *B); void updateposition(); calcforce should use the formulas above to calculate the acceleration vector that results from the gravitational pull. Once we know the acceleration vector, we can use it to update the velocity vector. That is, velocity += acceleration * time One of Newton's laws says that the effect of gravity from different sources is additive, so we should do this between each pair of objects in the system. Once we calculate the effect of the pull of all the bodies in a system, we can then update the positions (updateposition) of the objects: position += velocity * time We will be writing out more detailed code in class, so don't panic if you don't follow. ------ Now, to draw an object in 3-dimensions, you need to understand at least a little of the program skeleton that I gave you. Drawing is enabled in our graphical toolkit GTK+ using two objects: One is an instance of a subclass of Gtk::Window and the other is an instance of a subclass of Gtk::DrawingArea. The "Window" contains the "DrawingArea". You won't have to worry about the Window class called "dwindow". The DrawingArea subclass, called "darea" is the main class of the simulation program. This is the class you will need to complete. When the instance of the darea class is first created, and for each time that it needs to be redrawn, a signal is sent to the application that triggers the calling of the "on_expose_event" function. This function controls how the window is to be drawn each time it's called. It's actually a tail-recursive function in that the call to "invalidate_rect" causes it to be called again. You can consider this function as the body of a while loop. However, since it is a function, remember that any variable you declare in it are local. To draw in three dimensions, you need to make use of the "boxworld" object called "box". This object encapsulates the math needed to convert imaginary 3D coordinates to discrete 2D screen coordinates. It contains functions that allows you to draw a three dimensional box, a line, and either a solid or outlined circle given 3D coordinates (see boxworld.h). The maximum coordinates are defined by box.XDIM, box.YDIM, and box.ZDIM. The color of the objects, determined by the last argument, can be whitegc, blackgc, rgc, ggc or bgc (red, green or blue). You can try to figure out other colors by calling "makecolorgc". Where do you draw to? A basic necessity of computer animation is the use of a "offscreen buffer" that allows you to construct one complete animation frame at a time. This buffer is represented by the "dbuffer" variable in the darea class. All your drawing calls should be made with dbuffer as the first argument. Only after all calls to dbuffer draws are complete will a call be made to render the entire buffer onto the actual screen. ------ Skeleton Program files: boxworld.h, boxworld.cpp : contains implementation of boxworld: compile with g++ -c boxworld.cpp `pkg-config gtkmm-2.0 --cflags --libs` to create "boxworld.o" asn2.h, asn2.cpp : skeleton drawing program These are the two files you'll need to add to. Pay attention to the constants #defined in asn2.h, especially: UDIST : real world distance (in meters) represented by each screen pixel. This value is important to convert real coordinates to screen coordinates. UTIME : real-world time represented by each simulation/animation step. There are options to use a second, minute, hour, day, etc ... This value is critical as it's the "time" value used to in the formulas that update the velocity and positions of objects. ------ To simulate the gravitational effect between a number of bodies, you should create an array of pointers to body objects. A nested loop through the array would allow you to compute the gravitational pull between every pair of object. ------ Drawing a mesh that can be affected by gravity. How do you create a gravity well simulation? I suggest you do the following: Create a one-dimensional array of 501 body objects Create an additional, 2D array (you determine the size). Each element of the 2D array should point to some object in the 1D array. Why have two arrays? Because certain aspects of the simulation are easier to deal with when having just a 1D array. Then to draw the mesh, simply write a nested loop that draws a pair of lines between Mesh[i][j] and Mesh[i+1][j] and between Mesh[i][j] and Mesh[i][j+1], assuming "Mesh" is the name of your 2D array. Instead of creating the entire mesh, you should start with just 2-3 body objects to test your program. For fun, you may want to use random numbers for the intial coordinates of your objects. To generate random numbers using the Gtk+ toolkit: Glib::Rand rnd; // rnd is a random number generation object ... int x = rnd.get_int_range(2,10); will generate an integer between 2 and 10. Consult the online documentation for other methods of Glib::Rand ------- Summary: For this assignment, you need to add the following to the darea class: 1. An array of body objects. 2. modifications to the "iroutine" initialization method, which initializes this array (i.e., create the objects). 3. modifications to the "animate" method that computes each step of the animation. Use the "box" object to draw the graphics. Look at boxworld.h to find out how to use the drawline and drawcircle methods. The "gcs" blackgc, whitegc, rgc, bgc, and ggc allow to to draw in different colors. This part may involve the creation of other functions as well. Caution: when drawing to the 3D box, you need to convert your objects' real-space coordinates to valid box coordinates by dividing them by UDIST. This holds true for all but the radius, which is given in units of pixels in the first place.