You need to sign in or sign up before continuing.
Commit fc90aa06 authored by Antonio Caceres's avatar Antonio Caceres
Browse files

Functional typing complete.

parent 15118a00
Showing with 205 additions and 98 deletions
+205 -98
...@@ -2,21 +2,23 @@ ...@@ -2,21 +2,23 @@
CS1 24fa - Assignment 4 CS1 24fa - Assignment 4
Constants required for a Newton's cradle simulation. Constants required for a Newton's cradle simulation.
""" """
from vpython import * from vpython import color, pi, textures, vector # type: ignore
GRAV = 9.81 # gravitational acceleration in m/s^2 from support.types import VPythonColor, VPythonTexture
R = 0.2 # cord length in meters, functions as size parameter for whole system
THETA_INIT = -30 * pi / 180 # initial angle of 1st pendulum in radians
DT = 0.005 # timestep (between 0.000005 [slo-mo] and 0.01 [normal]) GRAV: float = 9.81 # gravitational acceleration in m/s^2
R: float = 0.2 # cord length in meters, functions as size parameter for whole system
THETA_INIT: float = -30 * pi / 180 # initial angle of 1st pendulum in radians
DT: float = 0.005 # timestep (between 0.000005 [slo-mo] and 0.01 [normal])
# TODO 0.1: Choose the relevant range of cells in the spreadsheet # TODO 0.1: Choose the relevant range of cells in the spreadsheet
RANGE = "" RANGE: str = ""
# TODO 0.2: Enter the relevant spreadsheet ID # TODO 0.2: Enter the relevant spreadsheet ID
SPREADSHEET_ID = "" SPREADSHEET_ID: str = ""
# Define color and texture dictionaries that map strings -> VPython attributes # Define color and texture dictionaries that map strings -> VPython attributes
COLOR_MAP = { COLOR_MAP: dict[str, VPythonColor] = {
"red": color.red, "red": color.red,
"green": color.green, "green": color.green,
"blue": color.blue, "blue": color.blue,
...@@ -26,10 +28,10 @@ COLOR_MAP = { ...@@ -26,10 +28,10 @@ COLOR_MAP = {
"magenta": color.magenta, "magenta": color.magenta,
"white": color.white, "white": color.white,
"black": color.black, "black": color.black,
"gray": color.gray(0.5) "gray": color.gray(0.5), # type: ignore
} }
TEXTURE_MAP = { TEXTURE_MAP: dict[str, VPythonTexture] = {
"earth": textures.earth, "earth": textures.earth,
"flower": textures.flower, "flower": textures.flower,
"granite": textures.granite, "granite": textures.granite,
...@@ -41,5 +43,5 @@ TEXTURE_MAP = { ...@@ -41,5 +43,5 @@ TEXTURE_MAP = {
"stones": textures.stones, "stones": textures.stones,
"stucco": textures.stucco, "stucco": textures.stucco,
"wood": textures.wood, "wood": textures.wood,
"wood_old": textures.wood_old "wood_old": textures.wood_old,
} }
...@@ -3,63 +3,91 @@ CS1 24fa - Assignment 4 ...@@ -3,63 +3,91 @@ CS1 24fa - Assignment 4
Helper functions to make a pendulum. Helper functions to make a pendulum.
""" """
from vpython import * from vpython import ( # type: ignore
box,
def make_bob(bob_pos, r_param, color, texture): color,
cos,
cylinder,
vector,
sin,
sphere,
textures,
)
from support.types import VPythonColor, VPythonTexture
def make_bob(bob_pos: vector,
r_param: float,
clr: VPythonColor,
texture: VPythonTexture,
) -> sphere:
""" """
Makes a spherical bob for the pendulum given certain parameters. Makes a spherical bob for the pendulum given certain parameters.
Args: Args:
bob_pos (VPython vector): posiiton of center of bob bob_pos (VPython vector): position of center of bob
r_param (float): size parameter used to define pendulum components r_param (float): size parameter used to define pendulum components
color (VPython color): color of bob clr (VPython color): color of bob
texture (VPython texture): texture of bob texture (VPython texture): texture of bob
Returns: Returns:
(VPython sphere): sphere representing pendulum bob (VPython sphere): sphere representing pendulum bob
""" """
return sphere(pos=bob_pos, radius=r_param/15, color=color, return sphere(pos=bob_pos, radius=r_param/15, color=clr,
texture=texture) texture=texture)
def make_axle(axle_pos, r_param, color=color.green): def make_axle(axle_pos: vector,
r_param: float,
clr: VPythonColor = color.green,
) -> box:
""" """
Makes a cuboidal axle/support for the pendulum given certain parameters. Makes a cuboidal axle/support for the pendulum given certain parameters.
Args: Args:
axle_pos (VPython vector): posiiton of center of axle axle_pos (VPython vector): position of center of axle
r_param (float): size parameter used to define pendulum components r_param (float): size parameter used to define pendulum components
color (optional, VPython color): color of axle, defaults to green clr (optional, VPython color): color of axle, defaults to green
Returns: Returns:
(VPython box): box representing pendulum axle (VPython box): box representing pendulum axle
""" """
return box(pos=axle_pos, size=vector(r_param/2, r_param/9, r_param/2), return box(pos=axle_pos, size=vector(r_param/2, r_param/9, r_param/2),
color=color, texture=textures.wood) color=clr, texture=textures.wood)
def make_cord(cord_pos, axis, r_param, color=color.white): def make_cord(cord_pos: vector,
axis: vector,
r_param: float,
clr: VPythonColor = color.white,
) -> cylinder:
""" """
Makes a cylindrical cord/string for the pendulum given certain parameters. Makes a cylindrical cord/string for the pendulum given certain parameters.
Args: Args:
cord_pos (VPython vector): posiiton of center of cord cord_pos (VPython vector): position of center of cord
axis (VPython vector): orientation of cord axis (VPython vector): orientation of cord
r_param (float): size parameter used to define pendulum components r_param (float): size parameter used to define pendulum components
color (optional, VPython color): color of cord, defaults to white clr (optional, VPython color): color of cord, defaults to white
Returns: Returns:
(VPython cylinder): cylinder representing pendulum cord (VPython cylinder): cylinder representing pendulum cord
""" """
return cylinder(pos=cord_pos, axis=axis, radius=r_param/50, color=color) return cylinder(pos=cord_pos, axis=axis, radius=r_param/50, color=clr)
def make_pendulum(axle_pos, r_param, theta, bob_color=color.white, bob_texture=textures.metal): def make_pendulum(axle_pos: vector,
r_param: float,
theta: float,
bob_color: VPythonColor = color.white,
bob_texture: VPythonTexture = textures.metal,
) -> tuple[box, sphere, cylinder]:
""" """
Builds a pendulum with a bob, axle and cord. Builds a pendulum with a bob, axle and cord.
Args: Args:
axle_pos (VPython vector): posiiton of center of axle axle_pos (VPython vector): position of center of axle
r_param (float): size parameter used to define pendulum components r_param (float): size parameter used to define pendulum components
theta (float); angle of the bob with respect to the axle theta (float); angle of the bob with respect to the axle
bob_color (optional, VPython color): color of bob, defaults to white bob_color (optional, VPython color): color of bob, defaults to white
......
...@@ -3,14 +3,14 @@ CS1 24fa - Assignment 4 ...@@ -3,14 +3,14 @@ CS1 24fa - Assignment 4
Helper functions and implementation of a Newton's cradle simulation. Helper functions and implementation of a Newton's cradle simulation.
""" """
from vpython import * from support.types import Pendulum
from support.sheets_api import get_values, update_values, get_pendulum_info
from src.pendulum import make_bob, make_axle, make_cord, make_pendulum # TODO 1: write the following 5 functions using relevant helper functions from `sheets_api`.
from src.constants import * def uniform_mass_newton_cradle(spreadsheet_id: str,
mass_range: str,
# TODO 1: write the following 5 functions using relevant helper functions from mass: float,
# `sheets_api.` num_pends: int,
def uniform_mass_newton_cradle(spreadsheet_id, mass_range, mass, num_pends): ) -> None:
""" """
Updates the spreadsheet such that all pendula have the same mass. Updates the spreadsheet such that all pendula have the same mass.
...@@ -20,16 +20,20 @@ def uniform_mass_newton_cradle(spreadsheet_id, mass_range, mass, num_pends): ...@@ -20,16 +20,20 @@ def uniform_mass_newton_cradle(spreadsheet_id, mass_range, mass, num_pends):
mass (float): mass to be uniformly applied to each pendulum mass (float): mass to be uniformly applied to each pendulum
num_pends (int): number of pendula in the Newton's cradle num_pends (int): number of pendula in the Newton's cradle
""" """
...
# TODO: create an empty list that will serve as the required nested list # TODO: create an empty list that will serve as the required nested list
# as described in the guide. Then, for each pendulum, append a list with # as described in the guide. Then, for each pendulum, append a list with
# the required string mass value to the empty list # the required string mass value to the empty list
# Then, use a helper function from `sheets_api.py` to update this nested # Then, use a helper function from `sheets_api.py` to update this nested
# list of values to the specific spreadsheet defined by the # list of values to the specific spreadsheet defined by the
# `spreadsheet_id` # `spreadsheet_id`
pass
def uniform_e_newton_cradle(spreadsheet_id, e_range, e_val, num_pends): def uniform_e_newton_cradle(spreadsheet_id: str,
e_range: str,
e_val: float,
num_pends: int,
) -> None:
""" """
Updates the spreadsheet such that all pendula have the same coefficient of Updates the spreadsheet such that all pendula have the same coefficient of
restitution. restitution.
...@@ -42,12 +46,15 @@ def uniform_e_newton_cradle(spreadsheet_id, e_range, e_val, num_pends): ...@@ -42,12 +46,15 @@ def uniform_e_newton_cradle(spreadsheet_id, e_range, e_val, num_pends):
pendulum pendulum
num_pends (int): number of pendula in the Newton's cradle num_pends (int): number of pendula in the Newton's cradle
""" """
...
# TODO: similar to the previous function, but you will be updating # TODO: similar to the previous function, but you will be updating
# the coefficients of restitution with the relevant value instead # the coefficients of restitution with the relevant value instead
pass
def elastic_newton_cradle(spreadsheet_id, e_range, num_pends): def elastic_newton_cradle(spreadsheet_id: str,
e_range: str,
num_pends: int,
) -> None:
""" """
Updates the spreadsheet such that all pendula have perfectly elastic Updates the spreadsheet such that all pendula have perfectly elastic
collisions with each other. collisions with each other.
...@@ -58,12 +65,15 @@ def elastic_newton_cradle(spreadsheet_id, e_range, num_pends): ...@@ -58,12 +65,15 @@ def elastic_newton_cradle(spreadsheet_id, e_range, num_pends):
spreadsheet spreadsheet
num_pends (int): number of pendula in the Newton's cradle num_pends (int): number of pendula in the Newton's cradle
""" """
...
# TODO: Use a helper function to update the spreadsheet with the elastic # TODO: Use a helper function to update the spreadsheet with the elastic
# coefficient of restitution value. # coefficient of restitution value.
pass
def inelastic_newton_cradle(spreadsheet_id, e_range, num_pends): def inelastic_newton_cradle(spreadsheet_id: str,
e_range: str,
num_pends: int,
) -> None:
""" """
Updates the spreadsheet such that all pendula have perfectly inelastic Updates the spreadsheet such that all pendula have perfectly inelastic
collisions with each other. collisions with each other.
...@@ -74,12 +84,16 @@ def inelastic_newton_cradle(spreadsheet_id, e_range, num_pends): ...@@ -74,12 +84,16 @@ def inelastic_newton_cradle(spreadsheet_id, e_range, num_pends):
spreadsheet spreadsheet
num_pends (int): number of pendula in the Newton's cradle num_pends (int): number of pendula in the Newton's cradle
""" """
...
# TODO: Use a helper function to update the spreadsheet with the inelastic # TODO: Use a helper function to update the spreadsheet with the inelastic
# coefficient of restitution value. # coefficient of restitution value.
pass
def make_newton_cradle(ranges, spreadsheet_id, r_param, theta_init): def make_newton_cradle(ranges: str,
spreadsheet_id: str,
r_param: float,
theta_init: float,
) -> list[Pendulum]:
""" """
Uses information from a specific Google Sheet to make a list of Uses information from a specific Google Sheet to make a list of
dictionaries representing each pendula and their physical quantities. dictionaries representing each pendula and their physical quantities.
...@@ -94,6 +108,7 @@ def make_newton_cradle(ranges, spreadsheet_id, r_param, theta_init): ...@@ -94,6 +108,7 @@ def make_newton_cradle(ranges, spreadsheet_id, r_param, theta_init):
(list[dict]): list of dictionaries, each dictionary representing a (list[dict]): list of dictionaries, each dictionary representing a
pendulum, with keys as physical attributes pendulum, with keys as physical attributes
""" """
...
# TODO: use a helper function from `sheets_api.py` to make a `pend_list` # TODO: use a helper function from `sheets_api.py` to make a `pend_list`
# TODO: write a `for` loop that goes through each pendulum dictionary # TODO: write a `for` loop that goes through each pendulum dictionary
...@@ -104,4 +119,3 @@ def make_newton_cradle(ranges, spreadsheet_id, r_param, theta_init): ...@@ -104,4 +119,3 @@ def make_newton_cradle(ranges, spreadsheet_id, r_param, theta_init):
# initializes the value for the key 'theta' depending on the value of i # initializes the value for the key 'theta' depending on the value of i
# initializes the value for the key 'omega' # initializes the value for the key 'omega'
pass
...@@ -3,11 +3,22 @@ CS1 24fa - Assignment 4 ...@@ -3,11 +3,22 @@ CS1 24fa - Assignment 4
Helper functions and implementation of a Newton's cradle simulation. Helper functions and implementation of a Newton's cradle simulation.
""" """
from vpython import * from vpython import ( # type: ignore
cos,
sin,
vector,
)
from support.types import Pendulum
# TODO 2: update angular components of a pendulum with the derivation provided # TODO 2: update angular components of a pendulum with the derivation provided
def angle_update(omega, theta, r_param, dt, g): def angle_update(omega: float,
theta: float,
r_param: float,
dt: float,
g: float,
) -> tuple[float, float]:
""" """
Updates the angle and angular velocity of a pendulum system. Updates the angle and angular velocity of a pendulum system.
...@@ -21,11 +32,15 @@ def angle_update(omega, theta, r_param, dt, g): ...@@ -21,11 +32,15 @@ def angle_update(omega, theta, r_param, dt, g):
Returns: Returns:
(tuple[float, float]): time-updated omega, theta (tuple[float, float]): time-updated omega, theta
""" """
pass ...
# Provided # Provided
def swing_update(pend_dict, r_param, dt, g): def swing_update(pend_dict: Pendulum,
r_param: float,
dt: float,
g: float,
) -> None:
""" """
Updates the angle & position of the pendulum components in a single swing. Updates the angle & position of the pendulum components in a single swing.
...@@ -49,7 +64,11 @@ def swing_update(pend_dict, r_param, dt, g): ...@@ -49,7 +64,11 @@ def swing_update(pend_dict, r_param, dt, g):
# TODO 3: update the swings for each pendulum in the list # TODO 3: update the swings for each pendulum in the list
def full_swing_update(pend_list, r_param, dt, g): def full_swing_update(pend_list: Pendulum,
r_param: float,
dt: float,
g: float,
) -> None:
""" """
Updates the omegas for all the pendula in the cradle, updating each Updates the omegas for all the pendula in the cradle, updating each
pendulum dictionary. pendulum dictionary.
...@@ -61,4 +80,4 @@ def full_swing_update(pend_list, r_param, dt, g): ...@@ -61,4 +80,4 @@ def full_swing_update(pend_list, r_param, dt, g):
dt (float): time step of update dt (float): time step of update
g (float): gravitational acceleration g (float): gravitational acceleration
""" """
pass ...
...@@ -4,15 +4,18 @@ Functionality from Google Sheets API to get and update values in a spreadsheet. ...@@ -4,15 +4,18 @@ Functionality from Google Sheets API to get and update values in a spreadsheet.
Also creates list of dictionaries representing the pendula in the Newton's cradle. Also creates list of dictionaries representing the pendula in the Newton's cradle.
""" """
from src.constants import RANGE, SPREADSHEET_ID from sys import exit
from typing import Any
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
from googleapiclient.discovery import build
from google.oauth2.service_account import Credentials from google.oauth2.service_account import Credentials
import termcolor import termcolor
from src.constants import RANGE, SPREADSHEET_ID
from support.types import PendAttrs
SERVICE_ACCOUNT_FILE = "./config.json" SERVICE_ACCOUNT_FILE = "./config.json"
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"] SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
...@@ -24,12 +27,12 @@ except Exception: ...@@ -24,12 +27,12 @@ except Exception:
"Please follow the instructions to get your config.json file here:", "red") "Please follow the instructions to get your config.json file here:", "red")
) )
print(termcolor.colored( print(termcolor.colored(
" https://cs1.caltech.codes/24fa/projects/04/service_account_instructions", color="blue") " https://cs1.caltech.codes/24fa/projects/04/service_account_instructions", color="blue"
) ))
exit(1) exit(1)
def get_values(spreadsheet_id: str, range_names: str): def get_values(spreadsheet_id: str, range_names: str | list[str]) -> dict[str, Any]:
""" """
Returns the values of a spreadsheet in a given range. Returns the values of a spreadsheet in a given range.
...@@ -53,12 +56,21 @@ def get_values(spreadsheet_id: str, range_names: str): ...@@ -53,12 +56,21 @@ def get_values(spreadsheet_id: str, range_names: str):
return result return result
except HttpError as e: except HttpError as e:
print(termcolor.colored( print(termcolor.colored(
"You do not have access to that spreadsheet OR the range is invalid.\nDid you make sure to share the spreadsheet to be available to 'Anyone without a link'?\nDid you change constants in constants.py:\n - constants.py:14\n - constants.py:16\nYou can control/cmd + click on the linkes to go to that line.", "red") "You do not have access to that spreadsheet OR the range is invalid.\n"
) "Did you make sure to share the spreadsheet to be available to 'Anyone with a link'?\n"
exit(1) "Did you change constants in constants.py:\n"
" - constants.py:14\n"
" - constants.py:16\n"
def update_values(spreadsheet_id, range_name, values): "You can control/cmd + click on the links to go to that line.",
"red"
))
raise
# exit(1)
def update_values(spreadsheet_id: str,
range_names: str | list[str],
values: list[str]) -> dict[str, Any]:
""" """
Updates the values in the specified spreadsheet in a given range. Updates the values in the specified spreadsheet in a given range.
...@@ -73,25 +85,21 @@ def update_values(spreadsheet_id, range_name, values): ...@@ -73,25 +85,21 @@ def update_values(spreadsheet_id, range_name, values):
Raises: Raises:
HttpError: if there is an error in getting the information HttpError: if there is an error in getting the information
""" """
try: service = build('sheets', 'v4', credentials=creds)
service = build('sheets', 'v4', credentials=creds) result = service.spreadsheets().values().batchUpdate(
result = service.spreadsheets().values().batchUpdate( spreadsheetId=spreadsheet_id,
spreadsheetId=spreadsheet_id, body={
body={ "valueInputOption": 'USER_ENTERED',
"valueInputOption": 'USER_ENTERED', "data": [{
"data": [{ 'range': range_names,
'range': range_name, 'values': values,
'values': values, }]
}] }
} ).execute()
).execute() return result
return result
except HttpError as error:
print(f"An error occurred: {error}") def get_pendulum_info(ranges: str | list[str], spreadsheet_id: str) -> list[PendAttrs]:
return error
def get_pendulum_info(ranges, spreadsheet_id):
""" """
Returns a list with all the information from the Google Sheet stored in Returns a list with all the information from the Google Sheet stored in
dictionaries for each pendulum. dictionaries for each pendulum.
......
from vpython import vector
from typing import Any, TypedDict
VPythonColor = vector
VPythonTexture = str
class PendAttrs(TypedDict):
masses: str
restitution_coeffs: str
colors: str
textures: str
class Pendulum(TypedDict):
theta: float
omega: float
masses: float
restitution_coeffs: float
pendulum: tuple[Any, Any, Any] # TODO
...@@ -3,8 +3,13 @@ CS1 24fa - Assignment 4 ...@@ -3,8 +3,13 @@ CS1 24fa - Assignment 4
Helper functions and implementation of a Newton's cradle simulation. Helper functions and implementation of a Newton's cradle simulation.
""" """
from vpython import * from support.types import Pendulum
from src.constants import *
from vpython import scene, rate
from src.constants import (
RANGE, SPREADSHEET_ID,
DT, GRAV, R, THETA_INIT,
)
from support.sheets_api import get_values, update_values, get_pendulum_info from support.sheets_api import get_values, update_values, get_pendulum_info
from src.pendulum import make_bob, make_axle, make_cord, make_pendulum from src.pendulum import make_bob, make_axle, make_cord, make_pendulum
from src.special_cradles import uniform_mass_newton_cradle, uniform_e_newton_cradle, elastic_newton_cradle, inelastic_newton_cradle, make_newton_cradle from src.special_cradles import uniform_mass_newton_cradle, uniform_e_newton_cradle, elastic_newton_cradle, inelastic_newton_cradle, make_newton_cradle
...@@ -14,7 +19,13 @@ scene.title = "Newton's cradle" ...@@ -14,7 +19,13 @@ scene.title = "Newton's cradle"
# TODO 4: use the physics derivation provided to update velocities post collision # TODO 4: use the physics derivation provided to update velocities post collision
def handle_collision(e, omega1, omega2, m1, m2, r_param): def handle_collision(e: float,
omega1: float,
omega2: float,
m1: float,
m2: float,
r_param: float,
) -> tuple[float, float]:
""" """
In case two bobs collide, handles the physics (momentum conservation). In case two bobs collide, handles the physics (momentum conservation).
...@@ -29,26 +40,30 @@ def handle_collision(e, omega1, omega2, m1, m2, r_param): ...@@ -29,26 +40,30 @@ def handle_collision(e, omega1, omega2, m1, m2, r_param):
Returns: Returns:
(tuple[float, float]): post-collision omegas of 1st, 2nd pendula (tuple[float, float]): post-collision omegas of 1st, 2nd pendula
""" """
pass ...
# TODO 5: handle pairwise pendulum motion and collisions # TODO 5: handle pairwise pendulum motion and collisions
def handle_two_bobs(pend1, pend2, r_param): def handle_two_bobs(pend1: Pendulum, pend2: Pendulum, r_param: float) -> None:
""" """
Handles the collisions and updates of 2 pendula in the cradle. Handles the collisions and updates of 2 pendula in the cradle.
Args: Args:
pend1 (dict): dictionary representing 1st pendulum, pend1 (Pendulum): dictionary representing 1st pendulum,
with keys as physical attributes with keys as physical attributes
pend2 (dict): dictionary representing 1st pendulum, pend2 (Pendulum): dictionary representing 1st pendulum,
with keys as physical attributes with keys as physical attributes
r_param (float): size parameter used to define pendulum components r_param (float): size parameter used to define pendulum components
""" """
pass ...
# TODO 6: perform one step of the newton cradle # TODO 6: perform one step of the newton cradle
def newton_cradle_tick(pend_list, r_param, dt, g): def newton_cradle_tick(pend_list: list[Pendulum],
r_param: float,
dt: float,
g: float,
) -> None:
""" """
Performs one 'tick' of the entire Newton's cradle (propagates any Performs one 'tick' of the entire Newton's cradle (propagates any
motion throughout all the pendula). motion throughout all the pendula).
...@@ -60,19 +75,17 @@ def newton_cradle_tick(pend_list, r_param, dt, g): ...@@ -60,19 +75,17 @@ def newton_cradle_tick(pend_list, r_param, dt, g):
dt (float): time step of update dt (float): time step of update
g (float): gravitational acceleration g (float): gravitational acceleration
""" """
pass ...
if __name__ == "__main__": if __name__ == "__main__":
pend_list # TODO 7.1: use a helper function to make pend_list with # TODO 7.1: use a helper function to make pend_list with
# RANGE, SPREADSHEET_ID, R and THETA_INIT # RANGE, SPREADSHEET_ID, R and THETA_INIT
pend_list = ...
done = False done = False
while not done: while not done:
rate(1/DT) # The rate to be included in the animation rate(1/DT) # The rate to be included in the animation
# TODO 7.2: Use another helper function to perform a timestep of the # TODO 7.2: Use another helper function to perform a timestep of the
# Newton's cradle # Newton's cradle
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment