コード例 #1
0
    def __init__(self, astronomical_object, initial_distance=1000):
        '''
        Initializes the FreeFallSimulation instance.

        @precondition: none.

        @postcondition: The instance is ready to use. You should
        bind_to_root though before you use the object.

        @param initial_distance: The initial distance of the falling
        object in meters. Defaults to 1000m.

        @param astronomical_object: The astronomical object the
        falling_object will free fall toward.
        '''
        self._canvas = None
        self._velocity = None
        self._velocity_label = None

        self.falling_object = \
            CanvasedFallingObject(FreeFallSimulation.WIDTH,
                                  FreeFallSimulation.FREE_FALL_HEIGHT,
                                  initial_distance=initial_distance)
        self.astronomical_object = astronomical_object
コード例 #2
0
class FreeFallSimulation(object):
    '''
    A representation of a free fall Simulation.

    Represents a free fall simulation for a single falling object and
    astronomical object.
    '''

    '''The height of the Free Fall part of the canvas.'''
    FREE_FALL_HEIGHT = 400

    '''The height of the planet part of the canvas.'''
    PLANET_IMG_HEIGHT = 150

    '''The width of the entire canvas.'''
    WIDTH = 150

    def __init__(self, astronomical_object, initial_distance=1000):
        '''
        Initializes the FreeFallSimulation instance.

        @precondition: none.

        @postcondition: The instance is ready to use. You should
        bind_to_root though before you use the object.

        @param initial_distance: The initial distance of the falling
        object in meters. Defaults to 1000m.

        @param astronomical_object: The astronomical object the
        falling_object will free fall toward.
        '''
        self._canvas = None
        self._velocity = None
        self._velocity_label = None

        self.falling_object = \
            CanvasedFallingObject(FreeFallSimulation.WIDTH,
                                  FreeFallSimulation.FREE_FALL_HEIGHT,
                                  initial_distance=initial_distance)
        self.astronomical_object = astronomical_object

    def _kaboom(self):
        '''
        Displays the kaboom image if it is not shown.

        @precondition: none.

        @postcondition: The kaboom image is displayed over the planet
        image if it is not already there.
        '''
        if not self._kaboom_image.on_canvas():
            height = FreeFallSimulation.FREE_FALL_HEIGHT
            self._kaboom_image.add_to_canvas(self._canvas,
                                             0,
                                             height,
                                             anchor=tkinter.NW)

    def _unkaboom(self):
        '''
        Removes the kaboom image if it is not shown.

        @precondition: none.

        @postcondition: The kaboom image is removed over the planet
        image if it is there.
        '''
        if self._kaboom_image.on_canvas():
            self._kaboom_image.remove_from_canvas(self._canvas)

    def bind_to_root(self, root, row, col):
        '''
        binds this instances canvas and labels to the given root and
        sets them up.

        @precondition: none.

        @postcondition: A canvas object is place under the root widget
        using a grid and is placed in col, row.

        @param root: The root tkinter widget that this canvas will
        attach to.

        @param col: The column number in the root's grid where this
        canvas should be place.

        @param row: The row number in the root's grid where this
        canvas should be placed.
        '''

        # Create a label with the astronomical object name.
        self._label = tkinter.Label(root, text=self.astronomical_object.name)
        self._label.grid(column=col, row=row, sticky=(tkinter.W, tkinter.E))

        # Create the canvas and add it to the grid expanding to all
        # four corners.
        height = (FreeFallSimulation.FREE_FALL_HEIGHT +
                  FreeFallSimulation.PLANET_IMG_HEIGHT)
        self._canvas = tkinter.Canvas(root,
                                      width=FreeFallSimulation.WIDTH,
                                      height=height,
                                      bg='black')
        self._canvas.grid(column=col,
                          row=row + 1,
                          sticky=(tkinter.N, tkinter.W, tkinter.E, tkinter.S))

        self.falling_object.add_to_canvas(self._canvas)

        # Add the planet image at the bottom
        height = FreeFallSimulation.FREE_FALL_HEIGHT
        self.astronomical_object.image.add_to_canvas(self._canvas,
                                                     0,
                                                     height,
                                                     anchor=tkinter.NW)

        # Add a label for the gravitational force just below the
        # canvas. We use a StringVar because it will update the label
        # automatically.
        self._g = tkinter.StringVar()
        self._g.set("{0:.3f}".format(self.astronomical_object.gravity))
        self._g_label = tkinter.Label(root, textvariable=self._g)
        self._g_label.grid(column=col, row=row + 2,
                           sticky=(tkinter.N, tkinter.W, tkinter.E, tkinter.S))

        # Add a label for the velocity just below the canvas. We use a
        # StringVar because it will update the label automatically.
        self._velocity = tkinter.StringVar()
        self._velocity.set('0.0')
        self._velocity_label = tkinter.Label(root, textvariable=self._velocity)
        self._velocity_label.grid(column=col, row=row + 3,
                                  sticky=(tkinter.N, tkinter.W,
                                          tkinter.E, tkinter.S))

        # Add a label for the time to land below the canvas. We use a
        # StringVar because it will update the label automatically.
        self._ttl = tkinter.StringVar()
        self._ttl.set('0.0')
        self._ttl_label = tkinter.Label(root, textvariable=self._ttl)
        self._ttl_label.grid(column=col, row=row + 4,
                             sticky=(tkinter.N, tkinter.W,
                                     tkinter.E, tkinter.S))

        # Add a label for the distance to land below the canvas. We
        # use a StringVar because it will update the label
        # automatically.
        self._d = tkinter.StringVar()
        self._d.set('0.0')
        self._d_label = tkinter.Label(root, textvariable=self._d)
        self._d_label.grid(column=col, row=row + 5,
                           sticky=(tkinter.N, tkinter.W,
                                   tkinter.E, tkinter.S))

        self._kaboom_image = FreeFallCanvasImage('kaboom', 'kaboom.gif')

    def move_falling_object(self, time=0):
        '''
        Move the falling object to the position at the specified time.

        Moves the falling object to its proper position at the given
        time and updates all the associated calculations.

        @precondition: none.

        @postcondition: The falling object is moved on the canvas to
        its position at the given time and the other calculates are
        updated to represent its new location.

        @param time: The time in seconds to where the ball should be
        moved.

        @raise FreeFallException: If the canvas is not initialized,
        this is thrown. Calling 'bind_to_root' first will solve this
        problem.
        '''

        # We need a canvas to do anything.
        if self._canvas is None:
            from freefallsimulator import FreeFallException
            raise FreeFallException("Tkinter objects not initialized. " + 
                                    "Call 'bind_to_root' first.")

        if self.has_collided(time):
            # If it's collided, set the time to 0. The resolution may not
            # be sufficient to show an exact 0.
            self._ttl.set('{0:.3f}'.format(float(0.0)))
            self._d.set('{0:.3f}'.format(float(0.0)))
            self._kaboom()
            self.falling_object.remove_object()
        else:
            # Prepare some calculations for display.
            g = self.astronomical_object.gravity
            t = time
            v = self.falling_object.calculate_velocity_at_time(gravity=g,
                                                               time=t)
            t0 = self.falling_object.calculate_time_to_surface(gravity=g)
            d = self.falling_object.calculate_distance_to_planet(time=t,
                                                                 gravity=g)

            # Move it.
            self.falling_object.create_object()
            self.falling_object.move_object(gravity=g, time=t)
            self._velocity.set('{0:.3f}'.format(v))
            self._ttl.set('{0:.3f}'.format(t0))
            self._d.set('{0:.3f}'.format(d))

            # We aren't at the end, so make sure we aren't displaying
            # the kaboom.
            self._unkaboom()

    def has_collided(self, time=0):
        '''
        Determines if a collision has occurred at the given time.

        @precondition: none.

        @postcondition: none.

        @param time: The time in seconds to where the ball should be
        checked for a collision.

        @return: True if a collision has occurred at or before the
        given time, False otherwise.
        '''

        i = self.falling_object.initial_distance
        g = self.astronomical_object.gravity

        tts = self.falling_object.calculate_time_to_surface(distance=i,
                                                            gravity=g)

        return tts <= time