curriculum/challenges/english/blocks/learn-encapsulation-by-building-a-projectile-trajectory-calculator/6633c06601c081735063b528.md
You have built a projectile trajectory calculator.
Now to conclude, modify the code you wrote outside the classes, and incorporate it into a little utility function called projectile_helper that takes in the desired values for speed, height and angle and prints to the terminal in sequence, the details of the projectile, the table of coordinates and the graph of the trajectory.
Call projectile_helper once with values of your choice.
You should create projectile_helper in the global scope.
({
test: () => runPython(`
assert _Node(_code).has_function('projectile_helper')
`)
})
You should print the requested strings in order. Do not print other values.
({
test: () => {
let code_ = code.replaceAll('print(', '__result.append(')
runPython(`
__result = []
${code_}
__result = [] # code_ includes a call, so let's reset it here
projectile_helper(12, 13, 14)
bullet = Projectile(12, 13, 14)
assert str(__result[0]) == str(bullet)
c = bullet.calculate_all_coordinates()
g = Graph(c)
assert __result[1] == g.create_coordinates_table()
assert __result[2] == g.create_trajectory()
`)
}
})
You should call the projectile_helper function at least once.
({
test: () => runPython(`
assert len(_Node(_code).find_calls('projectile_helper')) > 0
`)
})
You should not have variables or print calls in the global scope.
({
test: () => runPython(`
assert _Node(_code).find_calls('print') == [], "found print"
assert not _Node(_code).has_variable('graph'), "found graph"
assert not _Node(_code).has_variable('ball'), "found ball"
assert not _Node(_code).has_variable('coordinates'), "found coordinates"
`)
})
import math
GRAVITATIONAL_ACCELERATION = 9.81
PROJECTILE = "∙"
x_axis_tick = "T"
y_axis_tick = "⊣"
class Projectile:
__slots__ = ('__speed', '__height', '__angle')
def __init__(self, speed, height, angle):
self.__speed = speed
self.__height = height
self.__angle = math.radians(angle)
def __str__(self):
return f'''
Projectile details:
speed: {self.speed} m/s
height: {self.height} m
angle: {self.angle}°
displacement: {round(self.__calculate_displacement(), 1)} m
'''
def __calculate_displacement(self):
horizontal_component = self.__speed * math.cos(self.__angle)
vertical_component = self.__speed * math.sin(self.__angle)
squared_component = vertical_component**2
gh_component = 2 * GRAVITATIONAL_ACCELERATION * self.__height
sqrt_component = math.sqrt(squared_component + gh_component)
return horizontal_component * (vertical_component + sqrt_component) / GRAVITATIONAL_ACCELERATION
def __calculate_y_coordinate(self, x):
height_component = self.__height
angle_component = math.tan(self.__angle) * x
acceleration_component = GRAVITATIONAL_ACCELERATION * x ** 2 / (
2 * self.__speed ** 2 * math.cos(self.__angle) ** 2)
y_coordinate = height_component + angle_component - acceleration_component
return y_coordinate
def calculate_all_coordinates(self):
return [
(x, self.__calculate_y_coordinate(x))
for x in range(math.ceil(self.__calculate_displacement()))
]
@property
def height(self):
return self.__height
@property
def angle(self):
return round(math.degrees(self.__angle))
@property
def speed(self):
return self.__speed
@height.setter
def height(self, n):
self.__height = n
@angle.setter
def angle(self, n):
self.__angle = math.radians(n)
@speed.setter
def speed(self, s):
self.__speed = s
def __repr__(self):
return f'{self.__class__}({self.speed}, {self.height}, {self.angle})'
class Graph:
__slots__ = ('__coordinates')
def __init__(self, coord):
self.__coordinates = coord
def __repr__(self):
return f"Graph({self.__coordinates})"
def create_coordinates_table(self):
table = '\n x y\n'
for x, y in self.__coordinates:
table += f'{x:>3}{y:>7.2f}\n'
return table
def create_trajectory(self):
rounded_coords = [(round(x), round(y)) for x, y in self.__coordinates]
x_max = max(rounded_coords, key=lambda i: i[0])[0]
y_max = max(rounded_coords, key=lambda j: j[1])[1]
matrix_list = [[" " for _ in range(x_max + 1)] for _ in range(y_max + 1)]
for x, y in rounded_coords:
matrix_list[-1 - y][x] = PROJECTILE
matrix = ["".join(line) for line in matrix_list]
matrix_axes = [y_axis_tick + row for row in matrix]
matrix_axes.append(" " + x_axis_tick * (len(matrix[0])))
graph = "\n" + "\n".join(matrix_axes) + "\n"
return graph
--fcc-editable-region--
ball = Projectile(10, 3, 45)
print(ball)
coordinates = ball.calculate_all_coordinates()
graph = Graph(coordinates)
print(graph.create_trajectory())
--fcc-editable-region--
import math
GRAVITATIONAL_ACCELERATION = 9.81
PROJECTILE = "∙"
x_axis_tick = "T"
y_axis_tick = "⊣"
class Projectile:
__slots__ = ('__speed', '__height', '__angle')
def __init__(self, speed, height, angle):
self.__speed = speed
self.__height = height
self.__angle = math.radians(angle)
def __str__(self):
return f'''
Projectile details:
speed: {self.speed} m/s
height: {self.height} m
angle: {self.angle}°
displacement: {round(self.__calculate_displacement(), 1)} m
'''
def __calculate_displacement(self):
horizontal_component = self.__speed * math.cos(self.__angle)
vertical_component = self.__speed * math.sin(self.__angle)
squared_component = vertical_component**2
gh_component = 2 * GRAVITATIONAL_ACCELERATION * self.__height
sqrt_component = math.sqrt(squared_component + gh_component)
return horizontal_component * (vertical_component + sqrt_component) / GRAVITATIONAL_ACCELERATION
def __calculate_y_coordinate(self, x):
height_component = self.__height
angle_component = math.tan(self.__angle) * x
acceleration_component = GRAVITATIONAL_ACCELERATION * x ** 2 / (
2 * self.__speed ** 2 * math.cos(self.__angle) ** 2)
y_coordinate = height_component + angle_component - acceleration_component
return y_coordinate
def calculate_all_coordinates(self):
return [
(x, self.__calculate_y_coordinate(x))
for x in range(math.ceil(self.__calculate_displacement()))
]
@property
def height(self):
return self.__height
@property
def angle(self):
return round(math.degrees(self.__angle))
@property
def speed(self):
return self.__speed
@height.setter
def height(self, n):
self.__height = n
@angle.setter
def angle(self, n):
self.__angle = math.radians(n)
@speed.setter
def speed(self, s):
self.__speed = s
def __repr__(self):
return f'{self.__class__}({self.speed}, {self.height}, {self.angle})'
class Graph:
__slots__ = ('__coordinates')
def __init__(self, coord):
self.__coordinates = coord
def __repr__(self):
return f"Graph({self.__coordinates})"
def __str__(self):
return self.create_trajectory()
def create_coordinates_table(self):
table = '\n x y\n'
for x, y in self.__coordinates:
table += f'{x:>3}{y:>7.2f}\n'
return table
def create_trajectory(self):
rounded_coords = [(round(x), round(y)) for x, y in self.__coordinates]
x_max = max(rounded_coords, key=lambda i: i[0])[0]
y_max = max(rounded_coords, key=lambda j: j[1])[1]
matrix_list = [[" " for _ in range(x_max + 1)] for _ in range(y_max + 1)]
for x, y in rounded_coords:
matrix_list[-1 - y][x] = PROJECTILE
matrix = ["".join(line) for line in matrix_list]
matrix_axes = [y_axis_tick + row for row in matrix]
matrix_axes.append(" " + x_axis_tick * (len(matrix[0])))
graph = "\n" + "\n".join(matrix_axes) + "\n"
return graph
def projectile_helper(speed, height, angle):
p = Projectile(speed, height, angle)
print(p)
coord = p.calculate_all_coordinates()
g = Graph(coord)
print(g.create_coordinates_table())
print(g.create_trajectory())
projectile_helper(12, 43, 1)