Exemplo n.º 1
0
def init_graph():
    vpython.graph(
        title="Beads on a Cycloid Wire",
        xtitle="Time (s)",
        ytitle="180<sup>o</sup> - Cycloid Angle (<sup>o</sup>)",
        fast=False,
    )
    return
Exemplo n.º 2
0
def init_graph():
    vpython.graph(
        title="Ball on a Spring",
        xtitle="Time (s)",
        ytitle="Displacement (m)",
        fast=False,
    )
    return vpython.gcurve(color=vpython.color.red, width=2)
Exemplo n.º 3
0
def init_graphs():
    vpython.graph(
        title="Energy of Earth's Orbit",
        xtitle="Time (days)",
        ytitle="Energy (J)",
        fast=False,
    )
    return {
        "potential": vpython.gcurve(color=vpython.color.blue, width=2),
        "kinetic": vpython.gcurve(color=vpython.color.red, width=2),
        "total": vpython.gcurve(color=vpython.color.magenta, width=2),
    }
Exemplo n.º 4
0
def _make_plot(theta, display_width, display_height):
    """ Makes the VPython graph.

    :return: The graph object.
    """

    graph(width=display_width / 2 - 10,
          height=display_height,
          background=color.white,
          align='right',
          xtitle='Time (s)',
          ytitle='Angle (\u00b0)')
    f = gcurve(color=color.red, dot=True, interval=100)
    f.plot(0, math.degrees(theta))
    return f
Exemplo n.º 5
0
def draw_graph_lagrange():
    # przygotowanie wykresu
    gd = graph(title='Interpolacja Lagrange\'a')
    f1 = gcurve(graph=gd, color=color.cyan)
    dots = gdots(graph=gd, color=color.red)
    f2 = gcurve(graph=gd, color=color.green)

    # tablica do przechowywania wylosowanych punktów
    initial_points = []

    # zmienna pomocnicza używana przy losowaniu punktów wielomianu
    index = 0

    # wyliczanie wartości funkcji podstawowej w tym przypadku cos(2*x) * exp(-0.2 * x)
    field_end = 8.05
    for x in arange(0, field_end, 0.1):
        y = cos(2 * x) * exp(-0.2 * x)
        f1.plot(x, y)

        #losowanie punktów do wyliczania interpolacji - odbywa kiedy x znajduje się w przedziale 1..field_end-1
        if 1 < x <= field_end - 1 and index % 2 == 0 and bool(getrandbits(1)):
            initial_points.append({'x': x, 'y': y})
            dots.plot(x, y)
        index = index + 1

    # wyliczanie i rysowanie interpolacji
    for point in lagrange_interpolation(initial_points, field_end):
        f2.plot(point['x'], point['y'])
Exemplo n.º 6
0
def count():
    # algroytm działa także dla innych wielkości kwadratu r=a/2
    r = 0.5
    gd = graph(ymin=0, ymax=2 * r + 0.5, xmin=0, xmax=2 * r + 0.5)
    f1 = gcurve(graph=gd, color=color.cyan)
    f2 = gcurve(graph=gd, color=color.cyan)
    dots = gdots(graph=gd, color=color.red, radius=2)

    for x in arange(0, 2 * r + 0.001, 0.01):
        f1.plot(x, circle_point_lower(x, r))
        f2.plot(x, circle_point_upper(x, r))

    points_count = randrange(200, 300)
    ok_points = 0
    while x <= points_count:
        x = x + 1
        point = draw_point(2 * r)
        dots.plot(point[0], point[1])
        if circle_point_upper(point[0], r) >= point[1] >= circle_point_lower(
                point[0], r):
            ok_points = ok_points + 1

    field = ok_points / points_count
    pi = field / (r**2)
    f1.label = 'pi = ' + str(pi)
    f2.label = 'field = ' + str(field)
    gd.height = 450
    gd.width = 450
Exemplo n.º 7
0
def draw_graph(start, end, n=10):
    f1 = gcurve(graph=graph(title='Metoda trapezów'), color=color.cyan)

    for i in arange(start, end + 0.001, 0.01):
        x = round(i, 3)
        f1.plot(x, integral_function(x))

    f1.label = 'field = ' + str(2 * integral(start, end, n))
    def plot_supplimentary_information(self):
        self.plots = True
        atom_position_graph = vp.graph(title='Absolute Displacement of Red Atom', 
                                       xtitle=r't [s]', ytitle=r'|r(t)-vector| [m]', fast=True,
                                       legend=True)
        atom_velocity_graph = vp.graph(title='Absolute Speed of Red Atom', xtitle=r't [s]', ytitle=r'|r(t)-dot-vector| [m/s]', fast=True, 
                                       legend=True)
        atom_phasespace     = vp.graph(title='Phase space of Red Atom', ytitle=r'|r(t)-vector| [m]', fast=True, 
                                       legend=True, xtitle=r'|r(t)-dot-vector| [m/s]')
        label_constant = "Spring Constant: {} N/m"
        self.atom_position_points = vp.gcurve(graph=atom_position_graph, color=vp.color.red, size=0.1, 
        label=label_constant.format(self.atom_list[0].force_constant))
        
        self.atom_velocity_points = vp.gcurve(graph=atom_velocity_graph, color=vp.color.blue, size=0.1, 
            label=label_constant.format(self.atom_list[0].force_constant))

        self.atom_phasespace_points = vp.gcurve(graph=atom_phasespace, color=vp.color.green, size=0.1, 
            label=label_constant.format(self.atom_list[0].force_constant))
def draw_function_average(start, end, tries=200):
    g = graph(title='Metoda próbkowania średniej')
    f1 = gcurve(graph=g, color=color.cyan)
    dots = gdots(graph=g, color=color.red)

    points = count_points(start, end)
    for point in points['points']:
        f1.plot(point)

    sampling = average_sampling(start, end, tries)
    for point in sampling['points']:
        dots.plot(point)

    f1.label = '2 * wartość całki= ' + str(2 * sampling['value'])
Exemplo n.º 10
0
def newton_czybyszew(nodes_amount):
    gd = graph(title='Interpolacja Newtona\'a')
    f1 = gcurve(graph=gd, color=color.cyan)
    dots = gdots(graph=gd, color=color.red)
    f2 = gcurve(graph=gd, color=color.green)

    # wyliczanie wartości funkcji podstawowej w tym przypadku cos(2*x) * exp(-0.2 * x)
    field_end = 8.05
    for x in arange(0, field_end, 0.1):
        y = origin_newton_function(x)
        f1.plot(x, y)

    # tablica do przechowywania wylosowanych punktów
    initial_points = []
    for x in czybyszew_points(nodes_amount, 0, field_end):
        y = origin_newton_function(x)
        dots.plot(x, y)
        initial_points.append({'x': x, 'y': y})

    # wyliczanie i rysowanie interpolacji
    for point in newton_interpolation(initial_points, field_end):
        f2.plot(point['x'], point['y'])
def draw_function_simple(start, end, tries=200):
    g = graph(title='Metoda prostego próbkowania')
    f1 = gcurve(graph=g, color=color.cyan)
    dots = gdots(graph=g, color=color.red)

    points = count_points(start, end)
    for point in points['points']:
        f1.plot(point)

    correct_points = 0

    for _ in range(0, tries):
        point = draw_point(start, end - start)
        dots.plot(point)
        if point[1] <= integral_function(point[0]):
            correct_points = correct_points + 1

    # max_val jest największą wartością funkcji,
    # odpowiada zmiennej H która musi być większa od f(x) dlatego koryguję o 0.1
    p_gross = count_gross_field(end - start, points['max_val'] + 0.0001)
    p = correct_points / tries * p_gross

    f1.label = '2 * wartość całki= ' + str(2 * p)
    dots.label = 'pole całkowite= ' + str(p_gross)
Exemplo n.º 12
0
from vpython import graph
import time
#_________-ajustando la pantalla para la escena
escena = canvas(width=800, height=500)
escena.title = "Tiro Parabolico ITI Mecatronica 2do sem"
escena.background = color.white
escena.center = vector(0, 2, 0)

