mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 22:13:30 +08:00
A few bug fixes to the very buggy SVGMobject
This commit is contained in:
464
borsuk.py
464
borsuk.py
@ -22,6 +22,7 @@ from camera import Camera, ShadingCamera
|
|||||||
from mobject.svg_mobject import *
|
from mobject.svg_mobject import *
|
||||||
from mobject.tex_mobject import *
|
from mobject.tex_mobject import *
|
||||||
|
|
||||||
|
from eoc.chapter1 import PatreonThanks
|
||||||
from eoc.graph_scene import GraphScene
|
from eoc.graph_scene import GraphScene
|
||||||
|
|
||||||
class Jewel(VMobject):
|
class Jewel(VMobject):
|
||||||
@ -89,6 +90,10 @@ class Necklace(VMobject):
|
|||||||
|
|
||||||
################
|
################
|
||||||
|
|
||||||
|
class ThisIsGoingToBeGood(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
|
||||||
class CheckOutMathologer(PiCreatureScene):
|
class CheckOutMathologer(PiCreatureScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"logo_height" : 1.5,
|
"logo_height" : 1.5,
|
||||||
@ -98,12 +103,7 @@ class CheckOutMathologer(PiCreatureScene):
|
|||||||
"logo_color" : None,
|
"logo_color" : None,
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
logo = ImageMobject(self.logo_file)
|
logo = self.get_logo()
|
||||||
logo.scale_to_fit_height(self.logo_height)
|
|
||||||
logo.to_corner(UP+LEFT)
|
|
||||||
if self.logo_color is not None:
|
|
||||||
logo.highlight(self.logo_color)
|
|
||||||
logo.stroke_width = 1
|
|
||||||
name = TextMobject(self.channel_name)
|
name = TextMobject(self.channel_name)
|
||||||
name.next_to(logo, RIGHT)
|
name.next_to(logo, RIGHT)
|
||||||
|
|
||||||
@ -112,11 +112,8 @@ class CheckOutMathologer(PiCreatureScene):
|
|||||||
rect.next_to(logo, DOWN)
|
rect.next_to(logo, DOWN)
|
||||||
rect.to_edge(LEFT)
|
rect.to_edge(LEFT)
|
||||||
|
|
||||||
logo.save_state()
|
|
||||||
logo.shift(DOWN)
|
|
||||||
logo.highlight(BLACK)
|
|
||||||
self.play(
|
self.play(
|
||||||
logo.restore,
|
self.get_logo_intro_animation(logo),
|
||||||
self.pi_creature.change_mode, "hooray",
|
self.pi_creature.change_mode, "hooray",
|
||||||
)
|
)
|
||||||
self.play(
|
self.play(
|
||||||
@ -127,6 +124,21 @@ class CheckOutMathologer(PiCreatureScene):
|
|||||||
self.change_mode("happy")
|
self.change_mode("happy")
|
||||||
self.dither(2)
|
self.dither(2)
|
||||||
|
|
||||||
|
def get_logo(self):
|
||||||
|
logo = ImageMobject(self.logo_file)
|
||||||
|
logo.scale_to_fit_height(self.logo_height)
|
||||||
|
logo.to_corner(UP+LEFT)
|
||||||
|
if self.logo_color is not None:
|
||||||
|
logo.highlight(self.logo_color)
|
||||||
|
logo.stroke_width = 1
|
||||||
|
return logo
|
||||||
|
|
||||||
|
def get_logo_intro_animation(self, logo):
|
||||||
|
logo.save_state()
|
||||||
|
logo.shift(DOWN)
|
||||||
|
logo.highlight(BLACK)
|
||||||
|
return ApplyMethod(logo.restore)
|
||||||
|
|
||||||
class IntroduceStolenNecklaceProblem(Scene):
|
class IntroduceStolenNecklaceProblem(Scene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera_class" : ShadingCamera,
|
"camera_class" : ShadingCamera,
|
||||||
@ -688,6 +700,20 @@ class PointOutVSauce(CheckOutMathologer):
|
|||||||
"logo_height" : 1,
|
"logo_height" : 1,
|
||||||
"logo_color" : GREY,
|
"logo_color" : GREY,
|
||||||
}
|
}
|
||||||
|
def get_logo(self):
|
||||||
|
logo = SVGMobject(file_name = self.logo_file)
|
||||||
|
logo.scale_to_fit_height(self.logo_height)
|
||||||
|
logo.to_corner(UP+LEFT)
|
||||||
|
logo.set_stroke(width = 0)
|
||||||
|
logo.set_fill(GREEN)
|
||||||
|
logo.sort_submobjects()
|
||||||
|
return logo
|
||||||
|
|
||||||
|
def get_logo_intro_animation(self, logo):
|
||||||
|
return DrawBorderThenFill(
|
||||||
|
logo,
|
||||||
|
run_time = 2,
|
||||||
|
)
|
||||||
|
|
||||||
class WalkEquatorPostTransform(GraphScene):
|
class WalkEquatorPostTransform(GraphScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
@ -1097,6 +1123,7 @@ class GeneralizeBorsukUlam(Scene):
|
|||||||
arrow.next_to(sphere_set, RIGHT)
|
arrow.next_to(sphere_set, RIGHT)
|
||||||
f.next_to(arrow, UP)
|
f.next_to(arrow, UP)
|
||||||
output_space.next_to(arrow, RIGHT)
|
output_space.next_to(arrow, RIGHT)
|
||||||
|
equation.next_to(sphere_set, DOWN, buff = LARGE_BUFF)
|
||||||
equation.to_edge(RIGHT)
|
equation.to_edge(RIGHT)
|
||||||
lhs = VGroup(*equation[:2])
|
lhs = VGroup(*equation[:2])
|
||||||
eq = equation[2]
|
eq = equation[2]
|
||||||
@ -1196,11 +1223,6 @@ class GeneralizeBorsukUlam(Scene):
|
|||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
# class FourDBorsukUlam(GeneralizeBorsukUlam):
|
|
||||||
# CONFIG = {
|
|
||||||
# "n_dims" : 4,
|
|
||||||
# }
|
|
||||||
|
|
||||||
# class FiveDBorsukUlam(GeneralizeBorsukUlam):
|
# class FiveDBorsukUlam(GeneralizeBorsukUlam):
|
||||||
# CONFIG = {
|
# CONFIG = {
|
||||||
# "n_dims" : 5,
|
# "n_dims" : 5,
|
||||||
@ -1714,8 +1736,9 @@ class ChoicesInNecklaceCutting(Scene):
|
|||||||
|
|
||||||
######
|
######
|
||||||
|
|
||||||
def get_groups(self):
|
def get_groups(self, indices = None):
|
||||||
segments, tick_marks = self.necklace
|
segments, tick_marks = self.necklace
|
||||||
|
if indices is None:
|
||||||
n_segments = len(segments)
|
n_segments = len(segments)
|
||||||
indices = [0, n_segments/6, n_segments/2, n_segments]
|
indices = [0, n_segments/6, n_segments/2, n_segments]
|
||||||
|
|
||||||
@ -1727,8 +1750,8 @@ class ChoicesInNecklaceCutting(Scene):
|
|||||||
for i1, i2 in zip(indices, indices[1:])
|
for i1, i2 in zip(indices, indices[1:])
|
||||||
]
|
]
|
||||||
for group, index in zip(groups, indices[1:]):
|
for group, index in zip(groups, indices[1:]):
|
||||||
group.add(tick_marks[index].copy())
|
group[1].add(tick_marks[index].copy())
|
||||||
groups[-1][-1].add(tick_marks[-1])
|
groups[-1][1].add(tick_marks[-1])
|
||||||
|
|
||||||
for group in groups:
|
for group in groups:
|
||||||
group.target_points = [
|
group.target_points = [
|
||||||
@ -1800,6 +1823,9 @@ class CompareThisToSphereChoice(TeacherStudentsScene):
|
|||||||
)
|
)
|
||||||
self.dither(3)
|
self.dither(3)
|
||||||
|
|
||||||
|
class SimpleRotatingSphereWithPoint(ExternallyAnimatedScene):
|
||||||
|
pass
|
||||||
|
|
||||||
class ChoicesForSpherePoint(GeneralizeBorsukUlam):
|
class ChoicesForSpherePoint(GeneralizeBorsukUlam):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.add_sphere_set()
|
self.add_sphere_set()
|
||||||
@ -1828,6 +1854,7 @@ class ChoicesForSpherePoint(GeneralizeBorsukUlam):
|
|||||||
sphere_set.shift(UP)
|
sphere_set.shift(UP)
|
||||||
|
|
||||||
self.add(sphere_set)
|
self.add(sphere_set)
|
||||||
|
self.sphere_set = sphere_set
|
||||||
|
|
||||||
def initialize_words(self):
|
def initialize_words(self):
|
||||||
choice_one_words = TextMobject(
|
choice_one_words = TextMobject(
|
||||||
@ -2047,18 +2074,399 @@ class NecklaceDivisionSphereAssociation(ChoicesInNecklaceCutting):
|
|||||||
self.play(*self.swapping_anims)
|
self.play(*self.swapping_anims)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
class SimpleRotatingSphereWithAntipodes(ExternallyAnimatedScene):
|
||||||
|
pass
|
||||||
|
|
||||||
class TotalLengthOfEachJewelEquals(NecklaceDivisionSphereAssociation):
|
class TotalLengthOfEachJewelEquals(NecklaceDivisionSphereAssociation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"random_seed" : 2,
|
"camera_class" : ShadingCamera,
|
||||||
"hard_coded_fair_division_indices" : [],
|
"random_seed" : 1,
|
||||||
|
"thief_box_offset" : 1.2,
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
random.seed(self.random_seed)
|
random.seed(self.random_seed)
|
||||||
self.add_necklace()
|
self.add_necklace()
|
||||||
self.add_boxes_and_labels()
|
self.add_boxes_and_labels()
|
||||||
self.find_fair_division()
|
self.find_fair_division()
|
||||||
|
self.demonstrate_fair_division()
|
||||||
|
self.perform_antipodal_swap()
|
||||||
|
|
||||||
def find_fair_division(self):
|
def find_fair_division(self):
|
||||||
|
segments, tick_marks = self.necklace
|
||||||
|
segments.sort_submobjects()
|
||||||
|
segment_colors = [
|
||||||
|
segment.get_color()
|
||||||
|
for segment in segments
|
||||||
|
]
|
||||||
|
indices = self.get_fair_division_indices(segment_colors)
|
||||||
|
groups = self.get_groups(
|
||||||
|
[0] + list(np.array(indices)+1) + [len(segments)]
|
||||||
|
)
|
||||||
|
self.add(*groups)
|
||||||
|
binary_choice = [0, 1, 0]
|
||||||
|
|
||||||
|
v_lines = VGroup(*[DashedLine(UP, DOWN) for x in range(2)])
|
||||||
|
v_lines.move_to(self.necklace)
|
||||||
|
self.play(ShowCreation(v_lines))
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(line.move_to, segments[index].get_right())
|
||||||
|
for line, index in zip(v_lines, indices)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(group.move_to, group.target_points[choice])
|
||||||
|
for group, choice in zip(groups, binary_choice)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
self.groups = groups
|
||||||
|
self.v_lines = v_lines
|
||||||
|
|
||||||
|
def get_fair_division_indices(self, colors):
|
||||||
|
colors = np.array(list(colors))
|
||||||
|
color_types = map(Color, set([c.get_hex_l() for c in colors]))
|
||||||
|
type_to_count = dict([
|
||||||
|
(color, sum(colors == color))
|
||||||
|
for color in color_types
|
||||||
|
])
|
||||||
|
for i1, i2 in it.combinations(range(1, len(colors)-1), 2):
|
||||||
|
bools = [
|
||||||
|
sum(colors[i1:i2] == color) == type_to_count[color]/2
|
||||||
|
for color in color_types
|
||||||
|
]
|
||||||
|
if np.all(bools):
|
||||||
|
return i1, i2
|
||||||
|
raise Exception("No fair division found")
|
||||||
|
|
||||||
|
def demonstrate_fair_division(self):
|
||||||
|
segments, tick_marks = self.necklace
|
||||||
|
color_types = map(Color, set([
|
||||||
|
segment.get_color().get_hex_l()
|
||||||
|
for segment in segments
|
||||||
|
]))
|
||||||
|
top_segments = VGroup(*it.chain(
|
||||||
|
self.groups[0][0],
|
||||||
|
self.groups[2][0],
|
||||||
|
))
|
||||||
|
bottom_segments = self.groups[1][0]
|
||||||
|
for color in color_types:
|
||||||
|
monochrome_groups = [
|
||||||
|
VGroup(*filter(
|
||||||
|
lambda segment: segment.get_color() == color,
|
||||||
|
segment_group
|
||||||
|
))
|
||||||
|
for segment_group in top_segments, bottom_segments
|
||||||
|
]
|
||||||
|
labels = VGroup()
|
||||||
|
for i, group in enumerate(monochrome_groups):
|
||||||
|
group.save_state()
|
||||||
|
group.generate_target()
|
||||||
|
group.target.arrange_submobjects(buff = SMALL_BUFF)
|
||||||
|
brace = Brace(group.target, UP)
|
||||||
|
label = VGroup(
|
||||||
|
TextMobject("Thief %d"%(i+1)),
|
||||||
|
Jewel(color = group[0].get_color())
|
||||||
|
)
|
||||||
|
label.arrange_submobjects()
|
||||||
|
label.next_to(brace, UP)
|
||||||
|
full_group = VGroup(group.target, brace, label)
|
||||||
|
vect = LEFT if i == 0 else RIGHT
|
||||||
|
full_group.next_to(ORIGIN, vect, buff = MED_LARGE_BUFF)
|
||||||
|
full_group.to_edge(UP)
|
||||||
|
labels.add(brace, label)
|
||||||
|
equals = TexMobject("=")
|
||||||
|
equals.next_to(monochrome_groups[0].target, RIGHT)
|
||||||
|
|
||||||
|
for group, label in zip(monochrome_groups, labels):
|
||||||
|
self.play(
|
||||||
|
MoveToTarget(group),
|
||||||
|
FadeIn(labels)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(FadeIn(equals))
|
||||||
|
self.dither()
|
||||||
|
self.play(*it.chain(
|
||||||
|
[group.restore for group in monochrome_groups],
|
||||||
|
map(FadeOut, list(labels)+[equals]),
|
||||||
|
))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def perform_antipodal_swap(self):
|
||||||
|
binary_choices_list = [(1, 0, 1), (0, 1, 0)]
|
||||||
|
for binary_choices in binary_choices_list:
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(
|
||||||
|
group.move_to,
|
||||||
|
group.target_points[choice]
|
||||||
|
)
|
||||||
|
for group, choice in zip(self.groups, binary_choices)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class ExclaimBorsukUlam(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
self.student_says(
|
||||||
|
"Borsuk-Ulam!",
|
||||||
|
target_mode = "hooray"
|
||||||
|
)
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(pi.change_mode, "hooray")
|
||||||
|
for pi in self.get_everyone()
|
||||||
|
])
|
||||||
|
self.dither(3)
|
||||||
|
|
||||||
|
class ShowFunctionDiagram(TotalLengthOfEachJewelEquals):
|
||||||
|
CONFIG = {
|
||||||
|
"necklace_center" : ORIGIN,
|
||||||
|
"camera_class" : ShadingCamera,
|
||||||
|
"thief_box_offset" : 0.3,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.add_necklace()
|
||||||
|
self.add_number_pair()
|
||||||
|
self.add_sphere_arrow()
|
||||||
|
self.add_xy_plane()
|
||||||
|
|
||||||
|
def add_necklace(self):
|
||||||
|
random.seed(self.random_seed)
|
||||||
|
ChoicesInNecklaceCutting.add_necklace(self)
|
||||||
|
self.necklace.scale_to_fit_width(SPACE_WIDTH-1)
|
||||||
|
self.necklace.to_edge(UP, buff = LARGE_BUFF)
|
||||||
|
self.necklace.to_edge(LEFT, buff = SMALL_BUFF)
|
||||||
|
self.add(self.necklace)
|
||||||
|
|
||||||
|
self.find_fair_division()
|
||||||
|
|
||||||
|
def add_number_pair(self):
|
||||||
|
colors = [BLUE, GREEN]
|
||||||
|
pair, alt_pair = [
|
||||||
|
TextMobject(
|
||||||
|
"(Thief %d"%d, "X", ", Thief %d "%d, "X", ")"
|
||||||
|
)
|
||||||
|
for d in 1, 2
|
||||||
|
]
|
||||||
|
for tup in pair, alt_pair:
|
||||||
|
jewels = [Jewel(color = color) for color in colors]
|
||||||
|
for i, jewel in zip([1, 3], jewels):
|
||||||
|
jewel.replace(tup[i])
|
||||||
|
tup.submobjects[i] = jewel
|
||||||
|
|
||||||
|
tup.scale_to_fit_width(SPACE_WIDTH-2)
|
||||||
|
tup.next_to(self.necklace, buff = 2*LARGE_BUFF)
|
||||||
|
|
||||||
|
# arrow = Arrow(self.necklace, pair, color = WHITE)
|
||||||
|
arrow = TexMobject("\\rightarrow")
|
||||||
|
arrow.scale(1.5)
|
||||||
|
arrow.move_to(
|
||||||
|
Line(self.necklace.get_right(), pair.get_left())
|
||||||
|
)
|
||||||
|
arrow.highlight(YELLOW)
|
||||||
|
|
||||||
|
self.play(Write(arrow))
|
||||||
|
self.play(Write(pair))
|
||||||
|
self.dither()
|
||||||
|
pair.save_state()
|
||||||
|
self.play(Transform(pair, alt_pair, path_arc = np.pi))
|
||||||
|
self.dither(2)
|
||||||
|
self.play(ApplyMethod(pair.restore, path_arc = np.pi))
|
||||||
|
self.dither()
|
||||||
|
for choices in [(1, 0, 0), (0, 1, 0)]:
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(group.move_to, group.target_points[i])
|
||||||
|
for group, i in zip(self.groups, choices)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
self.num_pair = pair
|
||||||
|
|
||||||
|
def add_sphere_arrow(self):
|
||||||
|
arrow = TexMobject("\\updownarrow")
|
||||||
|
arrow.scale(1.5)
|
||||||
|
arrow.highlight(YELLOW)
|
||||||
|
arrow.next_to(self.necklace, DOWN, buff = LARGE_BUFF)
|
||||||
|
|
||||||
|
self.play(Write(arrow))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def add_xy_plane(self):
|
||||||
|
arrow = TexMobject("\\updownarrow")
|
||||||
|
arrow.scale(1.5)
|
||||||
|
arrow.highlight(YELLOW)
|
||||||
|
arrow.next_to(self.num_pair, DOWN, buff = 1.2*LARGE_BUFF)
|
||||||
|
|
||||||
|
xy_plane = NumberPlane()
|
||||||
|
xy_plane.scale_to_fit_width(SPACE_WIDTH-1)
|
||||||
|
xy_plane.next_to(arrow, DOWN, buff = LARGE_BUFF)
|
||||||
|
|
||||||
|
curved_arrow = Arc(
|
||||||
|
start_angle = 3*np.pi/4,
|
||||||
|
angle = -np.pi/2,
|
||||||
|
radius = 3,
|
||||||
|
color = YELLOW,
|
||||||
|
)
|
||||||
|
curved_arrow.add_tip()
|
||||||
|
curved_arrow.shift(2*DOWN)
|
||||||
|
|
||||||
|
self.play(Write(arrow))
|
||||||
|
self.play(ShowCreation(xy_plane))
|
||||||
|
self.dither()
|
||||||
|
self.play(ShowCreation(curved_arrow))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class WhatAboutGeneralCase(TeacherStudentsScene):
|
||||||
|
def construct(self):
|
||||||
|
self.student_says("""
|
||||||
|
What about when
|
||||||
|
there's more than 2 jewels?
|
||||||
|
""")
|
||||||
|
self.change_student_modes("confused", None, "sassy")
|
||||||
|
self.dither()
|
||||||
|
self.play(self.get_teacher().change_mode, "thinking")
|
||||||
|
self.dither()
|
||||||
|
self.teacher_says(
|
||||||
|
"""Use Borsuk-Ulam for
|
||||||
|
higher-dimensional spheres """,
|
||||||
|
target_mode = "hooray"
|
||||||
|
)
|
||||||
|
self.change_student_modes(*["confused"]*3)
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
|
class Simple3DSpace(ExternallyAnimatedScene):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class FourDBorsukUlam(GeneralizeBorsukUlam):
|
||||||
|
CONFIG = {
|
||||||
|
"n_dims" : 4,
|
||||||
|
}
|
||||||
|
def get_sphere_set(self):
|
||||||
|
sphere_set = GeneralizeBorsukUlam.get_sphere_set(self)
|
||||||
|
brace = Brace(sphere_set)
|
||||||
|
text = brace.get_text("Hypersphere in 4D")
|
||||||
|
sphere_set.add(brace, text)
|
||||||
|
return sphere_set
|
||||||
|
|
||||||
|
class CircleToSphereToQMarks(Scene):
|
||||||
|
def construct(self):
|
||||||
|
pis = VGroup()
|
||||||
|
modes = ["happy", "pondering", "pleading"]
|
||||||
|
shapes = [
|
||||||
|
Circle(color = BLUE, radius = 0.5),
|
||||||
|
VectorizedPoint(),
|
||||||
|
TexMobject("???")
|
||||||
|
]
|
||||||
|
for d, mode, shape in zip(it.count(2), modes, shapes):
|
||||||
|
randy = Randolph(mode = mode)
|
||||||
|
randy.scale(0.7)
|
||||||
|
bubble = randy.get_bubble(direction = LEFT)
|
||||||
|
bubble.resize_to_content()
|
||||||
|
bubble.pin_to(randy)
|
||||||
|
bubble.position_mobject_inside(shape)
|
||||||
|
title = TextMobject("%dD"%d)
|
||||||
|
randy.add(bubble, shape)
|
||||||
|
title.next_to(randy, UP)
|
||||||
|
randy.add(title)
|
||||||
|
pis.add(randy)
|
||||||
|
|
||||||
|
|
||||||
|
progression = VGroup(
|
||||||
|
pis[0],
|
||||||
|
Arrow(LEFT, RIGHT),
|
||||||
|
pis[1],
|
||||||
|
Arrow(LEFT, RIGHT),
|
||||||
|
pis[2],
|
||||||
|
)
|
||||||
|
progression.arrange_submobjects()
|
||||||
|
for mob in progression:
|
||||||
|
self.play(FadeIn(mob))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class BorsukPatreonThanks(PatreonThanks):
|
||||||
|
CONFIG = {
|
||||||
|
"specific_patrons" : [
|
||||||
|
"Ali Yahya",
|
||||||
|
"Meshal Alshammari",
|
||||||
|
"CrypticSwarm ",
|
||||||
|
"Ankit Agarwal",
|
||||||
|
"Yu Jun",
|
||||||
|
"Shelby Doolittle",
|
||||||
|
"Dave Nicponski",
|
||||||
|
"Damion Kistler",
|
||||||
|
"Juan Benet",
|
||||||
|
"Othman Alikhan",
|
||||||
|
"Markus Persson",
|
||||||
|
"Dan Buchoff",
|
||||||
|
"Derek Dai",
|
||||||
|
"Joseph John Cox",
|
||||||
|
"Luc Ritchie",
|
||||||
|
"Guido Gambardella",
|
||||||
|
"Jerry Ling",
|
||||||
|
"Mark Govea",
|
||||||
|
"Vecht",
|
||||||
|
"Jonathan Eppele",
|
||||||
|
"Shimin Kuang",
|
||||||
|
"Rish Kundalia",
|
||||||
|
"Achille Brighton",
|
||||||
|
"Kirk Werklund",
|
||||||
|
"Ripta Pasay",
|
||||||
|
"Felipe Diniz",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
class MortyLookingAtRectangle(Scene):
|
||||||
|
def construct(self):
|
||||||
|
morty = Mortimer()
|
||||||
|
morty.to_corner(DOWN+RIGHT)
|
||||||
|
url = TextMobject("www.thegreatcoursesplus.com/3blue1brown")
|
||||||
|
url.scale(0.75)
|
||||||
|
url.to_corner(UP+LEFT)
|
||||||
|
rect = Rectangle(height = 9, width = 16)
|
||||||
|
rect.scale_to_fit_height(5)
|
||||||
|
rect.next_to(url, DOWN)
|
||||||
|
rect.shift_onto_screen()
|
||||||
|
url.save_state()
|
||||||
|
url.next_to(morty.get_corner(UP+LEFT), UP)
|
||||||
|
url.shift_onto_screen()
|
||||||
|
|
||||||
|
self.add(morty)
|
||||||
|
self.play(
|
||||||
|
morty.change_mode, "raise_right_hand",
|
||||||
|
morty.look_at, url,
|
||||||
|
)
|
||||||
|
self.play(Write(url))
|
||||||
|
self.play(Blink(morty))
|
||||||
|
self.dither()
|
||||||
|
self.play(
|
||||||
|
url.restore,
|
||||||
|
morty.change_mode, "happy"
|
||||||
|
)
|
||||||
|
self.play(ShowCreation(rect))
|
||||||
|
self.dither()
|
||||||
|
self.play(Blink(morty))
|
||||||
|
for mode in ["pondering", "hooray", "happy", "pondering", "happy"]:
|
||||||
|
self.play(morty.change_mode, mode)
|
||||||
|
self.dither(2)
|
||||||
|
self.play(Blink(morty))
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
|
class RotatingThreeDSphereProjection(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"camera_class" : ShadingCamera,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
sphere = VGroup(*[
|
||||||
|
Circle(radius = np.sin(t)).shift(np.cos(t)*OUT)
|
||||||
|
for t in np.linspace(0, np.pi, 20)
|
||||||
|
])
|
||||||
|
sphere.set_stroke(BLUE, width = 2)
|
||||||
|
# sphere.set_fill(BLUE, opacity = 0.1)
|
||||||
|
|
||||||
|
self.play(Rotating(
|
||||||
|
sphere, axis = RIGHT+OUT,
|
||||||
|
run_time = 10
|
||||||
|
))
|
||||||
|
self.repeat_frames(4)
|
||||||
|
|
||||||
|
class FourDSphereProjectTo4D(ExternallyAnimatedScene):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -2073,22 +2481,6 @@ class TotalLengthOfEachJewelEquals(NecklaceDivisionSphereAssociation):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2596,6 +2596,10 @@ class PatreonThanks(Scene):
|
|||||||
special_thanks.highlight(YELLOW)
|
special_thanks.highlight(YELLOW)
|
||||||
special_thanks.to_edge(UP)
|
special_thanks.to_edge(UP)
|
||||||
|
|
||||||
|
patreon_logo = PatreonLogo()
|
||||||
|
# patreon_logo.scale_to_fit_width(morty.get_width())
|
||||||
|
patreon_logo.next_to(morty, UP, buff = MED_LARGE_BUFF)
|
||||||
|
|
||||||
left_patrons = VGroup(*map(TextMobject,
|
left_patrons = VGroup(*map(TextMobject,
|
||||||
self.specific_patrons[:n_patrons/2]
|
self.specific_patrons[:n_patrons/2]
|
||||||
))
|
))
|
||||||
@ -2623,7 +2627,10 @@ class PatreonThanks(Scene):
|
|||||||
# rate_func = None
|
# rate_func = None
|
||||||
# )
|
# )
|
||||||
|
|
||||||
self.play(morty.change_mode, "gracious")
|
self.play(
|
||||||
|
morty.change_mode, "gracious",
|
||||||
|
DrawBorderThenFill(patreon_logo)
|
||||||
|
)
|
||||||
self.play(Write(special_thanks, run_time = 1))
|
self.play(Write(special_thanks, run_time = 1))
|
||||||
self.play(
|
self.play(
|
||||||
Write(left_patrons),
|
Write(left_patrons),
|
||||||
|
@ -560,6 +560,15 @@ class Mobject(object):
|
|||||||
self.center()
|
self.center()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def sort_submobjects(self, point_to_num_func = lambda p : p[0]):
|
||||||
|
self.submobjects.sort(
|
||||||
|
lambda *mobs : cmp(*[
|
||||||
|
point_to_num_func(mob.get_center())
|
||||||
|
for mob in mobs
|
||||||
|
])
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
## Alignment
|
## Alignment
|
||||||
def align_data(self, mobject):
|
def align_data(self, mobject):
|
||||||
self.align_submobjects(mobject)
|
self.align_submobjects(mobject)
|
||||||
|
@ -189,7 +189,7 @@ class VMobjectFromSVGPathstring(VMobject):
|
|||||||
#list. This variable may get modified in the conditionals below.
|
#list. This variable may get modified in the conditionals below.
|
||||||
points = self.growing_path.points
|
points = self.growing_path.points
|
||||||
new_points = self.string_to_points(coord_string)
|
new_points = self.string_to_points(coord_string)
|
||||||
if isLower:
|
if isLower and len(points) > 0:
|
||||||
new_points += points[-1]
|
new_points += points[-1]
|
||||||
if command == "M": #moveto
|
if command == "M": #moveto
|
||||||
if len(points) > 0:
|
if len(points) > 0:
|
||||||
@ -201,9 +201,12 @@ class VMobjectFromSVGPathstring(VMobject):
|
|||||||
if command == "H":
|
if command == "H":
|
||||||
new_points[0,1] = points[-1,1]
|
new_points[0,1] = points[-1,1]
|
||||||
elif command == "V":
|
elif command == "V":
|
||||||
|
if isLower:
|
||||||
|
new_points[0,0] -= points[-1,0]
|
||||||
|
new_points[0,0] += points[-1,1]
|
||||||
new_points[0,1] = new_points[0,0]
|
new_points[0,1] = new_points[0,0]
|
||||||
new_points[0,0] = points[-1,0]
|
new_points[0,0] = points[-1,0]
|
||||||
new_points = new_points[[0, 0, 0]]
|
new_points = new_points.repeat(3, axis = 0)
|
||||||
elif command == "C": #curveto
|
elif command == "C": #curveto
|
||||||
pass #Yay! No action required
|
pass #Yay! No action required
|
||||||
elif command in ["S", "T"]: #smooth curveto
|
elif command in ["S", "T"]: #smooth curveto
|
||||||
@ -219,6 +222,13 @@ class VMobjectFromSVGPathstring(VMobject):
|
|||||||
#Both handles and new anchor are the start
|
#Both handles and new anchor are the start
|
||||||
new_points = points[[0, 0, 0]]
|
new_points = points[[0, 0, 0]]
|
||||||
# self.mark_paths_closed = True
|
# self.mark_paths_closed = True
|
||||||
|
|
||||||
|
#Handle situations where there's multiple relative control points
|
||||||
|
if isLower and len(points) > 3:
|
||||||
|
for i in range(3, len(new_points), 3):
|
||||||
|
new_points[i:i+3] -= points[-1]
|
||||||
|
new_points[i:i+3] += new_points[i-1]
|
||||||
|
|
||||||
self.growing_path.add_control_points(new_points)
|
self.growing_path.add_control_points(new_points)
|
||||||
|
|
||||||
def string_to_points(self, coord_string):
|
def string_to_points(self, coord_string):
|
||||||
|
@ -11,6 +11,26 @@ from animation.simple_animations import Rotating
|
|||||||
from topics.geometry import Circle, Line
|
from topics.geometry import Circle, Line
|
||||||
|
|
||||||
|
|
||||||
|
class PatreonLogo(SVGMobject):
|
||||||
|
CONFIG = {
|
||||||
|
"file_name" : "patreon_logo",
|
||||||
|
"fill_color" : "#ff5900",
|
||||||
|
"fill_opacity" : 1,
|
||||||
|
"stroke_width" : 0,
|
||||||
|
"height" : 2,
|
||||||
|
"propogate_style_to_family" : True
|
||||||
|
}
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
SVGMobject.__init__(self, **kwargs)
|
||||||
|
outer, inner = self.split()
|
||||||
|
# outer.add_subpath(inner.points)
|
||||||
|
# self.remove(inner)
|
||||||
|
inner.set_fill(BLACK, opacity = 1)
|
||||||
|
inner.set_stroke(self.fill_color, width = 0.5)
|
||||||
|
self.scale_to_fit_height(self.height)
|
||||||
|
self.center()
|
||||||
|
|
||||||
|
|
||||||
class VideoIcon(SVGMobject):
|
class VideoIcon(SVGMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"file_name" : "video_icon",
|
"file_name" : "video_icon",
|
||||||
|
Reference in New Issue
Block a user