mirror of
https://github.com/3b1b/manim.git
synced 2025-07-28 20:43:56 +08:00
663 lines
20 KiB
Python
663 lines
20 KiB
Python
from mobject.tex_mobject import TexMobject
|
|
from mobject import Mobject
|
|
from mobject.image_mobject import ImageMobject
|
|
from mobject.vectorized_mobject import VMobject
|
|
|
|
from animation.animation import Animation
|
|
from animation.transform import *
|
|
from animation.simple_animations import *
|
|
from topics.geometry import *
|
|
from topics.characters import *
|
|
from topics.functions import *
|
|
from topics.number_line import *
|
|
from topics.numerals import *
|
|
from scene import Scene
|
|
from camera import Camera
|
|
from mobject.svg_mobject import *
|
|
from mobject.tex_mobject import *
|
|
from mobject.vectorized_mobject import *
|
|
|
|
from topics.matrix import *
|
|
from topics.vector_space_scene import *
|
|
|
|
from ka_playgrounds.circuits import Resistor, Source, LongResistor
|
|
|
|
class OpeningQuote(Scene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"``On this quiz, I asked you to find the determinant of a",
|
|
"2x3 matrix.",
|
|
"Some of you, to my great amusement, actually tried to do this.''"
|
|
)
|
|
words.scale_to_fit_width(2*SPACE_WIDTH - 2)
|
|
words.to_edge(UP)
|
|
words[1].highlight(GREEN)
|
|
author = TextMobject("-(Via mathprofessorquotes.com, no name listed)")
|
|
author.highlight(YELLOW)
|
|
author.scale(0.7)
|
|
author.next_to(words, DOWN, buff = 0.5)
|
|
|
|
self.play(FadeIn(words))
|
|
self.dither(2)
|
|
self.play(Write(author, run_time = 3))
|
|
self.dither()
|
|
|
|
class AnotherFootnote(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.teacher.look(LEFT)
|
|
self.teacher_says(
|
|
"More footnotes!",
|
|
target_mode = "surprised",
|
|
run_time = 1
|
|
)
|
|
self.random_blink(2)
|
|
|
|
class ColumnsRepresentBasisVectors(Scene):
|
|
def construct(self):
|
|
matrix = Matrix([[3, 1], [4, 1], [5, 9]])
|
|
i_hat_words, j_hat_words = [
|
|
TextMobject("Where $\\hat{\\%smath}$ lands"%char)
|
|
for char in "i", "j"
|
|
]
|
|
i_hat_words.highlight(X_COLOR)
|
|
i_hat_words.next_to(ORIGIN, LEFT).to_edge(UP)
|
|
j_hat_words.highlight(Y_COLOR)
|
|
j_hat_words.next_to(ORIGIN, RIGHT).to_edge(UP)
|
|
question = TextMobject("How to interpret?")
|
|
question.next_to(matrix, UP)
|
|
question.highlight(YELLOW)
|
|
|
|
self.add(matrix)
|
|
self.play(Write(question, run_time = 2))
|
|
self.dither()
|
|
self.play(FadeOut(question))
|
|
for i, words in enumerate([i_hat_words, j_hat_words]):
|
|
arrow = Arrow(
|
|
words.get_bottom(),
|
|
matrix.get_mob_matrix()[0,i].get_top(),
|
|
color = words.get_color()
|
|
)
|
|
self.play(
|
|
Write(words, run_time = 1),
|
|
ShowCreation(arrow),
|
|
*[
|
|
ApplyMethod(m.highlight, words.get_color())
|
|
for m in matrix.get_mob_matrix()[:,i]
|
|
]
|
|
)
|
|
self.dither(2)
|
|
self.put_in_thought_bubble()
|
|
|
|
def put_in_thought_bubble(self):
|
|
everything = VMobject(*self.get_mobjects())
|
|
randy = Randolph().to_corner()
|
|
bubble = randy.get_bubble()
|
|
|
|
self.play(FadeIn(randy))
|
|
self.play(
|
|
ApplyFunction(
|
|
lambda m : bubble.position_mobject_inside(
|
|
m.scale_to_fit_height(2.5)
|
|
),
|
|
everything
|
|
),
|
|
ShowCreation(bubble),
|
|
randy.change_mode, "pondering"
|
|
)
|
|
self.play(Blink(randy))
|
|
self.dither()
|
|
self.play(randy.change_mode, "surprised")
|
|
self.dither()
|
|
|
|
class Symbolic2To3DTransform(Scene):
|
|
def construct(self):
|
|
func = TexMobject("L(", "\\vec{\\textbf{v}}", ")")
|
|
input_array = Matrix([2, 7])
|
|
input_array.highlight(YELLOW)
|
|
in_arrow = Arrow(LEFT, RIGHT, color = input_array.get_color())
|
|
func[1].highlight(input_array.get_color())
|
|
output_array = Matrix([1, 8, 2])
|
|
output_array.highlight(PINK)
|
|
out_arrow = Arrow(LEFT, RIGHT, color = output_array.get_color())
|
|
VMobject(
|
|
input_array, in_arrow, func, out_arrow, output_array
|
|
).arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
|
|
input_brace = Brace(input_array, DOWN)
|
|
input_words = input_brace.get_text("2d input")
|
|
output_brace = Brace(output_array, UP)
|
|
output_words = output_brace.get_text("3d output")
|
|
input_words.highlight(input_array.get_color())
|
|
output_words.highlight(output_array.get_color())
|
|
|
|
|
|
self.add(func, input_array)
|
|
self.play(
|
|
GrowFromCenter(input_brace),
|
|
Write(input_words)
|
|
)
|
|
mover = input_array.copy()
|
|
self.play(
|
|
Transform(mover, Dot().move_to(func)),
|
|
ShowCreation(in_arrow),
|
|
rate_func = rush_into
|
|
)
|
|
self.play(
|
|
Transform(mover, output_array),
|
|
ShowCreation(out_arrow),
|
|
rate_func = rush_from
|
|
)
|
|
self.play(
|
|
GrowFromCenter(output_brace),
|
|
Write(output_words)
|
|
)
|
|
self.dither()
|
|
|
|
class PlaneStartState(LinearTransformationScene):
|
|
def construct(self):
|
|
self.add_title("Input space")
|
|
labels = self.get_basis_vector_labels()
|
|
self.play(*map(Write, labels))
|
|
self.dither()
|
|
|
|
class OutputIn3dWords(Scene):
|
|
def construct(self):
|
|
words = TextMobject("Output in 3d")
|
|
words.scale(1.5)
|
|
self.play(Write(words))
|
|
self.dither()
|
|
|
|
class OutputIn3d(Scene):
|
|
pass
|
|
|
|
class ShowSideBySide2dTo3d(Scene):
|
|
pass
|
|
|
|
class AnimationLaziness(Scene):
|
|
def construct(self):
|
|
self.add(TextMobject("But there is some animation laziness..."))
|
|
|
|
class DescribeColumnsInSpecificTransformation(Scene):
|
|
def construct(self):
|
|
matrix = Matrix([
|
|
[2, 0],
|
|
[-1, 1],
|
|
[-2, 1],
|
|
])
|
|
matrix.highlight_columns(X_COLOR, Y_COLOR)
|
|
mob_matrix = matrix.get_mob_matrix()
|
|
i_col, j_col = [VMobject(*mob_matrix[:,i]) for i in 0, 1]
|
|
for col, char, vect in zip([i_col, j_col], ["i", "j"], [UP, DOWN]):
|
|
color = col[0].get_color()
|
|
col.words = TextMobject("Where $\\hat\\%smath$ lands"%char)
|
|
col.words.next_to(matrix, vect, buff = LARGE_BUFF)
|
|
col.words.highlight(color)
|
|
col.arrow = Arrow(
|
|
col.words.get_edge_center(-vect),
|
|
col.get_edge_center(vect),
|
|
color = color
|
|
)
|
|
|
|
self.play(Write(matrix.get_brackets()))
|
|
self.dither()
|
|
for col in i_col, j_col:
|
|
self.play(
|
|
Write(col),
|
|
ShowCreation(col.arrow),
|
|
Write(col.words, run_time = 1)
|
|
)
|
|
self.dither()
|
|
|
|
class CountRowsAndColumns(Scene):
|
|
def construct(self):
|
|
matrix = Matrix([
|
|
[2, 0],
|
|
[-1, 1],
|
|
[-2, 1],
|
|
])
|
|
matrix.highlight_columns(X_COLOR, Y_COLOR)
|
|
rows_brace = Brace(matrix, LEFT)
|
|
rows_words = rows_brace.get_text("3", "rows")
|
|
rows_words.highlight(PINK)
|
|
cols_brace = Brace(matrix, UP)
|
|
cols_words = cols_brace.get_text("2", "columns")
|
|
cols_words.highlight(TEAL)
|
|
title = TexMobject("3", "\\times", "2", "\\text{ matrix}")
|
|
title.to_edge(UP)
|
|
|
|
self.add(matrix)
|
|
self.play(
|
|
GrowFromCenter(rows_brace),
|
|
Write(rows_words, run_time = 2)
|
|
)
|
|
self.play(
|
|
GrowFromCenter(cols_brace),
|
|
Write(cols_words, run_time = 2)
|
|
)
|
|
self.dither()
|
|
self.play(
|
|
rows_words[0].copy().move_to, title[0],
|
|
cols_words[0].copy().move_to, title[2],
|
|
Write(VMobject(title[1], title[3]))
|
|
)
|
|
self.dither()
|
|
|
|
class WriteColumnSpaceDefinition(Scene):
|
|
def construct(self):
|
|
matrix = Matrix([
|
|
[2, 0],
|
|
[-1, 1],
|
|
[-2, 1],
|
|
])
|
|
matrix.highlight_columns(X_COLOR, Y_COLOR)
|
|
|
|
brace = Brace(matrix)
|
|
words = VMobject(
|
|
TextMobject("Span", "of columns"),
|
|
TexMobject("\\Updownarrow"),
|
|
TextMobject("``Column space''")
|
|
)
|
|
words.arrange_submobjects(DOWN, buff = 0.1)
|
|
words.next_to(brace, DOWN)
|
|
words[0][0].highlight(PINK)
|
|
words[2].highlight(TEAL)
|
|
words[0].add_background_rectangle()
|
|
words[2].add_background_rectangle()
|
|
VMobject(matrix, brace, words).center()
|
|
|
|
self.add(matrix)
|
|
self.play(
|
|
GrowFromCenter(brace),
|
|
Write(words, run_time = 2)
|
|
)
|
|
self.dither()
|
|
|
|
class MatrixInTheWild(Scene):
|
|
def construct(self):
|
|
randy = Randolph(color = PINK)
|
|
randy.look(LEFT)
|
|
randy.to_corner()
|
|
matrix = Matrix([
|
|
[3, 1],
|
|
[4, 1],
|
|
[5, 9],
|
|
])
|
|
matrix.next_to(randy, RIGHT, buff = LARGE_BUFF, aligned_edge = DOWN)
|
|
bubble = randy.get_bubble(height = 4)
|
|
bubble.make_green_screen()
|
|
VMobject(randy, bubble, matrix).to_corner(UP+LEFT, buff = MED_BUFF)
|
|
|
|
self.add(randy)
|
|
self.play(Write(matrix))
|
|
self.play(randy.look, RIGHT, run_time = 0.5)
|
|
self.play(randy.change_mode, "sassy")
|
|
self.play(Blink(randy))
|
|
self.play(
|
|
ShowCreation(bubble),
|
|
randy.change_mode, "pondering"
|
|
)
|
|
# self.play(matrix.highlight_columns, X_COLOR, Y_COLOR)
|
|
self.dither()
|
|
for x in range(3):
|
|
self.play(Blink(randy))
|
|
self.dither(2)
|
|
new_matrix = Matrix([[3, 1, 4], [1, 5, 9]])
|
|
new_matrix.move_to(matrix, aligned_edge = UP+LEFT)
|
|
self.play(
|
|
Transform(matrix, new_matrix),
|
|
FadeOut(bubble)
|
|
)
|
|
self.remove(matrix)
|
|
matrix = new_matrix
|
|
self.add(matrix)
|
|
self.play(randy.look, DOWN+RIGHT, run_time = 0.5)
|
|
self.play(randy.change_mode, "confused")
|
|
self.dither()
|
|
self.play(Blink(randy))
|
|
self.dither()
|
|
|
|
top_brace = Brace(matrix, UP)
|
|
top_words = top_brace.get_text("3 basis vectors")
|
|
top_words.submobject_gradient_highlight(GREEN, RED, BLUE)
|
|
side_brace = Brace(matrix, RIGHT)
|
|
side_words = side_brace.get_text("""
|
|
2 coordinates for
|
|
each landing spots
|
|
""")
|
|
side_words.highlight(YELLOW)
|
|
|
|
self.play(
|
|
GrowFromCenter(top_brace),
|
|
Write(top_words),
|
|
matrix.highlight_columns, X_COLOR, Y_COLOR, Z_COLOR
|
|
)
|
|
self.play(randy.change_mode, "happy")
|
|
self.play(
|
|
GrowFromCenter(side_brace),
|
|
Write(side_words, run_time = 2)
|
|
)
|
|
self.play(Blink(randy))
|
|
self.dither()
|
|
|
|
class ThreeDToTwoDInput(Scene):
|
|
pass
|
|
|
|
class ThreeDToTwoDInputWords(Scene):
|
|
def construct(self):
|
|
words = TextMobject("3d input")
|
|
words.scale(2)
|
|
self.play(Write(words))
|
|
self.dither()
|
|
|
|
class ThreeDToTwoDOutput(LinearTransformationScene):
|
|
CONFIG = {
|
|
"show_basis_vectors" : False,
|
|
"foreground_plane_kwargs" : {
|
|
"color" : GREY,
|
|
"x_radius" : SPACE_WIDTH,
|
|
"y_radius" : SPACE_HEIGHT,
|
|
"secondary_line_ratio" : 0
|
|
},
|
|
}
|
|
def construct(self):
|
|
title = TextMobject("Output in 2d")
|
|
title.to_edge(UP, buff = SMALL_BUFF)
|
|
subwords = TextMobject("""
|
|
(only showing basis vectors,
|
|
full 3d grid would be a mess)
|
|
""")
|
|
subwords.scale(0.75)
|
|
subwords.next_to(title, DOWN)
|
|
for words in title, subwords:
|
|
words.add_background_rectangle()
|
|
|
|
self.add(title)
|
|
i, j, k = it.starmap(self.add_vector, [
|
|
([3, 1], X_COLOR),
|
|
([1, 2], Y_COLOR),
|
|
([-2, -2], Z_COLOR)
|
|
])
|
|
pairs = [
|
|
(i, "L(\\hat\\imath)"),
|
|
(j, "L(\\hat\\jmath)"),
|
|
(k, "L(\\hat k)")
|
|
]
|
|
for v, tex in pairs:
|
|
self.label_vector(v, tex)
|
|
self.play(Write(subwords))
|
|
self.dither()
|
|
|
|
class ThreeDToTwoDSideBySide(Scene):
|
|
pass
|
|
|
|
class Symbolic2To1DTransform(Scene):
|
|
def construct(self):
|
|
func = TexMobject("L(", "\\vec{\\textbf{v}}", ")")
|
|
input_array = Matrix([2, 7])
|
|
input_array.highlight(YELLOW)
|
|
in_arrow = Arrow(LEFT, RIGHT, color = input_array.get_color())
|
|
func[1].highlight(input_array.get_color())
|
|
output_array = Matrix([1.8])
|
|
output_array.highlight(PINK)
|
|
out_arrow = Arrow(LEFT, RIGHT, color = output_array.get_color())
|
|
VMobject(
|
|
input_array, in_arrow, func, out_arrow, output_array
|
|
).arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
|
|
input_brace = Brace(input_array, DOWN)
|
|
input_words = input_brace.get_text("2d input")
|
|
output_brace = Brace(output_array, UP)
|
|
output_words = output_brace.get_text("1d output")
|
|
input_words.highlight(input_array.get_color())
|
|
output_words.highlight(output_array.get_color())
|
|
|
|
|
|
self.add(func, input_array)
|
|
self.play(
|
|
GrowFromCenter(input_brace),
|
|
Write(input_words)
|
|
)
|
|
mover = input_array.copy()
|
|
self.play(
|
|
Transform(mover, Dot().move_to(func)),
|
|
ShowCreation(in_arrow),
|
|
rate_func = rush_into,
|
|
run_time = 0.5
|
|
)
|
|
self.play(
|
|
Transform(mover, output_array),
|
|
ShowCreation(out_arrow),
|
|
rate_func = rush_from,
|
|
run_time = 0.5
|
|
)
|
|
self.play(
|
|
GrowFromCenter(output_brace),
|
|
Write(output_words)
|
|
)
|
|
self.dither()
|
|
|
|
class TwoDTo1DTransform(LinearTransformationScene):
|
|
CONFIG = {
|
|
"include_background_plane" : False,
|
|
"foreground_plane_kwargs" : {
|
|
"x_radius" : SPACE_WIDTH,
|
|
"y_radius" : SPACE_HEIGHT,
|
|
"secondary_line_ratio" : 1
|
|
},
|
|
"t_matrix" : [[1, 0], [2, 0]],
|
|
}
|
|
def construct(self):
|
|
line = NumberLine()
|
|
plane_words = TextMobject("2d space")
|
|
plane_words.next_to(self.j_hat, UP, buff = MED_BUFF)
|
|
plane_words.add_background_rectangle()
|
|
line_words = TextMobject("1d space (number line)")
|
|
line_words.next_to(line, UP)
|
|
|
|
|
|
self.play(Write(plane_words))
|
|
self.dither()
|
|
self.remove(plane_words)
|
|
mobjects = self.get_mobjects()
|
|
self.play(
|
|
*map(FadeOut, mobjects) + [ShowCreation(line)]
|
|
)
|
|
self.play(Write(line_words))
|
|
self.dither()
|
|
self.remove(line_words)
|
|
self.play(*map(FadeIn, mobjects))
|
|
self.apply_transposed_matrix(self.t_matrix)
|
|
self.play(Write(VMobject(*line.get_number_mobjects())))
|
|
self.dither()
|
|
self.show_matrix()
|
|
|
|
def show_matrix(self):
|
|
for vect, char in zip([self.i_hat, self.j_hat], ["i", "j"]):
|
|
vect.words = TextMobject(
|
|
"$\\hat\\%smath$ lands on"%char,
|
|
str(int(vect.get_end()[0]))
|
|
)
|
|
direction = UP if vect is self.i_hat else DOWN
|
|
vect.words.next_to(vect.get_end(), direction, buff = LARGE_BUFF)
|
|
vect.words.highlight(vect.get_color())
|
|
matrix = Matrix([[1, 2]])
|
|
matrix_words = TextMobject("Transformation matrix: ")
|
|
matrix_group = VMobject(matrix_words, matrix)
|
|
matrix_group.arrange_submobjects(buff = MED_BUFF)
|
|
matrix_group.to_edge(UP)
|
|
entries = matrix.get_entries()
|
|
|
|
self.play(Write(matrix_words), Write(matrix.get_brackets()))
|
|
for i, vect in enumerate([self.i_hat, self.j_hat]):
|
|
self.play(vect.rotate, np.pi/12, rate_func = wiggle)
|
|
self.play(Write(vect.words))
|
|
self.dither()
|
|
self.play(vect.words[1].copy().move_to, entries[i])
|
|
self.dither()
|
|
|
|
class TwoDTo1DTransformWithDots(TwoDTo1DTransform):
|
|
def construct(self):
|
|
line = NumberLine()
|
|
self.add(line, *self.get_mobjects())
|
|
offset = LEFT+DOWN
|
|
vect = 2*RIGHT+UP
|
|
dots = VMobject(*[
|
|
Dot(offset + a*vect, radius = 0.075)
|
|
for a in np.linspace(-2, 3, 18)
|
|
])
|
|
dots.submobject_gradient_highlight(YELLOW_B, YELLOW_C)
|
|
func = self.get_matrix_transformation(self.t_matrix)
|
|
new_dots = VMobject(*[
|
|
Dot(
|
|
func(dot.get_center()),
|
|
color = dot.get_color(),
|
|
radius = dot.radius
|
|
)
|
|
for dot in dots
|
|
])
|
|
words = TextMobject(
|
|
"Line of dots remains evenly spaced"
|
|
)
|
|
words.next_to(line, UP, buff = MED_BUFF)
|
|
|
|
self.play(Write(dots))
|
|
self.apply_transposed_matrix(
|
|
self.t_matrix,
|
|
added_anims = [Transform(dots, new_dots)]
|
|
)
|
|
self.play(Write(words))
|
|
self.dither()
|
|
|
|
class NextVideo(Scene):
|
|
def construct(self):
|
|
title = TextMobject("""
|
|
Next video: Dot products and duality
|
|
""")
|
|
title.scale_to_fit_width(2*SPACE_WIDTH - 2)
|
|
title.to_edge(UP)
|
|
rect = Rectangle(width = 16, height = 9, color = BLUE)
|
|
rect.scale_to_fit_height(6)
|
|
rect.next_to(title, DOWN)
|
|
|
|
self.add(title)
|
|
self.play(ShowCreation(rect))
|
|
self.dither()
|
|
|
|
class DotProductPreview(VectorScene):
|
|
CONFIG = {
|
|
"v_coords" : [3, 1],
|
|
"w_coords" : [2, -1],
|
|
"v_color" : YELLOW,
|
|
"w_color" : MAROON_C,
|
|
|
|
}
|
|
def construct(self):
|
|
self.lock_in_faded_grid()
|
|
self.add_symbols()
|
|
self.add_vectors()
|
|
self.grow_number_line()
|
|
self.project_w()
|
|
self.show_scaling()
|
|
|
|
|
|
def add_symbols(self):
|
|
v = matrix_to_mobject(self.v_coords).highlight(self.v_color)
|
|
w = matrix_to_mobject(self.w_coords).highlight(self.w_color)
|
|
v.add_background_rectangle()
|
|
w.add_background_rectangle()
|
|
dot = TexMobject("\\cdot")
|
|
eq = VMobject(v, dot, w)
|
|
eq.arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
|
eq.to_corner(UP+LEFT)
|
|
self.play(Write(eq), run_time = 1)
|
|
|
|
def add_vectors(self):
|
|
self.v = Vector(self.v_coords, color = self.v_color)
|
|
self.w = Vector(self.w_coords, color = self.w_color)
|
|
self.play(ShowCreation(self.v))
|
|
self.play(ShowCreation(self.w))
|
|
|
|
def grow_number_line(self):
|
|
line = NumberLine(stroke_width = 2).add_numbers()
|
|
line.rotate(self.v.get_angle())
|
|
self.play(Write(line), Animation(self.v))
|
|
self.play(
|
|
line.highlight, self.v.get_color(),
|
|
Animation(self.v),
|
|
rate_func = there_and_back
|
|
)
|
|
self.dither()
|
|
|
|
def project_w(self):
|
|
dot_product = np.dot(self.v.get_end(), self.w.get_end())
|
|
v_norm, w_norm = [
|
|
np.linalg.norm(vect.get_end())
|
|
for vect in self.v, self.w
|
|
]
|
|
projected_w = Vector(
|
|
self.v.get_end()*dot_product/(v_norm**2),
|
|
color = self.w.get_color()
|
|
)
|
|
projection_line = Line(
|
|
self.w.get_end(), projected_w.get_end(),
|
|
color = GREY
|
|
)
|
|
|
|
self.play(ShowCreation(projection_line))
|
|
self.add(self.w.copy().fade())
|
|
self.play(Transform(self.w, projected_w))
|
|
self.dither()
|
|
|
|
def show_scaling(self):
|
|
dot_product = np.dot(self.v.get_end(), self.w.get_end())
|
|
start_brace, interim_brace, final_brace = braces = [
|
|
Brace(
|
|
Line(ORIGIN, norm*RIGHT),
|
|
UP
|
|
)
|
|
for norm in 1, self.v.get_length(), dot_product
|
|
]
|
|
length_texs = list(it.starmap(TexMobject, [
|
|
("1",),
|
|
("\\text{Scale by }", "||\\vec{\\textbf{v}}||",),
|
|
("\\text{Length of}", "\\text{ scaled projection}",),
|
|
]))
|
|
length_texs[1][1].highlight(self.v_color)
|
|
length_texs[2][1].highlight(self.w_color)
|
|
for brace, tex_mob in zip(braces, length_texs):
|
|
tex_mob.add_background_rectangle()
|
|
brace.put_at_tip(tex_mob, buff = SMALL_BUFF)
|
|
brace.add(tex_mob)
|
|
brace.rotate(self.v.get_angle())
|
|
new_w = self.w.copy().scale(self.v.get_length())
|
|
|
|
self.play(Write(start_brace))
|
|
self.dither()
|
|
self.play(
|
|
Transform(start_brace, interim_brace),
|
|
Transform(self.w, new_w)
|
|
)
|
|
self.dither()
|
|
self.play(
|
|
Transform(start_brace, final_brace)
|
|
)
|
|
self.dither()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|