#______________________Agregando un grafico
"""El siguiente grafico mostrara la evolucion de la energia cinetica, potencial y total a lo 
largo del tiempo en que la esfera es expulsada y toca el suelo"""
graph1 = graph(x=0,
               y=0,
               width=500,
               height=350,
               title="Energia vs tiempo",
               xtitle='t',
               ytitle='E',
               foreground=color.black,
               background=color.white)

potencial = gcurve(graph=graph1, color=color.blue)
cinetica = gcurve(graph=graph1, color=color.red)
etotal = gcurve(graph=graph1, color=color.green)

#_________________determinando vectores y variables
pos = vector(-10, 0, 0)
vel = vector(10, 10, 0)  #vectores de poscicion y velocidad

grav = 10  #gravedad
m = 1
Exemplo n.º 13
0
import numpy as np
import vpython as vp

class ghistogram:
	def __init__(self, graph, bins, color = vp.color.red):
		self.bins = bins
		self.slotnumber = len(bins)
		self.slotwidth = bins[1]-bins[0]
		self.n = 0
		self.slots = np.zeros(len(bins))
		self.bars = vp.gvbars(graph = graph, delta=self.slotwidth, color=color)

	def plot(self, data):
		currentslots = np.zeros(self.slotnumber)
		for value in data:
			currentslots[min(max(int((value - self.bins[0])/self.slotwidth), 0), self.slotnumber-1)] += 1
		self.slots = (self.slots * self.n + currentslots)/(self.n + 1)
		self.n += 1
		if self.n == 1:
			for (currentbin, barlength) in zip(self.bins, self.slots):
				self.bars.plot( pos = (currentbin, barlength))
		else:
			self.bars.data = list(zip(self.bins, self.slots))

if __name__ == '__main__':
	vdist = vp.graph(width = 450)
	observation = ghistogram(graph = vdist, bins = np.arange(1, 3, 0.5))
	observation.plot(data=[1.2, 2.3, 4])
	observation.plot(data=[1, 1.7, 2.6])
	observation.plot (data=[-0.5, 2, 2.3])
Exemplo n.º 14
0
import vpython as vp
from robot_imu import RobotImu, ImuFusion
from delta_timer import DeltaTimer
import imu_settings

imu = RobotImu(gyro_offsets=imu_settings.gyro_offsets,
               mag_offsets=imu_settings.mag_offsets)
fusion = ImuFusion(imu)

vp.graph(xmin=0, xmax=60, scroll=True)
graph_pitch = vp.gcurve(color=vp.color.red)
graph_roll = vp.gcurve(color=vp.color.green)
graph_yaw = vp.gcurve(color=vp.color.blue)

timer = DeltaTimer()
while True:
    vp.rate(100)
    dt, elapsed = timer.update()
    fusion.update(dt)
    graph_pitch.plot(elapsed, fusion.pitch)
    graph_roll.plot(elapsed, fusion.roll)
    graph_yaw.plot(elapsed, fusion.yaw)
Exemplo n.º 15
0
import vpython as vp
import logging
import time
from robot_imu import RobotImu

logging.basicConfig(level=logging.INFO)
imu = RobotImu()


pr = vp.graph(xmin=0, xmax=60, scroll=True)
graph_pitch = vp.gcurve(color=vp.color.red, graph=pr)
graph_roll = vp.gcurve(color=vp.color.green, graph=pr)

xyz = vp.graph(xmin=0, xmax=60, scroll=True)
graph_x = vp.gcurve(color=vp.color.orange, graph=xyz)
graph_y = vp.gcurve(color=vp.color.cyan, graph=xyz)
graph_z = vp.gcurve(color=vp.color.purple, graph=xyz)

start = time.time()
while True:
    vp.rate(100)
    elapsed = time.time() - start
    pitch, roll = imu.read_accelerometer_pitch_and_roll()
    raw_accel = imu.read_accelerometer()
    graph_pitch.plot(elapsed, pitch)
    graph_roll.plot(elapsed, roll)
    print(f"Pitch: {pitch:.2f}, Roll: {roll:.2f}")
    graph_x.plot(elapsed, raw_accel.x)
    graph_y.plot(elapsed, raw_accel.y)
    graph_z.plot(elapsed, raw_accel.z)
