mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 21:44:19 +08:00
Merge branch 'master' of github.com:3b1b/manim into wallis-g
This commit is contained in:
@ -1,5 +1,9 @@
|
|||||||
from big_ol_pile_of_manim_imports import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
from old_projects.eoc.chapter8 import *
|
from old_projects.eoc.chapter8 import *
|
||||||
|
from active_projects.eop.histograms import *
|
||||||
|
from svgpathtools import *
|
||||||
|
|
||||||
|
import scipy.special
|
||||||
|
|
||||||
COIN_RADIUS = 0.3
|
COIN_RADIUS = 0.3
|
||||||
COIN_THICKNESS = 0.4 * COIN_RADIUS
|
COIN_THICKNESS = 0.4 * COIN_RADIUS
|
||||||
@ -665,19 +669,35 @@ class IllustrateAreaModel1(Scene):
|
|||||||
label_B_knowing_A = label_B
|
label_B_knowing_A = label_B
|
||||||
|
|
||||||
self.play(FadeOut(label_B_copy))
|
self.play(FadeOut(label_B_copy))
|
||||||
|
self.remove(indep_formula.get_part_by_tex("P(B)"))
|
||||||
label_B_knowing_A_copy = label_B_knowing_A.copy()
|
label_B_knowing_A_copy = label_B_knowing_A.copy()
|
||||||
self.add(label_B_knowing_A_copy)
|
self.add(label_B_knowing_A_copy)
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
label_B_knowing_A_copy.next_to, indep_formula[-2], RIGHT
|
label_B_knowing_A_copy.next_to, indep_formula.get_part_by_tex("\cdot"), RIGHT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# solve formula for P(B|A)
|
||||||
|
|
||||||
|
rearranged_formula = TexMobject(["P(B\mid A)", "=", "{P(A\\text{ and }B) \over P(A)}"])
|
||||||
|
rearranged_formula.move_to(indep_formula)
|
||||||
|
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
# in some places get_part_by_tex does not find the correct part
|
||||||
|
# so I picked out fitting indices
|
||||||
|
label_B_knowing_A_copy.move_to, rearranged_formula.get_part_by_tex("P(B\mid A)"),
|
||||||
|
label_A_copy.move_to, rearranged_formula[-1][10],
|
||||||
|
label_A_and_B_copy.move_to, rearranged_formula[-1][3],
|
||||||
|
indep_formula.get_part_by_tex("=").move_to, rearranged_formula.get_part_by_tex("="),
|
||||||
|
Transform(indep_formula.get_part_by_tex("\cdot"), rearranged_formula[-1][8]),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# # # # # # # # # # # # # # # # #
|
||||||
|
# Old version with SampleSpace #
|
||||||
|
# # # # # # # # # # # # # # # # #
|
||||||
|
|
||||||
# def show_independent_events(self):
|
# def show_independent_events(self):
|
||||||
# sample_space = SampleSpace(
|
# sample_space = SampleSpace(
|
||||||
@ -747,62 +767,200 @@ class IllustrateAreaModel1(Scene):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def color_label(self, label):
|
# def color_label(self, label):
|
||||||
label.set_color_by_tex("B", RED)
|
# label.set_color_by_tex("B", RED)
|
||||||
label.set_color_by_tex("I", GREEN)
|
# label.set_color_by_tex("I", GREEN)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class IllustrateAreaModel2(GraphScene):
|
||||||
class IllustrateAreaModel2(AreaIsDerivative):
|
|
||||||
|
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"y_max" : 4,
|
"x_min" : -5,
|
||||||
"y_min" : -4,
|
"x_max" : 5,
|
||||||
"num_iterations" : 7,
|
"y_min" : -0,
|
||||||
|
"y_max" : 0.6,
|
||||||
|
"graph_origin": 3*DOWN,
|
||||||
|
"num_rects": 20,
|
||||||
"y_axis_label" : "",
|
"y_axis_label" : "",
|
||||||
"num_rects" : 400,
|
"x_axis_label" : "",
|
||||||
"dT" : 0.25,
|
"variable_point_label" : "x",
|
||||||
"variable_point_label" : "T",
|
"y_axis_height" : 4
|
||||||
"area_opacity" : 0.8,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
|
||||||
|
x_max_1 = 0
|
||||||
|
x_min_1 = -x_max_1
|
||||||
|
|
||||||
|
x_max_2 = 5
|
||||||
|
x_min_2 = -x_max_2
|
||||||
|
|
||||||
|
|
||||||
self.setup_axes()
|
self.setup_axes()
|
||||||
self.introduce_variable_area()
|
graph = self.get_graph(lambda x: np.exp(-x**2) / ((0.5 * TAU) ** 0.5))
|
||||||
|
|
||||||
graph, label = self.get_v_graph_and_label()
|
self.add(graph)
|
||||||
|
|
||||||
rect_list = self.get_riemann_rectangles_list(
|
|
||||||
graph, self.num_iterations
|
|
||||||
)
|
|
||||||
VGroup(*rect_list).set_fill(opacity = 0.8)
|
|
||||||
rects = rect_list[0]
|
|
||||||
|
|
||||||
self.play(ShowCreation(graph))
|
cdf_formula = TexMobject("P(|X-\mu| < x) = \int_{-x}^x {\exp(-{1\over 2}({t\over \sigma})^2) \over \sigma\sqrt{2\pi}} dt")
|
||||||
self.play(Write(rects))
|
cdf_formula.set_color_by_tex("x", YELLOW)
|
||||||
for new_rects in rect_list[1:]:
|
cdf_formula.next_to(graph, LEFT, buff = 1)
|
||||||
rects.align_submobjects(new_rects)
|
self.add(cdf_formula)
|
||||||
for every_other_rect in rects[::2]:
|
|
||||||
every_other_rect.set_fill(opacity = 0)
|
|
||||||
self.play(Transform(
|
|
||||||
rects, new_rects,
|
|
||||||
run_time = 2,
|
|
||||||
submobject_mode = "lagged_start"
|
|
||||||
))
|
|
||||||
self.wait()
|
|
||||||
|
|
||||||
# self.play(FadeOut(self.x_axis.numbers))
|
self.v_graph = graph
|
||||||
self.add_T_label(6)
|
self.add_T_label(x_min_1, color = YELLOW, animated = False)
|
||||||
self.change_area_bounds(
|
|
||||||
new_t_max = 4,
|
self.remove(self.T_label_group, self.right_v_line)
|
||||||
rate_func = there_and_back,
|
#self.T_label_group[0].set_fill(opacity = 0).set_stroke(width = 0)
|
||||||
run_time = 2
|
#self.T_label_group[1].set_fill(opacity = 0).set_stroke(width = 0)
|
||||||
|
#self.right_v_line.set_fill(opacity = 0).set_stroke(width = 0)
|
||||||
|
|
||||||
|
#self.add(self.T_label_group)
|
||||||
|
area = self.area = self.get_area(graph, x_min_1, x_max_1)
|
||||||
|
|
||||||
|
right_bound_label = TexMobject("x", color = YELLOW)
|
||||||
|
right_bound_label.next_to(self.coords_to_point(0,0), DOWN)
|
||||||
|
right_bound_label.target = right_bound_label.copy().next_to(self.coords_to_point(self.x_max,0), DOWN)
|
||||||
|
right_bound_label.set_fill(opacity = 0).set_stroke(width = 0)
|
||||||
|
|
||||||
|
left_bound_label = TexMobject("-x", color = YELLOW)
|
||||||
|
left_bound_label.next_to(self.coords_to_point(0,0), DOWN)
|
||||||
|
left_bound_label.target = right_bound_label.copy().next_to(self.coords_to_point(self.x_min,0), DOWN)
|
||||||
|
left_bound_label.set_fill(opacity = 0).set_stroke(width = 0)
|
||||||
|
|
||||||
|
#integral = self.get_riemann_rectangles(
|
||||||
|
#graph,x_min = self.x_min, x_max = x_max_1)
|
||||||
|
self.add(area)
|
||||||
|
|
||||||
|
def integral_update_func(t):
|
||||||
|
return 100 * scipy.special.erf(
|
||||||
|
self.point_to_coords(self.right_v_line.get_center())[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
cdf_value = DecimalNumber(0, unit = "\%")
|
||||||
|
cdf_value.move_to(self.coords_to_point(0,0.2))
|
||||||
|
self.add_foreground_mobject(cdf_value)
|
||||||
|
|
||||||
|
self.add(ContinualChangingDecimal(
|
||||||
|
decimal_number_mobject = cdf_value,
|
||||||
|
number_update_func = integral_update_func,
|
||||||
|
num_decimal_points = 1
|
||||||
|
))
|
||||||
|
|
||||||
|
anim = self.get_animation_integral_bounds_change(
|
||||||
|
graph, x_min_2, x_max_2, run_time = 3)
|
||||||
|
|
||||||
|
# changing_cdf_value = ChangingDecimal(
|
||||||
|
# decimal_number_mobject = cdf_value,
|
||||||
|
# number_update_func = integral_update_func,
|
||||||
|
# num_decimal_points = 1
|
||||||
|
# )
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
anim
|
||||||
)
|
)
|
||||||
|
|
||||||
def func(self, x):
|
|
||||||
return np.exp(-x**2/2)
|
|
||||||
|
|
||||||
|
|
||||||
|
class IllustrateAreaModel3(Scene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
formula = TexMobject("E[X] = \sum_{i=1}^N p_i x_i").move_to(3 * LEFT + UP)
|
||||||
|
self.add(formula)
|
||||||
|
|
||||||
|
|
||||||
|
x_scale = 5.0
|
||||||
|
y_scale = 1.0
|
||||||
|
|
||||||
|
probabilities = np.array([1./8, 3./8, 3./8, 1./8])
|
||||||
|
prob_strings = ["{1\over 8}","{3\over 8}","{3\over 8}","{1\over 8}"]
|
||||||
|
cumulative_probabilities = np.cumsum(probabilities)
|
||||||
|
cumulative_probabilities = np.insert(cumulative_probabilities, 0, 0)
|
||||||
|
print cumulative_probabilities
|
||||||
|
y_values = np.array([0, 1, 2, 3])
|
||||||
|
|
||||||
|
hist = Histogram(probabilities, y_values,
|
||||||
|
mode = "widths",
|
||||||
|
x_scale = x_scale,
|
||||||
|
y_scale = y_scale,
|
||||||
|
x_labels = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
flat_hist = Histogram(probabilities, 0 * y_values,
|
||||||
|
mode = "widths",
|
||||||
|
x_scale = x_scale,
|
||||||
|
y_scale = y_scale,
|
||||||
|
x_labels = "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(FadeIn(flat_hist))
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(flat_hist, hist)
|
||||||
|
)
|
||||||
|
|
||||||
|
braces = VGroup()
|
||||||
|
p_labels = VGroup()
|
||||||
|
# add x labels (braces)
|
||||||
|
for (p,string,bar) in zip(probabilities, prob_strings,hist.bars):
|
||||||
|
brace = Brace(bar, DOWN, buff = 0.1)
|
||||||
|
p_label = TexMobject(string).next_to(brace, DOWN, buff = SMALL_BUFF).scale(0.7)
|
||||||
|
group = VGroup(brace, p_label)
|
||||||
|
braces.add(brace)
|
||||||
|
p_labels.add(p_label)
|
||||||
|
self.play(
|
||||||
|
Write(group)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
labels = VGroup()
|
||||||
|
for (y, bar) in zip(y_values, hist.bars):
|
||||||
|
label = TexMobject(str(int(y))).scale(0.7).next_to(bar, UP, buff = SMALL_BUFF)
|
||||||
|
self.play(FadeIn(label))
|
||||||
|
labels.add(label)
|
||||||
|
|
||||||
|
y_average = np.mean(y_values)
|
||||||
|
averaged_y_values = y_average * np.ones(np.shape(y_values))
|
||||||
|
|
||||||
|
averaged_hist = flat_hist = Histogram(probabilities, averaged_y_values,
|
||||||
|
mode = "widths",
|
||||||
|
x_scale = x_scale,
|
||||||
|
y_scale = y_scale,
|
||||||
|
x_labels = "none"
|
||||||
|
).fade(0.2)
|
||||||
|
|
||||||
|
ghost_hist = hist.copy().fade(0.8)
|
||||||
|
labels.fade(0.8)
|
||||||
|
self.bring_to_back(ghost_hist)
|
||||||
|
|
||||||
|
self.play(Transform(hist, averaged_hist))
|
||||||
|
|
||||||
|
average_label = TexMobject(str(y_average)).scale(0.7).next_to(averaged_hist, UP, SMALL_BUFF)
|
||||||
|
|
||||||
|
one_brace = Brace(averaged_hist, DOWN, buff = 0.1)
|
||||||
|
one_p_label = TexMobject(str(1)).next_to(one_brace, DOWN, buff = SMALL_BUFF).scale(0.7)
|
||||||
|
one_group = VGroup(one_brace, one_p_label)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeIn(average_label),
|
||||||
|
Transform(braces, one_brace),
|
||||||
|
Transform(p_labels, one_p_label),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AreaSplitting(Scene):
|
class AreaSplitting(Scene):
|
||||||
@ -932,6 +1090,62 @@ class AreaSplitting(Scene):
|
|||||||
#self.play(FadeIn(tally))
|
#self.play(FadeIn(tally))
|
||||||
|
|
||||||
|
|
||||||
|
class DieFace(SVGMobject):
|
||||||
|
|
||||||
|
def __init__(self, value, **kwargs):
|
||||||
|
|
||||||
|
self.value = value
|
||||||
|
self.file_name = "Dice-" + str(value)
|
||||||
|
self.ensure_valid_file()
|
||||||
|
|
||||||
|
paths, attributes = svg2paths(self.file_path)
|
||||||
|
print paths, attributes
|
||||||
|
SVGMobject.__init__(self, file_name = self.file_name)
|
||||||
|
# for submob in self.submobject_family():
|
||||||
|
# if type(submob) == Rectangle:
|
||||||
|
# submob.set_fill(opacity = 0)
|
||||||
|
# submob.set_stroke(width = 7)
|
||||||
|
|
||||||
|
class RowOfDice(VGroup):
|
||||||
|
CONFIG = {
|
||||||
|
"values" : range(1,7)
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_points(self):
|
||||||
|
for value in self.values:
|
||||||
|
new_die = DieFace(value)
|
||||||
|
new_die.submobjects[0].set_fill(opacity = 0)
|
||||||
|
new_die.submobjects[0].set_stroke(width = 7)
|
||||||
|
new_die.next_to(self, RIGHT)
|
||||||
|
self.add(new_die)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ShowUncertainty(PiCreatureScene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
row_of_dice = RowOfDice().scale(0.5).move_to(ORIGIN)
|
||||||
|
self.add(row_of_dice)
|
||||||
|
rounded_rect = RoundedRectangle(
|
||||||
|
width = 3,
|
||||||
|
height = 2,
|
||||||
|
corner_radius = 0.1
|
||||||
|
).shift(3*LEFT)
|
||||||
|
self.add(rounded_rect)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,190 +2,231 @@ from big_ol_pile_of_manim_imports import *
|
|||||||
from random import *
|
from random import *
|
||||||
|
|
||||||
def text_range(start,stop,step): # a range as a list of strings
|
def text_range(start,stop,step): # a range as a list of strings
|
||||||
numbers = np.arange(start,stop,step)
|
numbers = np.arange(start,stop,step)
|
||||||
labels = []
|
labels = []
|
||||||
for x in numbers:
|
for x in numbers:
|
||||||
labels.append(str(x))
|
labels.append(str(x))
|
||||||
return labels
|
return labels
|
||||||
|
|
||||||
|
|
||||||
class Histogram(VMobject):
|
class Histogram(VMobject):
|
||||||
|
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"start_color" : RED,
|
"start_color" : RED,
|
||||||
"end_color" : BLUE,
|
"end_color" : BLUE,
|
||||||
"x_scale" : 1.0,
|
"x_scale" : 1.0,
|
||||||
"y_scale" : 1.0,
|
"y_scale" : 1.0,
|
||||||
}
|
"x_labels" : "auto",
|
||||||
|
"x_min" : 0
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, x_values, y_values, **kwargs):
|
def __init__(self, x_values, y_values, mode = "widths", **kwargs):
|
||||||
|
# mode = "widths" : x_values means the widths of the bars
|
||||||
|
# mode = "posts" : x_values means the delimiters btw the bars
|
||||||
|
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
|
|
||||||
# preliminaries
|
if mode == "widths" and len(x_values) != len(y_values):
|
||||||
self.x_values = x_values
|
raise Exception("Array lengths do not match up!")
|
||||||
self.y_values = y_values
|
elif mode == "posts" and len(x_values) != len(y_values) + 1:
|
||||||
|
raise Exception("Array lengths do not match up!")
|
||||||
|
|
||||||
self.x_steps = x_values[1:] - x_values[:-1]
|
# preliminaries
|
||||||
self.x_min = x_values[0] - self.x_steps[0] * 0.5
|
self.y_values = np.array(y_values)
|
||||||
self.x_posts = (x_values[1:] + x_values[:-1]) * 0.5
|
|
||||||
self.x_max = x_values[-1] + self.x_steps[-1] * 0.5
|
|
||||||
self.x_posts = np.insert(self.x_posts,0,self.x_min)
|
|
||||||
self.x_posts = np.append(self.x_posts,self.x_max)
|
|
||||||
|
|
||||||
self.x_widths = self.x_posts[1:] - self.x_posts[:-1]
|
if mode == "widths":
|
||||||
|
self.widths = x_values
|
||||||
|
self.posts = np.cumsum(self.widths)
|
||||||
|
self.posts = np.insert(self.posts, 0, 0)
|
||||||
|
self.posts += self.x_min
|
||||||
|
self.x_max = self.posts[-1]
|
||||||
|
elif mode == "posts":
|
||||||
|
self.posts = x_values
|
||||||
|
self.widths = x_values[1:] - x_values[:-1]
|
||||||
|
self.x_min = self.posts[0]
|
||||||
|
self.x_max = self.posts[-1]
|
||||||
|
else:
|
||||||
|
raise Exception("Invalid mode or no mode specified!")
|
||||||
|
|
||||||
self.x_values_scaled = self.x_scale * x_values
|
self.x_mids = 0.5 * (self.posts[:-1] + self.posts[1:])
|
||||||
self.x_steps_scaled = self.x_scale * self.x_steps
|
|
||||||
self.x_posts_scaled = self.x_scale * self.x_posts
|
|
||||||
self.x_min_scaled = self.x_scale * self.x_min
|
|
||||||
self.x_max_scaled = self.x_scale * self.x_max
|
|
||||||
self.x_widths_scaled = self.x_scale * self.x_widths
|
|
||||||
|
|
||||||
self.y_values_scaled = self.y_scale * self.y_values
|
self.widths_scaled = self.x_scale * self.widths
|
||||||
|
self.posts_scaled = self.x_scale * self.posts
|
||||||
|
self.x_min_scaled = self.x_scale * self.x_min
|
||||||
|
self.x_max_scaled = self.x_scale * self.x_max
|
||||||
|
|
||||||
VMobject.__init__(self, **kwargs)
|
self.y_values_scaled = self.y_scale * self.y_values
|
||||||
digest_config(self, kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_points(self):
|
VMobject.__init__(self, **kwargs)
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
|
||||||
|
|
||||||
previous_bar = ORIGIN
|
def generate_points(self):
|
||||||
self.bars = []
|
|
||||||
outline_points = []
|
|
||||||
self.x_labels = text_range(self.x_values[0], self.x_max, self.x_steps[0])
|
|
||||||
|
|
||||||
for (i,x) in enumerate(self.x_values):
|
def empty_string_array(n):
|
||||||
|
arr = []
|
||||||
|
for i in range(n):
|
||||||
|
arr.append("")
|
||||||
|
return arr
|
||||||
|
|
||||||
bar = Rectangle(
|
def num_arr_to_string_arr(arr): # converts number array to string array
|
||||||
width = self.x_widths_scaled[i],
|
ret_arr = []
|
||||||
height = self.y_values_scaled[i],
|
for x in arr:
|
||||||
)
|
ret_arr.append(str(x))
|
||||||
t = float(x - self.x_values[0])/(self.x_values[-1] - self.x_values[0])
|
return ret_arr
|
||||||
bar_color = interpolate_color(
|
|
||||||
self.start_color,
|
|
||||||
self.end_color,
|
|
||||||
t
|
|
||||||
)
|
|
||||||
bar.set_fill(color = bar_color, opacity = 1)
|
|
||||||
bar.set_stroke(width = 0)
|
|
||||||
bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN)
|
|
||||||
|
|
||||||
self.add(bar)
|
|
||||||
self.bars.append(bar)
|
|
||||||
|
|
||||||
label = TextMobject(self.x_labels[i])
|
previous_bar = ORIGIN
|
||||||
label.next_to(bar,DOWN)
|
self.bars = []
|
||||||
self.add(label)
|
outline_points = []
|
||||||
|
if self.x_labels == "widths":
|
||||||
|
self.x_labels = num_arr_to_string_arr(self.widths)
|
||||||
|
elif self.x_labels == "mids":
|
||||||
|
print self.x_mids
|
||||||
|
self.x_labels = num_arr_to_string_arr(self.x_mids)
|
||||||
|
elif self.x_labels == "none":
|
||||||
|
self.x_labels = empty_string_array(len(self.widths))
|
||||||
|
|
||||||
if i == 0:
|
print self.x_labels
|
||||||
# start with the lower left
|
|
||||||
outline_points.append(bar.get_anchors()[-2])
|
|
||||||
|
|
||||||
# upper two points of each bar
|
for (i,x) in enumerate(self.x_mids):
|
||||||
outline_points.append(bar.get_anchors()[0])
|
|
||||||
outline_points.append(bar.get_anchors()[1])
|
|
||||||
|
|
||||||
previous_bar = bar
|
bar = Rectangle(
|
||||||
|
width = self.widths_scaled[i],
|
||||||
|
height = self.y_values_scaled[i],
|
||||||
|
)
|
||||||
|
t = float(x - self.x_min)/(self.x_max - self.x_min)
|
||||||
|
bar_color = interpolate_color(
|
||||||
|
self.start_color,
|
||||||
|
self.end_color,
|
||||||
|
t
|
||||||
|
)
|
||||||
|
bar.set_fill(color = bar_color, opacity = 1)
|
||||||
|
bar.set_stroke(width = 0)
|
||||||
|
bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN)
|
||||||
|
|
||||||
|
self.add(bar)
|
||||||
|
self.bars.append(bar)
|
||||||
|
|
||||||
# close the outline
|
label = TextMobject(self.x_labels[i])
|
||||||
# lower right
|
label.next_to(bar,DOWN)
|
||||||
outline_points.append(bar.get_anchors()[2])
|
self.add(label)
|
||||||
# lower left
|
|
||||||
outline_points.append(outline_points[0])
|
if i == 0:
|
||||||
|
# start with the lower left
|
||||||
|
outline_points.append(bar.get_anchors()[-2])
|
||||||
|
|
||||||
|
# upper two points of each bar
|
||||||
|
outline_points.append(bar.get_anchors()[0])
|
||||||
|
outline_points.append(bar.get_anchors()[1])
|
||||||
|
|
||||||
|
previous_bar = bar
|
||||||
|
# close the outline
|
||||||
|
# lower right
|
||||||
|
outline_points.append(bar.get_anchors()[2])
|
||||||
|
# lower left
|
||||||
|
outline_points.append(outline_points[0])
|
||||||
|
|
||||||
|
self.outline = Polygon(*outline_points)
|
||||||
|
self.outline.set_stroke(color = WHITE)
|
||||||
|
self.add(self.outline)
|
||||||
|
|
||||||
|
def get_lower_left_point(self):
|
||||||
|
return self.bars[0].get_anchors()[-2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BuildUpHistogram(Animation):
|
||||||
|
|
||||||
|
def __init__(self, hist, **kwargs):
|
||||||
|
self.histogram = hist
|
||||||
|
|
||||||
self.outline = Polygon(*outline_points)
|
|
||||||
self.outline.set_stroke(color = WHITE)
|
|
||||||
self.add(self.outline)
|
|
||||||
|
|
||||||
def get_lower_left_point(self):
|
|
||||||
return self.bars[0].get_anchors()[-2]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class FlashThroughHistogram(Animation):
|
class FlashThroughHistogram(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"cell_color" : WHITE,
|
"cell_color" : WHITE,
|
||||||
"cell_opacity" : 0.8,
|
"cell_opacity" : 0.8,
|
||||||
"hist_opacity" : 0.2
|
"hist_opacity" : 0.2
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, mobject, direction = "horizontal", mode = "random", **kwargs):
|
def __init__(self, mobject, direction = "horizontal", mode = "random", **kwargs):
|
||||||
|
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
|
|
||||||
self.cell_height = mobject.y_scale
|
self.cell_height = mobject.y_scale
|
||||||
self.prototype_cell = Rectangle(
|
self.prototype_cell = Rectangle(
|
||||||
width = 1,
|
width = 1,
|
||||||
height = self.cell_height,
|
height = self.cell_height,
|
||||||
fill_color = self.cell_color,
|
fill_color = self.cell_color,
|
||||||
fill_opacity = self.cell_opacity,
|
fill_opacity = self.cell_opacity,
|
||||||
stroke_width = 0,
|
stroke_width = 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
x_values = mobject.x_values
|
x_values = mobject.x_values
|
||||||
y_values = mobject.y_values
|
y_values = mobject.y_values
|
||||||
|
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
|
|
||||||
self.generate_cell_indices(x_values,y_values)
|
self.generate_cell_indices(x_values,y_values)
|
||||||
Animation.__init__(self,mobject,**kwargs)
|
Animation.__init__(self,mobject,**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_cell_indices(self,x_values,y_values):
|
def generate_cell_indices(self,x_values,y_values):
|
||||||
|
|
||||||
self.cell_indices = []
|
self.cell_indices = []
|
||||||
for (i,x) in enumerate(x_values):
|
for (i,x) in enumerate(x_values):
|
||||||
|
|
||||||
nb_cells = y_values[i]
|
nb_cells = y_values[i]
|
||||||
for j in range(nb_cells):
|
for j in range(nb_cells):
|
||||||
self.cell_indices.append((i, j))
|
self.cell_indices.append((i, j))
|
||||||
|
|
||||||
self.reordered_cell_indices = self.cell_indices
|
self.reordered_cell_indices = self.cell_indices
|
||||||
if self.mode == "random":
|
if self.mode == "random":
|
||||||
shuffle(self.reordered_cell_indices)
|
shuffle(self.reordered_cell_indices)
|
||||||
|
|
||||||
|
|
||||||
def cell_for_index(self,i,j):
|
def cell_for_index(self,i,j):
|
||||||
|
|
||||||
if self.direction == "vertical":
|
if self.direction == "vertical":
|
||||||
width = self.mobject.x_scale
|
width = self.mobject.x_scale
|
||||||
height = self.mobject.y_scale
|
height = self.mobject.y_scale
|
||||||
x = (i + 0.5) * self.mobject.x_scale
|
x = (i + 0.5) * self.mobject.x_scale
|
||||||
y = (j + 0.5) * self.mobject.y_scale
|
y = (j + 0.5) * self.mobject.y_scale
|
||||||
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
||||||
|
|
||||||
elif self.direction == "horizontal":
|
elif self.direction == "horizontal":
|
||||||
width = self.mobject.x_scale / self.mobject.y_values[i]
|
width = self.mobject.x_scale / self.mobject.y_values[i]
|
||||||
height = self.mobject.y_scale * self.mobject.y_values[i]
|
height = self.mobject.y_scale * self.mobject.y_values[i]
|
||||||
x = i * self.mobject.x_scale + (j + 0.5) * width
|
x = i * self.mobject.x_scale + (j + 0.5) * width
|
||||||
y = height / 2
|
y = height / 2
|
||||||
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
||||||
|
|
||||||
cell = Rectangle(width = width, height = height)
|
cell = Rectangle(width = width, height = height)
|
||||||
cell.move_to(center)
|
cell.move_to(center)
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
|
|
||||||
def update_mobject(self,t):
|
def update_mobject(self,t):
|
||||||
|
|
||||||
if t == 0:
|
if t == 0:
|
||||||
self.mobject.add(self.prototype_cell)
|
self.mobject.add(self.prototype_cell)
|
||||||
|
|
||||||
flash_nb = int(t * (len(self.cell_indices))) - 1
|
flash_nb = int(t * (len(self.cell_indices))) - 1
|
||||||
(i,j) = self.reordered_cell_indices[flash_nb]
|
(i,j) = self.reordered_cell_indices[flash_nb]
|
||||||
cell = self.cell_for_index(i,j)
|
cell = self.cell_for_index(i,j)
|
||||||
self.prototype_cell.width = cell.get_width()
|
self.prototype_cell.width = cell.get_width()
|
||||||
self.prototype_cell.height = cell.get_height()
|
self.prototype_cell.height = cell.get_height()
|
||||||
self.prototype_cell.generate_points()
|
self.prototype_cell.generate_points()
|
||||||
self.prototype_cell.move_to(cell.get_center())
|
self.prototype_cell.move_to(cell.get_center())
|
||||||
|
|
||||||
#if t == 1:
|
#if t == 1:
|
||||||
# self.mobject.remove(self.prototype_cell)
|
# self.mobject.remove(self.prototype_cell)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -205,47 +246,47 @@ class FlashThroughHistogram(Animation):
|
|||||||
|
|
||||||
class SampleScene(Scene):
|
class SampleScene(Scene):
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
|
||||||
x_values = np.array([1,2,3,4,5])
|
x_values = np.array([1,2,3,4,5])
|
||||||
y_values = np.array([4,3,5,2,3])
|
y_values = np.array([4,3,5,2,3])
|
||||||
|
|
||||||
hist1 = Histogram(
|
hist1 = Histogram(
|
||||||
x_values = x_values,
|
x_values = x_values,
|
||||||
y_values = y_values,
|
y_values = y_values,
|
||||||
x_scale = 0.5,
|
x_scale = 0.5,
|
||||||
y_scale = 0.5,
|
y_scale = 0.5,
|
||||||
).shift(1*DOWN)
|
).shift(1*DOWN)
|
||||||
self.add(hist1)
|
self.add(hist1)
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
y_values2 = np.array([3,8,7,15,5])
|
y_values2 = np.array([3,8,7,15,5])
|
||||||
|
|
||||||
hist2 = Histogram(
|
hist2 = Histogram(
|
||||||
x_values = x_values,
|
x_values = x_values,
|
||||||
y_values = y_values2,
|
y_values = y_values2,
|
||||||
x_scale = 0.5,
|
x_scale = 0.5,
|
||||||
y_scale = 0.5,
|
y_scale = 0.5,
|
||||||
x_labels = text_range(1,6,1),
|
x_labels = text_range(1,6,1),
|
||||||
)
|
)
|
||||||
|
|
||||||
v1 = hist1.get_lower_left_point()
|
v1 = hist1.get_lower_left_point()
|
||||||
v2 = hist2.get_lower_left_point()
|
v2 = hist2.get_lower_left_point()
|
||||||
hist2.shift(v1 - v2)
|
hist2.shift(v1 - v2)
|
||||||
|
|
||||||
# self.play(
|
# self.play(
|
||||||
# ReplacementTransform(hist1,hist2)
|
# ReplacementTransform(hist1,hist2)
|
||||||
# )
|
# )
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
FlashThroughHistogram(
|
FlashThroughHistogram(
|
||||||
hist1,
|
hist1,
|
||||||
direction = "horizontal",
|
direction = "horizontal",
|
||||||
mode = "linear",
|
mode = "linear",
|
||||||
run_time = 10,
|
run_time = 10,
|
||||||
rate_func = None,
|
rate_func = None,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -717,6 +717,41 @@ class Square(Rectangle):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class RoundedRectangle(Rectangle):
|
||||||
|
CONFIG = {
|
||||||
|
"corner_radius" : 0.5,
|
||||||
|
"close_new_points" : True
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_points(self):
|
||||||
|
y, x = self.height / 2., self.width / 2.
|
||||||
|
r = self.corner_radius
|
||||||
|
|
||||||
|
arc_ul = ArcBetweenPoints(x * LEFT + (y - r) * UP, (x - r) * LEFT + y * UP, angle = -TAU/4)
|
||||||
|
arc_ur = ArcBetweenPoints((x - r) * RIGHT + y * UP, x * RIGHT + (y - r) * UP, angle = -TAU/4)
|
||||||
|
arc_lr = ArcBetweenPoints(x * RIGHT + (y - r) * DOWN, (x - r) * RIGHT + y * DOWN, angle = -TAU/4)
|
||||||
|
arc_ll = ArcBetweenPoints(x * LEFT + (y - r) * DOWN, (x - r) * LEFT + y * DOWN, angle = TAU/4) # sic! bug in ArcBetweenPoints?
|
||||||
|
|
||||||
|
points = arc_ul.points
|
||||||
|
points = np.append(points,np.array([y * UP]), axis = 0)
|
||||||
|
points = np.append(points,np.array([y * UP]), axis = 0)
|
||||||
|
points = np.append(points,arc_ur.points, axis = 0)
|
||||||
|
points = np.append(points,np.array([x * RIGHT]), axis = 0)
|
||||||
|
points = np.append(points,np.array([x * RIGHT]), axis = 0)
|
||||||
|
points = np.append(points,arc_lr.points, axis = 0)
|
||||||
|
points = np.append(points,np.array([y * DOWN]), axis = 0)
|
||||||
|
points = np.append(points,np.array([y * DOWN]), axis = 0)
|
||||||
|
points = np.append(points,arc_ll.points[::-1], axis = 0) # sic! see comment above
|
||||||
|
points = np.append(points,np.array([x * LEFT]), axis = 0)
|
||||||
|
points = np.append(points,np.array([x * LEFT]), axis = 0)
|
||||||
|
points = np.append(points,np.array([x * LEFT + (y - r) * UP]), axis = 0)
|
||||||
|
|
||||||
|
points = points[::-1]
|
||||||
|
|
||||||
|
self.set_points(points)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Grid(VMobject):
|
class Grid(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"height": 6.0,
|
"height": 6.0,
|
||||||
|
@ -4,10 +4,12 @@ import string
|
|||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
|
from utils.color import *
|
||||||
|
|
||||||
from constants import *
|
from constants import *
|
||||||
from mobject.geometry import Circle
|
from mobject.geometry import Circle
|
||||||
from mobject.geometry import Rectangle
|
from mobject.geometry import Rectangle
|
||||||
|
from mobject.geometry import RoundedRectangle
|
||||||
from utils.bezier import is_closed
|
from utils.bezier import is_closed
|
||||||
from utils.config_ops import digest_config
|
from utils.config_ops import digest_config
|
||||||
from utils.config_ops import digest_locals
|
from utils.config_ops import digest_locals
|
||||||
@ -34,7 +36,7 @@ class SVGMobject(VMobject):
|
|||||||
"file_name": None,
|
"file_name": None,
|
||||||
"unpack_groups": True, # if False, creates a hierarchy of VGroups
|
"unpack_groups": True, # if False, creates a hierarchy of VGroups
|
||||||
"stroke_width": 0,
|
"stroke_width": 0,
|
||||||
"fill_opacity": 1,
|
"fill_opacity": 1.0,
|
||||||
# "fill_color" : LIGHT_GREY,
|
# "fill_color" : LIGHT_GREY,
|
||||||
"propagate_style_to_family": True,
|
"propagate_style_to_family": True,
|
||||||
}
|
}
|
||||||
@ -155,16 +157,54 @@ class SVGMobject(VMobject):
|
|||||||
return Circle().scale(rx * RIGHT + ry * UP).shift(x * RIGHT + y * DOWN)
|
return Circle().scale(rx * RIGHT + ry * UP).shift(x * RIGHT + y * DOWN)
|
||||||
|
|
||||||
def rect_to_mobject(self, rect_element):
|
def rect_to_mobject(self, rect_element):
|
||||||
if rect_element.hasAttribute("fill"):
|
fill_color = rect_element.getAttribute("fill")
|
||||||
if Color(str(rect_element.getAttribute("fill"))) == Color(WHITE):
|
stroke_color = rect_element.getAttribute("stroke")
|
||||||
return
|
stroke_width = rect_element.getAttribute("stroke-width")
|
||||||
mob = Rectangle(
|
corner_radius = rect_element.getAttribute("rx")
|
||||||
width=float(rect_element.getAttribute("width")),
|
|
||||||
height=float(rect_element.getAttribute("height")),
|
# input preprocessing
|
||||||
stroke_width=0,
|
if fill_color in ["", "none", "#FFF", "#FFFFFF"] or Color(fill_color) == Color(WHITE):
|
||||||
fill_color=WHITE,
|
opacity = 0
|
||||||
fill_opacity=1.0
|
fill_color = BLACK # shdn't be necessary but avoids error msgs
|
||||||
)
|
if fill_color in ["#000", "#000000"]:
|
||||||
|
fill_color = WHITE
|
||||||
|
if stroke_color in ["", "none", "#FFF", "#FFFFFF"] or Color(stroke_color) == Color(WHITE):
|
||||||
|
stroke_width = 0
|
||||||
|
stroke_color = BLACK
|
||||||
|
if stroke_color in ["#000", "#000000"]:
|
||||||
|
stroke_color = WHITE
|
||||||
|
if stroke_width in ["", "none", "0"]:
|
||||||
|
stroke_width = 0
|
||||||
|
|
||||||
|
# is there sth to draw?
|
||||||
|
if opacity == 0 and stroke_width == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if corner_radius in ["", "0", "none"]:
|
||||||
|
corner_radius = 0
|
||||||
|
|
||||||
|
corner_radius = float(corner_radius)
|
||||||
|
|
||||||
|
if corner_radius == 0:
|
||||||
|
mob = Rectangle(
|
||||||
|
width = float(rect_element.getAttribute("width")),
|
||||||
|
height = float(rect_element.getAttribute("height")),
|
||||||
|
stroke_width = stroke_width,
|
||||||
|
stroke_color = stroke_color,
|
||||||
|
fill_color = fill_color,
|
||||||
|
fill_opacity = opacity
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
mob = RoundedRectangle(
|
||||||
|
width = float(rect_element.getAttribute("width")),
|
||||||
|
height = float(rect_element.getAttribute("height")),
|
||||||
|
stroke_width = stroke_width,
|
||||||
|
stroke_color = stroke_color,
|
||||||
|
fill_color = fill_color,
|
||||||
|
fill_opacity = opacity,
|
||||||
|
corner_radius = corner_radius
|
||||||
|
)
|
||||||
|
|
||||||
mob.shift(mob.get_center() - mob.get_corner(UP + LEFT))
|
mob.shift(mob.get_center() - mob.get_corner(UP + LEFT))
|
||||||
return mob
|
return mob
|
||||||
|
|
||||||
|
@ -1501,7 +1501,7 @@ class AreaIsDerivative(PlotVelocity, ReconfigurableScene):
|
|||||||
self.add(*self.get_v_graph_and_label())
|
self.add(*self.get_v_graph_and_label())
|
||||||
self.x_axis_label_mob.shift(MED_LARGE_BUFF*DOWN)
|
self.x_axis_label_mob.shift(MED_LARGE_BUFF*DOWN)
|
||||||
self.v_graph_label.shift(MED_LARGE_BUFF*DOWN)
|
self.v_graph_label.shift(MED_LARGE_BUFF*DOWN)
|
||||||
self.foreground_mobjects = []
|
self.foreground_mobjects = []
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.introduce_variable_area()
|
self.introduce_variable_area()
|
||||||
|
@ -4,12 +4,13 @@ from constants import *
|
|||||||
import itertools as it
|
import itertools as it
|
||||||
|
|
||||||
from scene.scene import Scene
|
from scene.scene import Scene
|
||||||
from animation.creation import Write
|
from animation.creation import Write, DrawBorderThenFill, ShowCreation
|
||||||
from animation.transform import Transform
|
from animation.transform import Transform
|
||||||
from animation.update import UpdateFromAlphaFunc
|
from animation.update import UpdateFromAlphaFunc
|
||||||
from mobject.functions import ParametricFunction
|
from mobject.functions import ParametricFunction
|
||||||
from mobject.geometry import Line
|
from mobject.geometry import Line
|
||||||
from mobject.geometry import Rectangle
|
from mobject.geometry import Rectangle
|
||||||
|
from mobject.geometry import RegularPolygon
|
||||||
from mobject.number_line import NumberLine
|
from mobject.number_line import NumberLine
|
||||||
from mobject.svg.tex_mobject import TexMobject
|
from mobject.svg.tex_mobject import TexMobject
|
||||||
from mobject.svg.tex_mobject import TextMobject
|
from mobject.svg.tex_mobject import TextMobject
|
||||||
@ -49,6 +50,8 @@ class GraphScene(Scene):
|
|||||||
"default_input_color": YELLOW,
|
"default_input_color": YELLOW,
|
||||||
"default_riemann_start_color": BLUE,
|
"default_riemann_start_color": BLUE,
|
||||||
"default_riemann_end_color": GREEN,
|
"default_riemann_end_color": GREEN,
|
||||||
|
"area_opacity" : 0.8,
|
||||||
|
"num_rects" : 50,
|
||||||
}
|
}
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
@ -245,6 +248,8 @@ class GraphScene(Scene):
|
|||||||
sample_input = x
|
sample_input = x
|
||||||
elif input_sample_type == "right":
|
elif input_sample_type == "right":
|
||||||
sample_input = x + dx
|
sample_input = x + dx
|
||||||
|
elif input_sample_type == "center":
|
||||||
|
sample_input = x + 0.5 * dx
|
||||||
else:
|
else:
|
||||||
raise Exception("Invalid input sample type")
|
raise Exception("Invalid input sample type")
|
||||||
graph_point = self.input_to_graph_point(sample_input, graph)
|
graph_point = self.input_to_graph_point(sample_input, graph)
|
||||||
@ -284,6 +289,18 @@ class GraphScene(Scene):
|
|||||||
for n in range(n_iterations)
|
for n in range(n_iterations)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_area(self, graph, t_min, t_max):
|
||||||
|
numerator = max(t_max - t_min, 0.01)
|
||||||
|
dx = float(numerator) / self.num_rects
|
||||||
|
return self.get_riemann_rectangles(
|
||||||
|
graph,
|
||||||
|
x_min = t_min,
|
||||||
|
x_max = t_max,
|
||||||
|
dx = dx,
|
||||||
|
stroke_width = 0,
|
||||||
|
).set_fill(opacity = self.area_opacity)
|
||||||
|
|
||||||
def transform_between_riemann_rects(self, curr_rects, new_rects, **kwargs):
|
def transform_between_riemann_rects(self, curr_rects, new_rects, **kwargs):
|
||||||
transform_kwargs = {
|
transform_kwargs = {
|
||||||
"run_time": 2,
|
"run_time": 2,
|
||||||
@ -421,6 +438,84 @@ class GraphScene(Scene):
|
|||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
|
|
||||||
|
def add_T_label(self, x_val, color = WHITE, animated = False, **kwargs):
|
||||||
|
triangle = RegularPolygon(n=3, start_angle = np.pi/2)
|
||||||
|
triangle.scale_to_fit_height(MED_SMALL_BUFF)
|
||||||
|
triangle.move_to(self.coords_to_point(x_val, 0), UP)
|
||||||
|
triangle.set_fill(color, 1)
|
||||||
|
triangle.set_stroke(width = 0)
|
||||||
|
T_label = TexMobject(self.variable_point_label, fill_color = color)
|
||||||
|
T_label.next_to(triangle, DOWN)
|
||||||
|
v_line = self.get_vertical_line_to_graph(
|
||||||
|
x_val, self.v_graph,
|
||||||
|
color = YELLOW
|
||||||
|
)
|
||||||
|
|
||||||
|
if animated:
|
||||||
|
self.play(
|
||||||
|
DrawBorderThenFill(triangle),
|
||||||
|
ShowCreation(v_line),
|
||||||
|
Write(T_label, run_time = 1),
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.add(triangle, v_line, T_label)
|
||||||
|
|
||||||
|
self.T_label_group = VGroup(T_label, triangle)
|
||||||
|
self.right_v_line = v_line
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_animation_integral_bounds_change(
|
||||||
|
self,
|
||||||
|
graph,
|
||||||
|
new_t_min,
|
||||||
|
new_t_max,
|
||||||
|
run_time = 1.0
|
||||||
|
):
|
||||||
|
curr_t_min = self.x_axis.point_to_number(self.area.get_left())
|
||||||
|
curr_t_max = self.x_axis.point_to_number(self.area.get_right())
|
||||||
|
if new_t_min is None:
|
||||||
|
new_t_min = curr_t_min
|
||||||
|
if new_t_max is None:
|
||||||
|
new_t_max = curr_t_max
|
||||||
|
|
||||||
|
group = VGroup(self.area)
|
||||||
|
if hasattr(self, "right_v_line"):
|
||||||
|
group.add(self.right_v_line)
|
||||||
|
else:
|
||||||
|
group.add(VGroup())
|
||||||
|
# because update_group expects 3 elements in group
|
||||||
|
if hasattr(self, "T_label_group"):
|
||||||
|
group.add(self.T_label_group)
|
||||||
|
else:
|
||||||
|
group.add(VGroup())
|
||||||
|
|
||||||
|
def update_group(group, alpha):
|
||||||
|
area, v_line, T_label = group
|
||||||
|
t_min = interpolate(curr_t_min, new_t_min, alpha)
|
||||||
|
t_max = interpolate(curr_t_max, new_t_max, alpha)
|
||||||
|
new_area = self.get_area(graph,t_min, t_max)
|
||||||
|
new_v_line = self.get_vertical_line_to_graph(
|
||||||
|
t_max, graph
|
||||||
|
)
|
||||||
|
new_v_line.set_color(v_line.get_color())
|
||||||
|
T_label.move_to(new_v_line.get_bottom(), UP)
|
||||||
|
|
||||||
|
#Fade close to 0
|
||||||
|
if len(T_label) > 0:
|
||||||
|
T_label[0].set_fill(opacity = min(1, t_max))
|
||||||
|
|
||||||
|
Transform(area, new_area).update(1)
|
||||||
|
Transform(v_line, new_v_line).update(1)
|
||||||
|
return group
|
||||||
|
|
||||||
|
return UpdateFromAlphaFunc(group, update_group, run_time = run_time)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def animate_secant_slope_group_change(
|
def animate_secant_slope_group_change(
|
||||||
self, secant_slope_group,
|
self, secant_slope_group,
|
||||||
target_dx=None,
|
target_dx=None,
|
||||||
@ -462,3 +557,23 @@ class GraphScene(Scene):
|
|||||||
)
|
)
|
||||||
secant_slope_group.kwargs["x"] = target_x
|
secant_slope_group.kwargs["x"] = target_x
|
||||||
secant_slope_group.kwargs["dx"] = target_dx
|
secant_slope_group.kwargs["dx"] = target_dx
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user