2016-11-11 14:42:40 -08:00
|
|
|
from helpers import *
|
|
|
|
|
|
|
|
from scene import Scene
|
|
|
|
# from topics.geometry import
|
2016-12-26 07:10:38 -08:00
|
|
|
from mobject.tex_mobject import TexMobject, TextMobject
|
2016-11-13 11:35:06 -08:00
|
|
|
from mobject.vectorized_mobject import VGroup, VectorizedPoint
|
2016-11-11 14:42:40 -08:00
|
|
|
from animation.simple_animations import Write, ShowCreation
|
|
|
|
from topics.number_line import NumberLine
|
|
|
|
from topics.functions import ParametricFunction
|
2017-01-09 15:06:54 -08:00
|
|
|
from topics.geometry import Rectangle, DashedLine, Line
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
class GraphScene(Scene):
|
|
|
|
CONFIG = {
|
|
|
|
"x_min" : -1,
|
|
|
|
"x_max" : 10,
|
|
|
|
"x_axis_width" : 9,
|
|
|
|
"x_tick_frequency" : 1,
|
2016-12-26 07:10:38 -08:00
|
|
|
"x_leftmost_tick" : None, #Change if different from x_min
|
2016-11-11 14:42:40 -08:00
|
|
|
"x_labeled_nums" : range(1, 10),
|
|
|
|
"x_axis_label" : "x",
|
|
|
|
"y_min" : -1,
|
|
|
|
"y_max" : 10,
|
|
|
|
"y_axis_height" : 6,
|
|
|
|
"y_tick_frequency" : 1,
|
2016-12-26 07:10:38 -08:00
|
|
|
"y_bottom_tick" : None, #Change if different from y_min
|
2016-11-11 14:42:40 -08:00
|
|
|
"y_labeled_nums" : range(1, 10),
|
|
|
|
"y_axis_label" : "y",
|
|
|
|
"axes_color" : GREY,
|
|
|
|
"graph_origin" : 2.5*DOWN + 4*LEFT,
|
|
|
|
"y_axis_numbers_nudge" : 0.4*UP+0.5*LEFT,
|
2017-01-05 11:57:13 -08:00
|
|
|
"num_graph_anchor_points" : 25,
|
2017-01-09 11:22:28 -08:00
|
|
|
"default_graph_colors" : [BLUE, GREEN, YELLOW],
|
|
|
|
"default_derivative_color" : RED,
|
2016-11-11 14:42:40 -08:00
|
|
|
}
|
|
|
|
def setup_axes(self, animate = True):
|
|
|
|
x_num_range = float(self.x_max - self.x_min)
|
2017-01-05 11:57:13 -08:00
|
|
|
self.space_unit_to_x = self.x_axis_width/x_num_range
|
2016-11-11 14:42:40 -08:00
|
|
|
x_axis = NumberLine(
|
|
|
|
x_min = self.x_min,
|
|
|
|
x_max = self.x_max,
|
2017-01-05 11:57:13 -08:00
|
|
|
space_unit_to_num = self.space_unit_to_x,
|
2016-11-11 14:42:40 -08:00
|
|
|
tick_frequency = self.x_tick_frequency,
|
2016-12-26 07:10:38 -08:00
|
|
|
leftmost_tick = self.x_leftmost_tick or self.x_min,
|
2016-11-11 14:42:40 -08:00
|
|
|
numbers_with_elongated_ticks = self.x_labeled_nums,
|
|
|
|
color = self.axes_color
|
|
|
|
)
|
|
|
|
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
2016-11-13 11:35:06 -08:00
|
|
|
if self.x_labeled_nums:
|
|
|
|
x_axis.add_numbers(*self.x_labeled_nums)
|
2016-12-26 07:10:38 -08:00
|
|
|
x_label = TextMobject(self.x_axis_label)
|
2016-11-11 14:42:40 -08:00
|
|
|
x_label.next_to(x_axis, RIGHT+UP, buff = SMALL_BUFF)
|
2016-12-26 07:10:38 -08:00
|
|
|
x_label.shift_onto_screen()
|
2016-11-14 15:06:44 -08:00
|
|
|
x_axis.add(x_label)
|
|
|
|
self.x_axis_label_mob = x_label
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
y_num_range = float(self.y_max - self.y_min)
|
2017-01-05 11:57:13 -08:00
|
|
|
self.space_unit_to_y = self.y_axis_height/y_num_range
|
2016-11-11 14:42:40 -08:00
|
|
|
y_axis = NumberLine(
|
|
|
|
x_min = self.y_min,
|
|
|
|
x_max = self.y_max,
|
2017-01-05 11:57:13 -08:00
|
|
|
space_unit_to_num = self.space_unit_to_y,
|
2016-11-11 14:42:40 -08:00
|
|
|
tick_frequency = self.y_tick_frequency,
|
2016-12-26 07:10:38 -08:00
|
|
|
leftmost_tick = self.y_bottom_tick or self.y_min,
|
2016-11-11 14:42:40 -08:00
|
|
|
numbers_with_elongated_ticks = self.y_labeled_nums,
|
|
|
|
color = self.axes_color
|
|
|
|
)
|
|
|
|
y_axis.shift(self.graph_origin-y_axis.number_to_point(0))
|
|
|
|
y_axis.rotate(np.pi/2, about_point = y_axis.number_to_point(0))
|
2016-11-13 11:35:06 -08:00
|
|
|
if self.y_labeled_nums:
|
|
|
|
y_axis.add_numbers(*self.y_labeled_nums)
|
|
|
|
y_axis.numbers.shift(self.y_axis_numbers_nudge)
|
2016-12-26 07:10:38 -08:00
|
|
|
y_label = TextMobject(self.y_axis_label)
|
2016-11-11 14:42:40 -08:00
|
|
|
y_label.next_to(y_axis.get_top(), RIGHT, buff = 2*MED_BUFF)
|
2016-12-26 07:10:38 -08:00
|
|
|
y_label.shift_onto_screen()
|
2016-11-14 15:06:44 -08:00
|
|
|
y_axis.add(y_label)
|
|
|
|
self.y_axis_label_mob = y_label
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
if animate:
|
|
|
|
self.play(Write(VGroup(x_axis, y_axis)))
|
|
|
|
else:
|
2016-12-29 14:31:01 -08:00
|
|
|
self.add(x_axis, y_axis)
|
2016-11-11 14:42:40 -08:00
|
|
|
self.x_axis, self.y_axis = x_axis, y_axis
|
2017-01-09 11:22:28 -08:00
|
|
|
self.default_graph_colors = it.cycle(self.default_graph_colors)
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
def coords_to_point(self, x, y):
|
|
|
|
assert(hasattr(self, "x_axis") and hasattr(self, "y_axis"))
|
|
|
|
result = self.x_axis.number_to_point(x)[0]*RIGHT
|
|
|
|
result += self.y_axis.number_to_point(y)[1]*UP
|
|
|
|
return result
|
|
|
|
|
|
|
|
def graph_function(self, func,
|
2017-01-09 11:22:28 -08:00
|
|
|
color = None,
|
2016-12-26 07:10:38 -08:00
|
|
|
animate = False,
|
2016-11-11 14:42:40 -08:00
|
|
|
is_main_graph = True,
|
|
|
|
):
|
2017-01-09 11:22:28 -08:00
|
|
|
if color is None:
|
|
|
|
color = self.default_graph_colors.next()
|
2016-11-11 14:42:40 -08:00
|
|
|
|
2017-01-05 11:57:13 -08:00
|
|
|
def parameterized_function(alpha):
|
2016-11-11 14:42:40 -08:00
|
|
|
x = interpolate(self.x_min, self.x_max, alpha)
|
|
|
|
return self.coords_to_point(x, func(x))
|
|
|
|
|
2017-01-05 11:57:13 -08:00
|
|
|
graph = ParametricFunction(
|
|
|
|
parameterized_function,
|
|
|
|
color = color,
|
|
|
|
num_anchor_points = self.num_graph_anchor_points,
|
|
|
|
)
|
|
|
|
graph.underlying_function = func
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
if is_main_graph:
|
|
|
|
self.graph = graph
|
2016-11-13 11:35:06 -08:00
|
|
|
self.func = func
|
2016-11-11 14:42:40 -08:00
|
|
|
if animate:
|
|
|
|
self.play(ShowCreation(graph))
|
|
|
|
self.add(graph)
|
|
|
|
return graph
|
|
|
|
|
|
|
|
|
2017-01-05 11:57:13 -08:00
|
|
|
def input_to_graph_point(self, x, graph = None):
|
|
|
|
if graph is None:
|
|
|
|
assert(hasattr(self, "graph"))
|
|
|
|
graph = self.graph
|
2017-01-06 13:01:34 -08:00
|
|
|
return self.coords_to_point(x, graph.underlying_function(x))
|
2017-01-05 11:57:13 -08:00
|
|
|
|
|
|
|
def angle_of_tangent(self, x, graph = None, dx = 0.01):
|
|
|
|
vect = self.input_to_graph_point(x + dx, graph) - self.input_to_graph_point(x, graph)
|
2016-11-11 14:42:40 -08:00
|
|
|
return angle_of_vector(vect)
|
|
|
|
|
2017-01-05 11:57:13 -08:00
|
|
|
def slope_of_tangent(self, *args):
|
|
|
|
return np.tan(self.angle_of_tangent(*args))
|
|
|
|
|
2017-01-09 11:22:28 -08:00
|
|
|
def get_derivative_graph(self, graph = None, dx = 0.01, color = None):
|
|
|
|
if color is None:
|
|
|
|
color = self.default_derivative_color
|
2017-01-05 11:57:13 -08:00
|
|
|
derivative_graph = self.graph_function(
|
|
|
|
lambda x : self.slope_of_tangent(x, graph, dx) / self.space_unit_to_y,
|
|
|
|
color = color,
|
|
|
|
animate = False,
|
|
|
|
is_main_graph = False
|
|
|
|
)
|
|
|
|
self.remove(derivative_graph)
|
|
|
|
return derivative_graph
|
|
|
|
|
|
|
|
|
2016-11-11 14:42:40 -08:00
|
|
|
def label_graph(self, graph, label = "f(x)",
|
|
|
|
proportion = 0.7,
|
|
|
|
direction = LEFT,
|
2017-01-09 15:06:54 -08:00
|
|
|
buff = MED_BUFF,
|
2016-11-11 14:42:40 -08:00
|
|
|
animate = True
|
|
|
|
):
|
|
|
|
label = TexMobject(label)
|
|
|
|
label.highlight(graph.get_color())
|
|
|
|
label.next_to(
|
|
|
|
graph.point_from_proportion(proportion),
|
|
|
|
direction,
|
|
|
|
buff = buff
|
|
|
|
)
|
|
|
|
if animate:
|
|
|
|
self.play(Write(label))
|
|
|
|
self.add(label)
|
|
|
|
return label
|
|
|
|
|
2016-11-13 11:35:06 -08:00
|
|
|
def get_riemann_rectangles(self,
|
|
|
|
x_min = None,
|
|
|
|
x_max = None,
|
|
|
|
dx = 0.1,
|
|
|
|
stroke_width = 1,
|
|
|
|
start_color = BLUE,
|
|
|
|
end_color = GREEN):
|
|
|
|
assert(hasattr(self, "func"))
|
|
|
|
x_min = x_min if x_min is not None else self.x_min
|
|
|
|
x_max = x_max if x_max is not None else self.x_max
|
|
|
|
rectangles = VGroup()
|
|
|
|
for x in np.arange(x_min, x_max, dx):
|
|
|
|
points = VGroup(*map(VectorizedPoint, [
|
|
|
|
self.coords_to_point(x, 0),
|
|
|
|
self.coords_to_point(x+dx, self.func(x+dx)),
|
|
|
|
]))
|
|
|
|
rect = Rectangle()
|
|
|
|
rect.replace(points, stretch = True)
|
|
|
|
rect.set_fill(opacity = 1)
|
|
|
|
rectangles.add(rect)
|
|
|
|
rectangles.gradient_highlight(start_color, end_color)
|
|
|
|
rectangles.set_stroke(BLACK, width = stroke_width)
|
|
|
|
return rectangles
|
|
|
|
|
2017-01-06 13:01:34 -08:00
|
|
|
def get_vertical_line_to_graph(self,
|
|
|
|
x,
|
|
|
|
graph = None,
|
2017-01-09 15:06:54 -08:00
|
|
|
line_class = Line,
|
2017-01-06 13:01:34 -08:00
|
|
|
line_kwargs = None,
|
|
|
|
):
|
|
|
|
if graph is None:
|
|
|
|
assert(hasattr(self, "graph"))
|
|
|
|
graph = self.graph
|
|
|
|
if line_kwargs is None:
|
|
|
|
line_kwargs = {}
|
|
|
|
if "color" not in line_kwargs:
|
|
|
|
line_kwargs["color"] = graph.get_color()
|
2017-01-09 11:22:28 -08:00
|
|
|
return line_class(
|
2017-01-06 13:01:34 -08:00
|
|
|
self.coords_to_point(x, 0),
|
|
|
|
self.input_to_graph_point(x, graph),
|
|
|
|
**line_kwargs
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|