Exemplo n.º 16
0
import vpython as vp
import time
from robot_imu import RobotImu
import imu_settings

imu = RobotImu(gyro_offsets=imu_settings.gyro_offsets)

vp.graph(xmin=0, xmax=60, ymax=360, ymin=-360, scroll=True)
graph_x = vp.gcurve(color=vp.color.red)
graph_y = vp.gcurve(color=vp.color.green)
graph_z = vp.gcurve(color=vp.color.blue)

start = time.time()
while True:
    vp.rate(100)
    elapsed = time.time() - start
    gyro = imu.read_gyroscope()
    print(f"Gyro x: {gyro.x:.2f}, y: {gyro.y:.2f}, z: {gyro.z:.2f}")
    graph_x.plot(elapsed, gyro.x)
    graph_y.plot(elapsed, gyro.y)
    graph_z.plot(elapsed, gyro.z)
Exemplo n.º 17
0
#!/usr/bin/env python3

from __future__ import division
import vpython as vp

# CANVAS
scene = vp.canvas(fullscreen=True)

# GRAPH WINDOW
gr = vp.graph(title="Graph Window Objects",
              x=0,
              y=0,
              xtitle="x_title",
              ytitle="y_title",
              background=vp.color.white)

# RED BOX
redbox = vp.box(pos=vp.vector(4, 2, 3),
                size=vp.vector(8, 4, 6),
                color=vp.color.red)
            if vp.mag(delta) > vp.mag(self.amplitude):
                self._amp = delta
                #print the maxium distance from rest position, during the entire oscillation
                print("\rMeasured amplitude:", vp.mag(delta), end='')
        else:
            self._amp = delta


# create oscillator instance
osc = oscillator(RADIUS, initial_position, initial_velocity)

time = 0

vp.graph(scroll=True,
         fast=False,
         xmin=0,
         xmax=50,
         xtitle='times(s)',
         ytitle='delta (m)')

g0 = vp.gcurve()
#g1 = vp.gcurve(color = vp.color.red)

while True:

    vp.rate(100)

    # add elastic force
    delta = osc.mass.pos - rest_position
    elastic_force = -(K * delta) / M

    # add fluid resistance
Exemplo n.º 19
0
# Bessel . py
from numpy import *
from vpython import *
from vpython import graph
Xmax = 40.0
Xmin = 0.25
step = 0.1
order = 10
start = 50  #
graph1 = graph(width=500,
               height=500,
               title='Sperical Bessel,L=1c(red),10',
               xtitle='x',
               ytitle='j (x)',
               xmin=Xmin,
               xmax=Xmax,
               ymin=-0.2,
               ymax=0.5)
funct1 = gcurve(color=color.red)
funct2 = gcurve(color=color.green)


def down(x, n, m):
    j = zeros((start + 2), float)
    j[m + 1] = j[m] = 1.0
    for k in range(m, 0, -1):
        j[k - 1] = ((2.0 * k + 1.0) / x) * j[k] - j[k + 1]
    scale = (sin(x) / x) / j[0]
    return j[n] * scale

Exemplo n.º 20
0
from __future__ import division
import vpython as vp
import numpy as np

# Animation/Simulation scene
scene = vp.canvas(x=0, y=0, width=800, height=450)
#scene.lights = []   # get rid of default lights

scene.ambient = vp.color.gray(0.2)

# Graph Analytical
win1 = vp.graph(x=700,
                y=0,
                width=800,
                height=450,
                title="Single Simple Pendulum",
                xtitle="x",
                ytitle="y",
                foreground=vp.color.black,
                background=vp.color.white,
                stereo="redcyan")

#
initial_position = vp.vector(-4, 0, 0)
initial_velocity = vp.vector(0, 0, 0)

