2018-03-31 19:05:53 -07:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2018-03-30 18:19:23 -07:00
|
|
|
from constants import *
|
2016-11-11 14:42:40 -08:00
|
|
|
|
2018-03-30 18:19:23 -07:00
|
|
|
from scene.scene import Scene
|
2018-03-31 15:11:35 -07:00
|
|
|
from animation.creation import Write
|
2017-01-24 16:21:35 -08:00
|
|
|
from animation.transform import Transform
|
2018-03-31 19:00:26 -07:00
|
|
|
from animation.update import UpdateFromAlphaFunc
|
2018-03-31 19:05:53 -07:00
|
|
|
from mobject.functions import ParametricFunction
|
2018-03-31 19:00:26 -07:00
|
|
|
from mobject.geometry import Line
|
|
|
|
from mobject.geometry import Rectangle
|
|
|
|
from mobject.number_line import NumberLine
|
2018-03-31 18:05:02 -07:00
|
|
|
from mobject.svg.tex_mobject import TexMobject
|
|
|
|
from mobject.svg.tex_mobject import TextMobject
|
|
|
|
from mobject.types.vectorized_mobject import VGroup
|
|
|
|
from mobject.types.vectorized_mobject import VectorizedPoint
|
2018-03-30 18:19:23 -07:00
|
|
|
from utils.bezier import interpolate
|
|
|
|
from utils.color import color_gradient
|
2018-03-31 15:11:35 -07:00
|
|
|
from utils.color import invert_color
|
2018-03-30 18:19:23 -07:00
|
|
|
from utils.space_ops import angle_of_vector
|
2016-11-11 14:42:40 -08:00
|
|
|
|
2018-04-01 10:21:42 -07:00
|
|
|
# TODO, this should probably reimplemented entirely, especially so as to
|
|
|
|
# better reuse code from mobject/coordinate_systems
|
|
|
|
|
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
|
2017-01-30 14:34:59 -08:00
|
|
|
"x_labeled_nums" : None,
|
2017-01-24 16:21:35 -08:00
|
|
|
"x_axis_label" : "$x$",
|
2016-11-11 14:42:40 -08:00
|
|
|
"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
|
2017-01-30 14:34:59 -08:00
|
|
|
"y_labeled_nums" : None,
|
2017-01-24 16:21:35 -08:00
|
|
|
"y_axis_label" : "$y$",
|
2016-11-11 14:42:40 -08:00
|
|
|
"axes_color" : GREY,
|
|
|
|
"graph_origin" : 2.5*DOWN + 4*LEFT,
|
2017-04-04 16:57:53 -07:00
|
|
|
"exclude_zero_label" : True,
|
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],
|
2017-01-24 16:21:35 -08:00
|
|
|
"default_derivative_color" : GREEN,
|
|
|
|
"default_input_color" : YELLOW,
|
2017-04-27 12:01:33 -07:00
|
|
|
"default_riemann_start_color" : BLUE,
|
|
|
|
"default_riemann_end_color" : GREEN,
|
2016-11-11 14:42:40 -08:00
|
|
|
}
|
2017-04-07 16:05:51 -07:00
|
|
|
def setup(self):
|
|
|
|
self.default_graph_colors_cycle = it.cycle(self.default_graph_colors)
|
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def setup_axes(self, animate = False):
|
2017-04-18 18:37:15 -07:00
|
|
|
##TODO, once eoc is done, refactor this to be less redundant.
|
2016-11-11 14:42:40 -08:00
|
|
|
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
|
2017-08-10 13:46:57 -07:00
|
|
|
if self.x_labeled_nums is None:
|
|
|
|
self.x_labeled_nums = []
|
2017-04-18 18:37:15 -07:00
|
|
|
if self.x_leftmost_tick is None:
|
|
|
|
self.x_leftmost_tick = self.x_min
|
2016-11-11 14:42:40 -08:00
|
|
|
x_axis = NumberLine(
|
|
|
|
x_min = self.x_min,
|
|
|
|
x_max = self.x_max,
|
2017-05-23 13:17:31 -07:00
|
|
|
unit_size = self.space_unit_to_x,
|
2016-11-11 14:42:40 -08:00
|
|
|
tick_frequency = self.x_tick_frequency,
|
2017-04-18 18:37:15 -07:00
|
|
|
leftmost_tick = self.x_leftmost_tick,
|
2016-11-11 14:42:40 -08:00
|
|
|
numbers_with_elongated_ticks = self.x_labeled_nums,
|
|
|
|
color = self.axes_color
|
|
|
|
)
|
2017-01-30 14:34:59 -08:00
|
|
|
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
|
|
|
if len(self.x_labeled_nums) > 0:
|
2017-04-04 16:57:53 -07:00
|
|
|
if self.exclude_zero_label:
|
|
|
|
self.x_labeled_nums = filter(
|
|
|
|
lambda x : x != 0,
|
|
|
|
self.x_labeled_nums
|
|
|
|
)
|
|
|
|
x_axis.add_numbers(*self.x_labeled_nums)
|
2017-08-10 13:46:57 -07:00
|
|
|
if self.x_axis_label:
|
|
|
|
x_label = TextMobject(self.x_axis_label)
|
|
|
|
x_label.next_to(
|
|
|
|
x_axis.get_tick_marks(), UP+RIGHT,
|
|
|
|
buff = SMALL_BUFF
|
|
|
|
)
|
|
|
|
x_label.shift_onto_screen()
|
|
|
|
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
|
2017-08-10 13:46:57 -07:00
|
|
|
|
|
|
|
if self.y_labeled_nums is None:
|
|
|
|
self.y_labeled_nums = []
|
2017-04-18 18:37:15 -07:00
|
|
|
if self.y_bottom_tick is None:
|
|
|
|
self.y_bottom_tick = self.y_min
|
2016-11-11 14:42:40 -08:00
|
|
|
y_axis = NumberLine(
|
|
|
|
x_min = self.y_min,
|
|
|
|
x_max = self.y_max,
|
2017-05-23 13:17:31 -07:00
|
|
|
unit_size = self.space_unit_to_y,
|
2016-11-11 14:42:40 -08:00
|
|
|
tick_frequency = self.y_tick_frequency,
|
2017-04-18 18:37:15 -07:00
|
|
|
leftmost_tick = self.y_bottom_tick,
|
2016-11-11 14:42:40 -08:00
|
|
|
numbers_with_elongated_ticks = self.y_labeled_nums,
|
2017-08-10 13:46:57 -07:00
|
|
|
color = self.axes_color,
|
|
|
|
line_to_number_vect = LEFT,
|
2016-11-11 14:42:40 -08:00
|
|
|
)
|
|
|
|
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))
|
2017-01-30 14:34:59 -08:00
|
|
|
if len(self.y_labeled_nums) > 0:
|
2017-04-04 16:57:53 -07:00
|
|
|
if self.exclude_zero_label:
|
|
|
|
self.y_labeled_nums = filter(
|
|
|
|
lambda y : y != 0,
|
|
|
|
self.y_labeled_nums
|
|
|
|
)
|
|
|
|
y_axis.add_numbers(*self.y_labeled_nums)
|
2017-08-10 13:46:57 -07:00
|
|
|
if self.y_axis_label:
|
|
|
|
y_label = TextMobject(self.y_axis_label)
|
|
|
|
y_label.next_to(
|
|
|
|
y_axis.get_tick_marks(), UP+RIGHT,
|
|
|
|
buff = SMALL_BUFF
|
|
|
|
)
|
|
|
|
y_label.shift_onto_screen()
|
|
|
|
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)
|
2017-02-19 15:03:16 -08:00
|
|
|
self.x_axis, self.y_axis = self.axes = VGroup(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
|
|
|
|
|
2018-01-08 17:24:54 -08:00
|
|
|
def point_to_coords(self, point):
|
|
|
|
return (self.x_axis.point_to_number(point),
|
|
|
|
self.y_axis.point_to_number(point))
|
|
|
|
|
2017-01-27 19:31:20 -08:00
|
|
|
def get_graph(
|
|
|
|
self, func,
|
|
|
|
color = None,
|
|
|
|
x_min = None,
|
|
|
|
x_max = None,
|
|
|
|
):
|
2017-01-09 11:22:28 -08:00
|
|
|
if color is None:
|
2017-04-07 16:05:51 -07:00
|
|
|
color = self.default_graph_colors_cycle.next()
|
2017-01-27 19:31:20 -08:00
|
|
|
if x_min is None:
|
|
|
|
x_min = self.x_min
|
|
|
|
if x_max is None:
|
|
|
|
x_max = self.x_max
|
2016-11-11 14:42:40 -08:00
|
|
|
|
2017-01-05 11:57:13 -08:00
|
|
|
def parameterized_function(alpha):
|
2017-01-27 19:31:20 -08:00
|
|
|
x = interpolate(x_min, x_max, alpha)
|
2017-01-28 18:18:32 -08:00
|
|
|
y = func(x)
|
|
|
|
if not np.isfinite(y):
|
|
|
|
y = self.y_max
|
|
|
|
return self.coords_to_point(x, y)
|
2016-11-11 14:42:40 -08:00
|
|
|
|
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
|
|
|
return graph
|
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def input_to_graph_point(self, x, 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
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def angle_of_tangent(self, x, graph, dx = 0.01):
|
2017-01-05 11:57:13 -08:00
|
|
|
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-24 16:21:35 -08:00
|
|
|
def slope_of_tangent(self, *args, **kwargs):
|
|
|
|
return np.tan(self.angle_of_tangent(*args, **kwargs))
|
2017-01-05 11:57:13 -08:00
|
|
|
|
2017-01-27 19:31:20 -08:00
|
|
|
def get_derivative_graph(self, graph, dx = 0.01, **kwargs):
|
|
|
|
if "color" not in kwargs:
|
|
|
|
kwargs["color"] = self.default_derivative_color
|
|
|
|
def deriv(x):
|
|
|
|
return self.slope_of_tangent(x, graph, dx) / self.space_unit_to_y
|
|
|
|
return self.get_graph(deriv, **kwargs)
|
2017-01-05 11:57:13 -08:00
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def get_graph_label(
|
|
|
|
self,
|
|
|
|
graph,
|
|
|
|
label = "f(x)",
|
|
|
|
x_val = None,
|
|
|
|
direction = RIGHT,
|
2017-01-25 16:40:59 -08:00
|
|
|
buff = MED_SMALL_BUFF,
|
2017-01-24 16:21:35 -08:00
|
|
|
color = None,
|
|
|
|
):
|
2016-11-11 14:42:40 -08:00
|
|
|
label = TexMobject(label)
|
2017-01-20 09:26:14 -08:00
|
|
|
color = color or graph.get_color()
|
2018-03-30 11:51:31 -07:00
|
|
|
label.set_color(color)
|
2017-01-24 16:21:35 -08:00
|
|
|
if x_val is None:
|
2017-01-25 13:00:01 -08:00
|
|
|
#Search from right to left
|
|
|
|
for x in np.linspace(self.x_max, self.x_min, 100):
|
|
|
|
point = self.input_to_graph_point(x, graph)
|
2018-03-30 11:25:37 -07:00
|
|
|
if point[1] < FRAME_Y_RADIUS:
|
2017-01-24 16:21:35 -08:00
|
|
|
break
|
2017-01-25 13:00:01 -08:00
|
|
|
x_val = x
|
2016-11-11 14:42:40 -08:00
|
|
|
label.next_to(
|
2017-01-24 16:21:35 -08:00
|
|
|
self.input_to_graph_point(x_val, graph),
|
2016-11-11 14:42:40 -08:00
|
|
|
direction,
|
|
|
|
buff = buff
|
|
|
|
)
|
2017-01-24 16:21:35 -08:00
|
|
|
label.shift_onto_screen()
|
2016-11-11 14:42:40 -08:00
|
|
|
return label
|
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def get_riemann_rectangles(
|
|
|
|
self,
|
|
|
|
graph,
|
|
|
|
x_min = None,
|
|
|
|
x_max = None,
|
|
|
|
dx = 0.1,
|
2017-04-04 16:57:53 -07:00
|
|
|
input_sample_type = "left",
|
2017-01-24 16:21:35 -08:00
|
|
|
stroke_width = 1,
|
2017-04-18 18:37:15 -07:00
|
|
|
stroke_color = BLACK,
|
2017-04-14 11:05:04 -07:00
|
|
|
fill_opacity = 1,
|
2017-04-27 12:01:33 -07:00
|
|
|
start_color = None,
|
|
|
|
end_color = None,
|
2017-04-14 16:15:39 -07:00
|
|
|
show_signed_area = True,
|
2017-04-15 18:25:06 -07:00
|
|
|
width_scale_factor = 1.001
|
2017-04-14 16:15:39 -07:00
|
|
|
):
|
2016-11-13 11:35:06 -08:00
|
|
|
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
|
2017-04-27 12:01:33 -07:00
|
|
|
if start_color is None:
|
|
|
|
start_color = self.default_riemann_start_color
|
|
|
|
if end_color is None:
|
|
|
|
end_color = self.default_riemann_end_color
|
2016-11-13 11:35:06 -08:00
|
|
|
rectangles = VGroup()
|
2017-04-14 16:15:39 -07:00
|
|
|
x_range = np.arange(x_min, x_max, dx)
|
|
|
|
colors = color_gradient([start_color, end_color], len(x_range))
|
|
|
|
for x, color in zip(x_range, colors):
|
2017-04-04 16:57:53 -07:00
|
|
|
if input_sample_type == "left":
|
|
|
|
sample_input = x
|
|
|
|
elif input_sample_type == "right":
|
|
|
|
sample_input = x+dx
|
|
|
|
else:
|
|
|
|
raise Exception("Invalid input sample type")
|
|
|
|
graph_point = self.input_to_graph_point(sample_input, graph)
|
2016-11-13 11:35:06 -08:00
|
|
|
points = VGroup(*map(VectorizedPoint, [
|
|
|
|
self.coords_to_point(x, 0),
|
2017-04-15 18:25:06 -07:00
|
|
|
self.coords_to_point(x+width_scale_factor*dx, 0),
|
2017-04-04 16:57:53 -07:00
|
|
|
graph_point
|
2016-11-13 11:35:06 -08:00
|
|
|
]))
|
2017-04-04 16:57:53 -07:00
|
|
|
|
2016-11-13 11:35:06 -08:00
|
|
|
rect = Rectangle()
|
|
|
|
rect.replace(points, stretch = True)
|
2017-04-14 16:15:39 -07:00
|
|
|
if graph_point[1] < self.graph_origin[1] and show_signed_area:
|
|
|
|
fill_color = invert_color(color)
|
|
|
|
else:
|
|
|
|
fill_color = color
|
|
|
|
rect.set_fill(fill_color, opacity = fill_opacity)
|
2017-04-18 18:37:15 -07:00
|
|
|
rect.set_stroke(stroke_color, width = stroke_width)
|
2016-11-13 11:35:06 -08:00
|
|
|
rectangles.add(rect)
|
|
|
|
return rectangles
|
|
|
|
|
2017-04-12 09:06:04 -07:00
|
|
|
def get_riemann_rectangles_list(
|
|
|
|
self,
|
|
|
|
graph,
|
|
|
|
n_iterations,
|
|
|
|
max_dx = 0.5,
|
2017-04-14 11:05:04 -07:00
|
|
|
power_base = 2,
|
|
|
|
stroke_width = 1,
|
2017-04-12 09:06:04 -07:00
|
|
|
**kwargs
|
|
|
|
):
|
|
|
|
return [
|
|
|
|
self.get_riemann_rectangles(
|
|
|
|
graph = graph,
|
|
|
|
dx = float(max_dx)/(power_base**n),
|
2017-04-14 11:05:04 -07:00
|
|
|
stroke_width = float(stroke_width)/(power_base**n),
|
2017-04-12 09:06:04 -07:00
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
for n in range(n_iterations)
|
|
|
|
]
|
|
|
|
|
2017-04-14 11:05:04 -07:00
|
|
|
def transform_between_riemann_rects(self, curr_rects, new_rects, **kwargs):
|
|
|
|
transform_kwargs = {
|
|
|
|
"run_time" : 2,
|
|
|
|
"submobject_mode" : "lagged_start"
|
|
|
|
}
|
2017-04-18 18:37:15 -07:00
|
|
|
added_anims = kwargs.get("added_anims", [])
|
2017-04-14 11:05:04 -07:00
|
|
|
transform_kwargs.update(kwargs)
|
|
|
|
curr_rects.align_submobjects(new_rects)
|
|
|
|
x_coords = set() #Keep track of new repetitions
|
|
|
|
for rect in curr_rects:
|
|
|
|
x = rect.get_center()[0]
|
|
|
|
if x in x_coords:
|
|
|
|
rect.set_fill(opacity = 0)
|
|
|
|
else:
|
|
|
|
x_coords.add(x)
|
2017-04-18 18:37:15 -07:00
|
|
|
self.play(
|
|
|
|
Transform(curr_rects, new_rects, **transform_kwargs),
|
|
|
|
*added_anims
|
|
|
|
)
|
2017-04-14 11:05:04 -07:00
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def get_vertical_line_to_graph(
|
|
|
|
self,
|
|
|
|
x, graph,
|
|
|
|
line_class = Line,
|
2017-01-25 13:00:01 -08:00
|
|
|
**line_kwargs
|
2017-01-24 16:21:35 -08:00
|
|
|
):
|
2017-01-06 13:01:34 -08:00
|
|
|
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
|
2017-02-16 13:03:26 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
def get_vertical_lines_to_graph(
|
|
|
|
self, graph,
|
|
|
|
x_min = None,
|
|
|
|
x_max = None,
|
|
|
|
num_lines = 20,
|
|
|
|
**kwargs
|
|
|
|
):
|
|
|
|
x_min = x_min or self.x_min
|
|
|
|
x_max = x_max or self.x_max
|
|
|
|
return VGroup(*[
|
|
|
|
self.get_vertical_line_to_graph(x, graph, **kwargs)
|
|
|
|
for x in np.linspace(x_min, x_max, num_lines)
|
|
|
|
])
|
2017-01-06 13:01:34 -08:00
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
def get_secant_slope_group(
|
|
|
|
self,
|
|
|
|
x, graph,
|
|
|
|
dx = None,
|
|
|
|
dx_line_color = None,
|
|
|
|
df_line_color = None,
|
|
|
|
dx_label = None,
|
|
|
|
df_label = None,
|
|
|
|
include_secant_line = True,
|
|
|
|
secant_line_color = None,
|
|
|
|
secant_line_length = 10,
|
|
|
|
):
|
2017-02-16 13:03:26 -08:00
|
|
|
"""
|
|
|
|
Resulting group is of the form VGroup(
|
|
|
|
dx_line,
|
|
|
|
df_line,
|
|
|
|
dx_label, (if applicable)
|
|
|
|
df_label, (if applicable)
|
|
|
|
secant_line, (if applicable)
|
|
|
|
)
|
2017-03-22 15:06:17 -07:00
|
|
|
with attributes of those names.
|
2017-02-16 13:03:26 -08:00
|
|
|
"""
|
2017-01-24 16:21:35 -08:00
|
|
|
kwargs = locals()
|
|
|
|
kwargs.pop("self")
|
|
|
|
group = VGroup()
|
|
|
|
group.kwargs = kwargs
|
|
|
|
|
|
|
|
dx = dx or float(self.x_max - self.x_min)/10
|
|
|
|
dx_line_color = dx_line_color or self.default_input_color
|
|
|
|
df_line_color = df_line_color or graph.get_color()
|
|
|
|
|
|
|
|
p1 = self.input_to_graph_point(x, graph)
|
|
|
|
p2 = self.input_to_graph_point(x+dx, graph)
|
|
|
|
interim_point = p2[0]*RIGHT + p1[1]*UP
|
|
|
|
|
|
|
|
group.dx_line = Line(
|
|
|
|
p1, interim_point,
|
|
|
|
color = dx_line_color
|
|
|
|
)
|
|
|
|
group.df_line = Line(
|
|
|
|
interim_point, p2,
|
|
|
|
color = df_line_color
|
|
|
|
)
|
2017-01-25 16:40:59 -08:00
|
|
|
group.add(group.dx_line, group.df_line)
|
2017-01-25 13:00:01 -08:00
|
|
|
|
|
|
|
labels = VGroup()
|
2017-01-24 16:21:35 -08:00
|
|
|
if dx_label is not None:
|
|
|
|
group.dx_label = TexMobject(dx_label)
|
2017-01-25 13:00:01 -08:00
|
|
|
labels.add(group.dx_label)
|
2017-01-25 16:40:59 -08:00
|
|
|
group.add(group.dx_label)
|
2017-01-25 13:00:01 -08:00
|
|
|
if df_label is not None:
|
|
|
|
group.df_label = TexMobject(df_label)
|
|
|
|
labels.add(group.df_label)
|
2017-01-25 16:40:59 -08:00
|
|
|
group.add(group.df_label)
|
2017-01-25 13:00:01 -08:00
|
|
|
|
|
|
|
if len(labels) > 0:
|
|
|
|
max_width = 0.8*group.dx_line.get_width()
|
|
|
|
max_height = 0.8*group.df_line.get_height()
|
|
|
|
if labels.get_width() > max_width:
|
|
|
|
labels.scale_to_fit_width(max_width)
|
|
|
|
if labels.get_height() > max_height:
|
|
|
|
labels.scale_to_fit_height(max_height)
|
|
|
|
|
|
|
|
if dx_label is not None:
|
|
|
|
group.dx_label.next_to(
|
2017-03-24 13:59:14 -07:00
|
|
|
group.dx_line,
|
|
|
|
np.sign(dx)*DOWN,
|
|
|
|
buff = group.dx_label.get_height()/2
|
2017-01-25 13:00:01 -08:00
|
|
|
)
|
2018-03-30 11:51:31 -07:00
|
|
|
group.dx_label.set_color(group.dx_line.get_color())
|
2017-01-24 16:21:35 -08:00
|
|
|
|
|
|
|
if df_label is not None:
|
2017-01-25 13:00:01 -08:00
|
|
|
group.df_label.next_to(
|
2017-03-24 13:59:14 -07:00
|
|
|
group.df_line,
|
|
|
|
np.sign(dx)*RIGHT,
|
|
|
|
buff = group.df_label.get_height()/2
|
2017-01-25 13:00:01 -08:00
|
|
|
)
|
2018-03-30 11:51:31 -07:00
|
|
|
group.df_label.set_color(group.df_line.get_color())
|
2017-01-24 16:21:35 -08:00
|
|
|
|
|
|
|
if include_secant_line:
|
|
|
|
secant_line_color = secant_line_color or self.default_derivative_color
|
|
|
|
group.secant_line = Line(p1, p2, color = secant_line_color)
|
|
|
|
group.secant_line.scale_in_place(
|
|
|
|
secant_line_length/group.secant_line.get_length()
|
|
|
|
)
|
2017-01-25 16:40:59 -08:00
|
|
|
group.add(group.secant_line)
|
2017-01-24 16:21:35 -08:00
|
|
|
|
|
|
|
return group
|
|
|
|
|
2017-01-25 13:00:01 -08:00
|
|
|
def animate_secant_slope_group_change(
|
|
|
|
self, secant_slope_group,
|
|
|
|
target_dx = None,
|
|
|
|
target_x = None,
|
2017-01-24 16:21:35 -08:00
|
|
|
run_time = 3,
|
2017-01-25 13:00:01 -08:00
|
|
|
added_anims = None,
|
2017-01-24 16:21:35 -08:00
|
|
|
**anim_kwargs
|
|
|
|
):
|
2017-01-25 13:00:01 -08:00
|
|
|
if target_dx is None and target_x is None:
|
|
|
|
raise Exception("At least one of target_x and target_dx must not be None")
|
|
|
|
if added_anims is None:
|
|
|
|
added_anims = []
|
|
|
|
|
2017-01-24 16:21:35 -08:00
|
|
|
start_dx = secant_slope_group.kwargs["dx"]
|
2017-01-25 13:00:01 -08:00
|
|
|
start_x = secant_slope_group.kwargs["x"]
|
|
|
|
if target_dx is None:
|
|
|
|
target_dx = start_dx
|
|
|
|
if target_x is None:
|
|
|
|
target_x = start_x
|
2017-01-24 16:21:35 -08:00
|
|
|
def update_func(group, alpha):
|
|
|
|
dx = interpolate(start_dx, target_dx, alpha)
|
2017-01-25 13:00:01 -08:00
|
|
|
x = interpolate(start_x, target_x, alpha)
|
2017-01-24 16:21:35 -08:00
|
|
|
kwargs = dict(secant_slope_group.kwargs)
|
|
|
|
kwargs["dx"] = dx
|
2017-01-25 13:00:01 -08:00
|
|
|
kwargs["x"] = x
|
2017-01-24 16:21:35 -08:00
|
|
|
new_group = self.get_secant_slope_group(**kwargs)
|
|
|
|
Transform(group, new_group).update(1)
|
|
|
|
return group
|
|
|
|
|
2017-01-25 13:00:01 -08:00
|
|
|
self.play(
|
|
|
|
UpdateFromAlphaFunc(
|
|
|
|
secant_slope_group, update_func,
|
|
|
|
run_time = run_time,
|
|
|
|
**anim_kwargs
|
|
|
|
),
|
|
|
|
*added_anims
|
|
|
|
)
|
|
|
|
secant_slope_group.kwargs["x"] = target_x
|
|
|
|
secant_slope_group.kwargs["dx"] = target_dx
|
2017-01-24 16:21:35 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-01-06 13:01:34 -08:00
|
|
|
|
2016-11-11 14:42:40 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|