Planning a Gravity Simulator Top-Down

Defining a Universe object

We will use top-down thinking to design objects for our gravity simulator. That is, although we know that we will need a Body object to represent heavenly bodies, we start by planning a Universe object. This object should contain an array of Body objects as one of our fields, but we will provide additional fields.

type Universe
    bodies []Body

We will model a Universe on an infinite two-dimensional plane; adding a third dimension will not make our physics simulation more challenging, but it will make visualizing the gravity simulation more difficult. For that matter, although the plane is infinite, we will only be able to visualize a finite window of this plane when rendering a visualization. We will therefore establish a rendering window as a square whose width is a Universe field width.

type Universe
    bodies []Body
    width float

Finally, in the next lesson, we will say more about the physics engine that we will build, which depends upon a gravitational constant that dictates the strength of gravity between any two bodies in the universe. Establishing this gravitational constant as a Universe field will allow us to play around with the force of gravity later by changing this field.

type Universe
    bodies []Body
    width float
    gravitationalConstant float

Establishing the fields of a heavenly body

As for a Body object, we will first assign it a name, like "Trisolaris", "Europa", or "Earth".

type Body
    name string

We also should assign a Body a mass and a radius.

type Body
    name string
    mass float
    radius float

Thinking ahead to building a physics engine, we will need to maintain the position of each Body in two-dimensional space, as well as its velocity and acceleration. We will say more about this in the next section, but both velocity and acceleration will be represented in terms of their x- and y-components. As a result, position, velocity, and acceleration can each be represented as an ordered pair, which we define as its own object.

type Body
    name string
    mass float
    radius float
    position OrderedPair
    velocity OrderedPair
    acceleration OrderedPair
type OrderedPair
    x float
    y float

Finally, we add a color attribute to a Body. As we saw in the last chapter’s graphics code alongs, every rectangular pixel on a computer screen emits a single color formed as a mixture of differing amounts of the three primary colors of light: red, green, and blue (hence the acronym “RGB”). We can therefore represent the intensity of each primary color in a pixel as an integer between 0 and 255, inclusively, with larger integers corresponding to greater intensities. The figure below shows a collection of colors along with triples of integers corresponding to their RGB representations.

A collection of colors along with their RGB codes. This table corresponds to mixing colors of light instead of pigment, which causes some non-intuitive effects; for example, yellow is formed by mixing equal parts red and green. The last six colors appear muted because their channel values are half-strength compared to a full 255. If all three colors are mixed in equal proportions, then we obtain a color on the gray scale between white (255, 255, 255) and black (0, 0, 0). Source: Excel at Finance.

We now complete our implementation of a Body by adding three integer fields corresponding to each color channel.

type Body
    name string
    mass float
    radius float
    position OrderedPair
    velocity OrderedPair
    acceleration OrderedPair
    red int
    green int
    blue int

Starting our implementation design

Now that we have designed the objects for our simulator, let us return to the central problem that we want to solve and state this problem a bit more formally given what we have learned about object-oriented programming.

Gravity Simulator Problem

Input: A Universe object initialUniverse, an integer numGens, and a a floating-point number time.

Output: An array timePoints of numGens + 1 Universe objects, where timePoints[0] is initialUniverse, and timePoints[i] is obtained from timePoints[i-1] by approximating the results of the force of gravity over a time interval equal to time (in seconds).
STOP: Try sketching a solution to the Gravity Simulator Problem top-down in pseudocode.

As we have seen before in top-down design, laziness is a virtue. Our highest-level function for solving the Gravity Simulator Problem, SimulateGravity(), delegates most of its work to an UpdateUniverse() function, which takes as input a Universe object u and a decimal time and returns the updated Universe resulting from applying the gravity simulation over a single time interval of time seconds.

SimulateGravity(initialUniverse, numGens, time)
    timePoints ← array of numGens+1 Universe objects
    timePoints[0] ← initialUniverse
    for every integer i between 1 and numGens
        timePoints[i] ← UpdateUniverse(timePoints[i-1], time)
    return timePoints

As for UpdateUniverse(), it is just as lazy, passing its own work to not one but four subroutines. One such function copies over one Universe object’s fields into a new Universe, whereas we use the other three to update the acceleration, velocity, and position of a single Body object b.

UpdateUniverse(currentUniverse, time)
    newUniverse ← CopyUniverse(currentUniverse)
    for every body b in newUniverse
        b.acceleration ← UpdateAcceleration(newUniverse, b)
        b.velocity ← UpdateVelocity(b, time)
        b.position ← UpdatePosition(b, time)
    return newUniverse 

We have now reached a point where we need to understand more about the gritty details of gravity in order to implement our simulator. Has it been a long time since high school? Or perhaps you didn’t learn physics at all, since after all, only 42% of American high school students take a physics course? Either way, let’s do some physics!

Page Contents
Scroll to Top