# Define Ball/Blob
ball = vp.sphere(pos=initial_position,
                 radius=0.5,
                 color=vp.color.cyan,
                 opacity=0.8,
                 make_trail=True,
Exemplo n.º 21
0
from vpython import *
from numpy import *
from vpython import graph
import random

jmax = 20
x = 0.0
y = 0.0
graph1 = graph(width=500,
               height=500,
               title='Random Walk',
               xtitle='x',
               ytitle='y')
pts = gcurve(color=color.yellow)
for i in range(0, jmax + 1):
    pts.plot(pos=(x, y))
    x += (random.random() - 0.5) * 2
    y += (random.random() - 0.5) * 2
    pts.plot(pos=(x, y))
Exemplo n.º 22
0
# must change further arguments to implement different initial conditions

# Universal constant
gravitational_constant = 1.  # 6.67e-11 N m^2 / kg^2

# Define animation parameters
animation_time_step = 0.01  # seconds
rate_of_animation = 1 / animation_time_step
time_step = 2.
stop_time = 20000.


# mars_initial
mars_position = (((mars_initial_velocity.x**2) + (mars_initial_velocity.y**2)) / 2) * time_step

position_plot_field = vp.graph(title='Position vs. Time', xtitle='Time', ytitle='Position', fast=True)
position_points = vp.gcurve(graph=position_plot_field, color=vp.color.green, size=0.1)
mars_position_curve = vp.gcurve(graph=position_plot_field,
                               data=[[0, mars_position], [stop_time, mars_position]],
                               color=vp.color.green)
velocity_plot_field = vp.graph(title='Velocity vs. Time', xtitle='Time', ytitle='Velocity', fast=True)
mars_velocity_points = vp.gcurve(graph=velocity_plot_field, color=vp.color.green, size=0.1)

phase_space = vp.graph(title='Phase Space Diagram', xtitle='Position', ytitle='Velocity', fast=True)
phase_space_points = vp.gcurve(graph=phase_space, color=vp.color.green, size=0.1)

# mars_initial1
mars1_position = (((mars_initial_velocity1.x**2) + (mars_initial_velocity1.y**2)) / 2) * time_step

position1_plot_field = vp.graph(title='Position1 vs. Time', xtitle='Time', ytitle='Position1', fast=True)
position1_points = vp.gcurve(graph=position1_plot_field, color=vp.color.blue, size=0.1)
Exemplo n.º 23
0
def optimized_animate_TRod(y, dt, rb):
    """ Animates RigidBody's vpython object (optimized version).

    TODO: To finally optimize, make sure vp.rate is consistent with real time and make sure to only display
    results needed. Exclude non-displayed results that are left out because of frame-rate."""
    frame_rate = dt**-1

    rb.create_vpython_object()
    animation_graph = vp.graph(scroll=True,
                               fast=True,
                               xmin=-5,
                               xmax=5,
                               ymin=-2.5,
                               ymax=2.5)
    omega1_plot = vp.gcurve(color=vp.color.red)
    omega2_plot = vp.gcurve(color=vp.color.green)
    omega3_plot = vp.gcurve(color=vp.color.blue)
    plot_interval = 1
    t = 0
    tot_axes = len(rb.R)
    omegas1 = y[:, 0].copy()
    omegas2 = y[:, 1].copy()
    omegas3 = y[:, 2].copy()
    np_axes = y[:, 3:12].copy()
    np_axes = np_axes.reshape((len(np_axes), 3, 3))
    vp_axes = []

    for R in np_axes:
        vp_R = []
        for axis in R:
            vp_R.append(vp.vector(*axis))
        vp_axes.append(vp_R)

    for k in range(len(y)):
        vp.rate(frame_rate)  # set framerate

        omega1 = omegas1[k]
        omega2 = omegas2[k]
        omega3 = omegas3[k]
        R = vp_axes[k]

        # Plot curves.
        if not k % plot_interval:  # Ex: If plot_interval = 3, only plot every 3rd point.

            omega1_plot.plot(t, omega1)
            omega2_plot.plot(t, omega2)
            omega3_plot.plot(t, omega3)

        t += dt

        # Loop over axes and rotate object and arrows.
        # Rotate x_arrow
        old_length = rb.vp_object_axes[0].length
        rb.vp_object_axes[0].axis = R[0]
        rb.vp_object_axes[0].length = old_length

        # Rotate y_arrow
        old_length = rb.vp_object_axes[1].length
        rb.vp_object_axes[1].axis = R[1]
        rb.vp_object_axes[1].length = old_length

        # Rotate z_arrow
        old_length = rb.vp_object_axes[2].length
        rb.vp_object_axes[2].axis = R[2]
        rb.vp_object_axes[2].length = old_length

        # # Rotation version (don't use both axis-setting and rotation)
        # rb.vp_object.rotate(angle=omega1*dt, axis=R[0])
        # rb.vp_object.rotate(angle=omega2*dt, axis=R[1])
        # rb.vp_object.rotate(angle=omega3*dt, axis=R[2])

        # Axis-setting version (don't use both axis-setting and rotation)
        rb.vp_object.axis = R[0]
        rb.vp_object.up = R[1]

        # Update angular momentum arrows.
        L = rb.I_body @ np.array(
            (omega1, omega2,
             omega3))  # Do this calc. outside loop for better optimization.
        L /= np.linalg.norm(L) * 1 / 5
        L_x = np.zeros(3)
        L_x[0] = L[0]
        L_y = np.zeros(3)
        L_y[1] = L[1]
        L_z = np.zeros(3)
        L_z[2] = L[2]
        rb.L_arrow.axis = vp.vector(*L)
        rb.L_components[0].axis = vp.vector(*L_x)
        rb.L_components[1].axis = vp.vector(*L_y)
        rb.L_components[
            1].pos = rb.L_components[0].pos + rb.L_components[0].axis
        rb.L_components[2].axis = vp.vector(*L_z)
        rb.L_components[
            2].pos = rb.L_components[1].pos + rb.L_components[1].axis
## Archivo para crear los escenarios del programa
# -*- coding: utf-8 -*-

import vpython as vp
import modulo_particulas as mod
import numpy as np

particulas = []

s = "Grafica de la simulacion actual"
rutherford = vp.graph(title=s,
                      xtitle='angulo',
                      ytitle='# particulas',
                      fast=False,
                      width=600)


# Funcion que elimina los objetos del escenario
def limpiar_escenario():
    for obj in vp.scene.objects:
        obj.visible = False
        if obj.make_trail:
            obj.clear_trail()
        del obj
    global particulas, rutherford
    particulas.clear()
    rutherford.delete()


# Para escenario 2 y 3
datos = np.loadtxt("longitudes_de_onda.csv",
Exemplo n.º 25
0
from vpython import *
from vpython import graph
import random




graph1 = graph(title='Monte Carlo Circle', xtitle= 'x', ytitle = 'y')



Nbox=0
Ncirc=0
for i in range(10**4):
    
    x=random.choice([2*random.random(),0*random.random()])
    y=random.choice([2*random.random(),0*random.random()])
    if y-x*sin(x)*sin(x)<=0:
        yes=(x,y,0)
        points(pos=yes)
        rate(100)
        #rate(30)
        #X=append(X,x)
        #Y=append(Y,y)
        Ncirc+=1
    else:
        no=(x,y,0)
        points(pos=no,color=color.green)
        Nbox+=1

result=4*(Ncirc)/(Nbox+Ncirc)
Exemplo n.º 26
0
from vpython import *
from vpython import graph
import random
from pydub import AudioSegment
from pydub.playback import play
wavfile = 'geiger.wav'
sound = AudioSegment.from_file(wavfile)

lambda1 = 0.005
max = 100
time_max = 500
seed = 68111
number = nloop = max
graph1 = graph(title='Spontaneous Decay', xtitle='Time', ytitle='Number')
decayfunc = gcurve(color=color.green)
for time in arange(0, time_max + 1):

    for atom in arange(1, number + 1):

        decay = random.random()
        if (decay < lambda1):

            nloop = nloop - 1
            play(sound)

    number = nloop
    decayfunc.plot(pos=(time, number))
    rate(30)
Exemplo n.º 27
0

def barx(v):
    return int(v / deltav)  # index into bars array


nhisto = int(4500 / deltav)
histo = []
for i in range(nhisto):
    histo.append(0.0)
histo[barx(pavg / mass)] = Natoms

gg = vp.graph(width=win,
              height=0.4 * win,
              xmax=3000,
              align='left',
              xtitle='speed, m/s',
              ytitle='Number of atoms',
              ymax=Natoms * deltav / 1000)

theory = vp.gcurve(color=vp.color.cyan)
dv = 10
for v in range(0, 3001 + dv, dv):  # theoretical prediction
    theory.plot(v, (deltav / dv) * Natoms * 4 * vp.pi *
                ((mass / (2 * vp.pi * k * T))**1.5) *
                vp.exp(-0.5 * mass * (v**2) / (k * T)) * (v**2) * dv)

accum = []
for i in range(int(3000 / deltav)):
    accum.append([deltav * (i + .5), 0])
vdist = vp.gvbars(color=vp.color.red, delta=deltav)
Exemplo n.º 28
0
def main():
    box_size = 10
    box_thickness = 1
    ball_radius = 0.5
    # Set the axis of each side to be a perpendicular unit vector pointing into
    # the box. That'll allow us to handle all the walls in a loop, rather than
    # spelling them out
    axes = [
        vpython.vector(1, 0, 0),
        vpython.vector(-1, 0, 0),
        vpython.vector(0, 1, 0),
    ]
    box_sides = []
    for axis in axes:
        side = vpython.box(
            pos=-0.5 * box_size * axis,
            axis=axis,
            height=box_size,
            width=box_size,
        )
        box_sides.append(side)

        vpython.arrow(
            pos=-0.5 * box_size * axis,
            axis=axis,
            color=vpython.color.blue,
        )

    # Initial position chosen randomly inside the box
    x0 = vpython.vector(
        random.random() * box_size - 0.5 * box_size,
        random.random() * box_size - 0.5 * box_size,
        0,
    )
    # Initial velocity small enough that we should not escape
    v_max = math.sqrt(-2 * GRAVITY * (0.5 * box_size - x0.y))
    v0 = vpython.vector(
        random.random() * v_max,
        random.random() * v_max,
        0,
    )
    # Initialize the ball. Python also lets us attach an extra attribute, in
    # this case velocity, to the ball object.
    ball = vpython.sphere(
        pos=x0,
        color=vpython.color.red,
        radius=ball_radius,
    )
    ball.v = v0
    ball.m = 1
    # Set up the graph pane and each curve we want in it
    vpython.graph(
        title="Ball in a Box",
        xtitle="Time (s)",
        ytitle="Energy (J)",
        fast=False,
    )
    graph_pot = vpython.gcurve(color=vpython.color.blue,
                               width=2,
                               label="Potential Energy")
    graph_kin = vpython.gcurve(color=vpython.color.red,
                               width=2,
                               label="Kinetic Energy")
    graph_tot = vpython.gcurve(color=vpython.color.magenta,
                               width=2,
                               label="Total Energy")
    # Time loop! Handle gravity and collisions
    t, dt, tmax = 0, 0.001, 10
    while t < tmax:
        t += dt
        vpython.rate(1 / dt)
        dvdt = -GRAVITY * vpython.vector(0, -1, 0)
        ball.v += dvdt * dt
        ball.pos += ball.v * dt
        # Check for collisions
        for side in box_sides:
            # Vector from the center of the ball to the center of the wall
            center_to_center = ball.pos - side.pos
            # Project onto the wall's perpendicular unit vector to get distance
            distance = center_to_center.dot(side.axis)
            # If it's a collision, flip the component of the ball's velocity
            # that's perpendicular to the wall
            if distance < (ball.radius + 0.5 * box_thickness):
                dv = -2 * side.axis.dot(ball.v)
                ball.v += side.axis * dv
        energy_pot = -ball.m * GRAVITY * ball.pos.y
        energy_kin = 0.5 * ball.m * ball.v.dot(ball.v)
        graph_pot.plot(t, energy_pot)
        graph_kin.plot(t, energy_kin)
        graph_tot.plot(t, energy_pot + energy_kin)
    return