mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +08:00
Finished with ECF Project
This commit is contained in:
@ -132,6 +132,8 @@ class ComplexHomotopy(Homotopy):
|
|||||||
to_cammel_case(complex_homotopy.__name__)
|
to_cammel_case(complex_homotopy.__name__)
|
||||||
|
|
||||||
|
|
||||||
|
####### Pi Creature Stuff #############
|
||||||
|
|
||||||
class WalkPiCreature(Animation):
|
class WalkPiCreature(Animation):
|
||||||
def __init__(self, pi_creature, destination, *args, **kwargs):
|
def __init__(self, pi_creature, destination, *args, **kwargs):
|
||||||
self.final = deepcopy(pi_creature).move_to(destination)
|
self.final = deepcopy(pi_creature).move_to(destination)
|
||||||
@ -155,13 +157,33 @@ class WalkPiCreature(Animation):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BlinkPiCreature(Transform):
|
||||||
|
def __init__(self, pi_creature, run_time = 0.2, *args, **kwargs):
|
||||||
|
blinked = deepcopy(pi_creature).blink()
|
||||||
|
Transform.__init__(
|
||||||
|
self, pi_creature, blinked,
|
||||||
|
alpha_func = there_and_back,
|
||||||
|
run_time = run_time,
|
||||||
|
*args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class WaveArm(Transform):
|
||||||
|
def __init__(self, pi_creature, *args, **kwargs):
|
||||||
|
final = deepcopy(pi_creature)
|
||||||
|
body_to_arm = pi_creature.arm.get_center()-pi_creature.get_center()
|
||||||
|
if body_to_arm[0] < 0:
|
||||||
|
wag_direction = LEFT
|
||||||
|
else:
|
||||||
|
wag_direction = RIGHT
|
||||||
|
final.arm.wag(0.7*UP, wag_direction, 2.0)
|
||||||
|
final.rewire_part_attributes(self_from_parts = True)
|
||||||
|
Transform.__init__(
|
||||||
|
self, pi_creature, final,
|
||||||
|
alpha_func = there_and_back,
|
||||||
|
*args, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
###### Something different ###############
|
|
||||||
def pi_creature_step(scene, pi_creature, destination):
|
|
||||||
final = deepcopy(pi_creature).move_to(destination)
|
|
||||||
intermediate = pi_creature.get_step_intermediate(final)
|
|
||||||
scene.animate(Transform(pi_creature, intermediate))
|
|
||||||
scene.animate(Transform(pi_creature, final))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -104,14 +104,28 @@ class PiCreature(Mobject):
|
|||||||
vect = pi_creature.get_center() - self.get_center()
|
vect = pi_creature.get_center() - self.get_center()
|
||||||
result = deepcopy(self).shift(vect / 2.0)
|
result = deepcopy(self).shift(vect / 2.0)
|
||||||
result.rewire_part_attributes()
|
result.rewire_part_attributes()
|
||||||
if vect[0] < 0:
|
left_forward = vect[0] > 0
|
||||||
result.right_leg.wag(-vect/2.0, DOWN)
|
if self.right_leg.get_center()[0] < self.left_leg.get_center()[0]:
|
||||||
|
#For Mortimer's case
|
||||||
|
left_forward = not left_forward
|
||||||
|
if left_forward:
|
||||||
result.left_leg.wag(vect/2.0, DOWN)
|
result.left_leg.wag(vect/2.0, DOWN)
|
||||||
|
result.right_leg.wag(-vect/2.0, DOWN)
|
||||||
else:
|
else:
|
||||||
result.left_leg.wag(-vect/2.0, DOWN)
|
|
||||||
result.right_leg.wag(vect/2.0, DOWN)
|
result.right_leg.wag(vect/2.0, DOWN)
|
||||||
|
result.left_leg.wag(-vect/2.0, DOWN)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def blink(self):
|
||||||
|
for eye in self.left_eye, self.right_eye:
|
||||||
|
bottom = min(eye.points[:,1])
|
||||||
|
eye.apply_function(
|
||||||
|
lambda (x, y, z) : (x, bottom, z)
|
||||||
|
)
|
||||||
|
self.rewire_part_attributes(self_from_parts = True)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Randolph(PiCreature):
|
class Randolph(PiCreature):
|
||||||
pass #Nothing more than an alternative name
|
pass #Nothing more than an alternative name
|
||||||
|
@ -67,24 +67,54 @@ class ImageMobject(Mobject2D):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
class SpeechBubble(ImageMobject):
|
class SpeechBubble(ImageMobject):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, direction = LEFT, *args, **kwargs):
|
||||||
ImageMobject.__init__(self, "speech_bubble", *args, **kwargs)
|
ImageMobject.__init__(self, "speech_bubble", *args, **kwargs)
|
||||||
self.scale(0.25)
|
self.direction = direction
|
||||||
|
self.scale(0.4)
|
||||||
|
self.center()
|
||||||
|
if direction[0] > 0:
|
||||||
|
self.rotate(np.pi, UP)
|
||||||
|
self.reload_tip()
|
||||||
|
|
||||||
|
def reload_tip(self):
|
||||||
|
#TODO, perhaps make this resiliant to different point orderings
|
||||||
|
self.tip = self.points[-1]
|
||||||
|
|
||||||
|
def speak_from(self, mobject):
|
||||||
|
dest = mobject.get_center()
|
||||||
|
dest += self.direction * mobject.get_width()/2
|
||||||
|
dest += UP * mobject.get_height()/2
|
||||||
|
self.shift(dest - self.tip)
|
||||||
|
self.reload_tip()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def write(self, text):
|
||||||
|
smidgeon = 0.1*UP + 0.2*self.direction
|
||||||
|
self.text = text_mobject(text)
|
||||||
|
self.text.scale(0.75*self.get_width() / self.text.get_width())
|
||||||
|
self.text.shift(self.get_center() + smidgeon)
|
||||||
|
|
||||||
|
|
||||||
class ThoughtBubble(ImageMobject):
|
class ThoughtBubble(ImageMobject):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
ImageMobject.__init__(self, "thought_bubble", *args, **kwargs)
|
ImageMobject.__init__(self, "thought_bubble", *args, **kwargs)
|
||||||
self.scale(0.5)
|
self.scale(0.5)
|
||||||
|
self.center()
|
||||||
|
|
||||||
class SimpleFace(ImageMobject):
|
class Face(ImageMobject):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, mode = "simple", *args, **kwargs):
|
||||||
ImageMobject.__init__(self, "simple_face", *args, **kwargs)
|
"""
|
||||||
|
Mode can be "simple", "talking", "straight"
|
||||||
|
"""
|
||||||
|
ImageMobject.__init__(self, mode + "_face", *args, **kwargs)
|
||||||
self.scale(0.5)
|
self.scale(0.5)
|
||||||
|
self.center()
|
||||||
|
|
||||||
class VideoIcon(ImageMobject):
|
class VideoIcon(ImageMobject):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
ImageMobject.__init__(self, "video_icon", *args, **kwargs)
|
ImageMobject.__init__(self, "video_icon", *args, **kwargs)
|
||||||
self.scale(0.3)
|
self.scale(0.3)
|
||||||
|
self.center()
|
||||||
|
|
||||||
def text_mobject(text, size = "\\Large"):
|
def text_mobject(text, size = "\\Large"):
|
||||||
return tex_mobjects(text, size, TEMPLATE_TEXT_FILE)
|
return tex_mobjects(text, size, TEMPLATE_TEXT_FILE)
|
||||||
|
@ -95,10 +95,12 @@ class Mobject(object):
|
|||||||
self.points += v
|
self.points += v
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def wag(self, wag_direction = (0, 1, 0), wag_axis = (-1, 0, 0)):
|
def wag(self, wag_direction = RIGHT, wag_axis = DOWN,
|
||||||
|
wag_factor = 1.0):
|
||||||
alphas = np.dot(self.points, np.transpose(wag_axis))
|
alphas = np.dot(self.points, np.transpose(wag_axis))
|
||||||
alphas -= min(alphas)
|
alphas -= min(alphas)
|
||||||
alphas /= max(alphas)
|
alphas /= max(alphas)
|
||||||
|
alphas = alphas**wag_factor
|
||||||
self.points += np.dot(
|
self.points += np.dot(
|
||||||
alphas.reshape((len(alphas), 1)),
|
alphas.reshape((len(alphas), 1)),
|
||||||
np.array(wag_direction).reshape((1, 3))
|
np.array(wag_direction).reshape((1, 3))
|
||||||
|
@ -18,12 +18,11 @@ class Arrow(Mobject1D):
|
|||||||
tail = None, length = 1, tip_length = 0.25,
|
tail = None, length = 1, tip_length = 0.25,
|
||||||
normal = (0, 0, 1), *args, **kwargs):
|
normal = (0, 0, 1), *args, **kwargs):
|
||||||
self.point = np.array(point)
|
self.point = np.array(point)
|
||||||
if tail == None:
|
if tail is not None:
|
||||||
|
direction = self.point - tail
|
||||||
|
length = np.linalg.norm(direction)
|
||||||
self.direction = np.array(direction) / np.linalg.norm(direction)
|
self.direction = np.array(direction) / np.linalg.norm(direction)
|
||||||
self.length = length
|
self.length = length
|
||||||
else:
|
|
||||||
self.direction = self.point - tail
|
|
||||||
self.length = np.linalg.norm(self.direction)
|
|
||||||
self.normal = np.array(normal)
|
self.normal = np.array(normal)
|
||||||
self.tip_length = tip_length
|
self.tip_length = tip_length
|
||||||
Mobject1D.__init__(self, *args, **kwargs)
|
Mobject1D.__init__(self, *args, **kwargs)
|
||||||
|
@ -10,22 +10,19 @@ from animation import *
|
|||||||
from mobject import *
|
from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from region import *
|
from region import *
|
||||||
from scene import Scene, GraphScene
|
from scene import Scene
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
|
|
||||||
class SampleScene(GraphScene):
|
class SampleScene(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
randy = Randolph()
|
||||||
self.generate_regions()
|
self.add(randy)
|
||||||
self.generate_dual_graph()
|
self.dither()
|
||||||
self.generate_spanning_tree()
|
self.animate(BlinkPiCreature(randy))
|
||||||
self.add(self.spanning_tree)
|
self.dither()
|
||||||
for count in range(len(self.regions)):
|
self.animate(WaveArm(randy))
|
||||||
self.add(tex_mobject(str(count)).shift(self.dual_points[count]))
|
self.dither()
|
||||||
for count in range(len(self.edges)):
|
|
||||||
self.add(tex_mobject(str(count)).shift(self.edges[count].get_center()))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
150
scene/graphs.py
150
scene/graphs.py
@ -11,18 +11,40 @@ from region import *
|
|||||||
from constants import *
|
from constants import *
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
CUBE_GRAPH = {
|
class Graph():
|
||||||
"name" : "CubeGraph",
|
def __init__(self):
|
||||||
# 5 7
|
#List of points in R^3
|
||||||
# 12
|
vertices = []
|
||||||
# 03
|
#List of pairs of indices of vertices
|
||||||
# 4 6
|
edges = []
|
||||||
"vertices" : [
|
#List of tuples of indices of vertices. The last should
|
||||||
|
#be a cycle whose interior is the entire graph, and when
|
||||||
|
#regions are computed its complement will be taken.
|
||||||
|
region_cycles = []
|
||||||
|
|
||||||
|
self.construct()
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
class CubeGraph(Graph):
|
||||||
|
"""
|
||||||
|
5 7
|
||||||
|
12
|
||||||
|
03
|
||||||
|
4 6
|
||||||
|
"""
|
||||||
|
def construct(self):
|
||||||
|
self.vertices = [
|
||||||
(x, y, 0)
|
(x, y, 0)
|
||||||
for r in (1, 2)
|
for r in (1, 2)
|
||||||
for x, y in it.product([-r,r], [-r, r])
|
for x, y in it.product([-r,r], [-r, r])
|
||||||
],
|
]
|
||||||
"edges" : [
|
self.edges = [
|
||||||
(0, 1),
|
(0, 1),
|
||||||
(0, 2),
|
(0, 2),
|
||||||
(3, 1),
|
(3, 1),
|
||||||
@ -35,8 +57,8 @@ CUBE_GRAPH = {
|
|||||||
(1, 5),
|
(1, 5),
|
||||||
(2, 6),
|
(2, 6),
|
||||||
(3, 7),
|
(3, 7),
|
||||||
],
|
]
|
||||||
"region_cycles" : [
|
self.region_cycles = [
|
||||||
[0, 2, 3, 1],
|
[0, 2, 3, 1],
|
||||||
[4, 0, 1, 5],
|
[4, 0, 1, 5],
|
||||||
[4, 6, 2, 0],
|
[4, 6, 2, 0],
|
||||||
@ -44,15 +66,17 @@ CUBE_GRAPH = {
|
|||||||
[7, 5, 1, 3],
|
[7, 5, 1, 3],
|
||||||
[4, 6, 7, 5],#By convention, last region will be "outside"
|
[4, 6, 7, 5],#By convention, last region will be "outside"
|
||||||
]
|
]
|
||||||
}
|
|
||||||
|
|
||||||
SAMPLE_GRAPH = {
|
|
||||||
"name" : "SampleGraph",
|
class SampleGraph(Graph):
|
||||||
# 4 2 3 8
|
"""
|
||||||
# 0 1
|
4 2 3 8
|
||||||
# 7
|
0 1
|
||||||
# 5 6
|
7
|
||||||
"vertices" :[
|
5 6
|
||||||
|
"""
|
||||||
|
def construct(self):
|
||||||
|
self.vertices = [
|
||||||
( 0, 0, 0),
|
( 0, 0, 0),
|
||||||
( 2, 0, 0),
|
( 2, 0, 0),
|
||||||
( 1, 2, 0),
|
( 1, 2, 0),
|
||||||
@ -62,8 +86,8 @@ SAMPLE_GRAPH = {
|
|||||||
( 2,-2, 0),
|
( 2,-2, 0),
|
||||||
( 4,-1, 0),
|
( 4,-1, 0),
|
||||||
( 6, 2, 0),
|
( 6, 2, 0),
|
||||||
],
|
]
|
||||||
"edges" : [
|
self.edges = [
|
||||||
(0, 1),
|
(0, 1),
|
||||||
(1, 2),
|
(1, 2),
|
||||||
(1, 3),
|
(1, 3),
|
||||||
@ -79,8 +103,8 @@ SAMPLE_GRAPH = {
|
|||||||
(7, 1),
|
(7, 1),
|
||||||
(7, 8),
|
(7, 8),
|
||||||
(8, 3),
|
(8, 3),
|
||||||
],
|
]
|
||||||
"region_cycles" : [
|
self.region_cycles = [
|
||||||
(0, 1, 2),
|
(0, 1, 2),
|
||||||
(1, 3, 2),
|
(1, 3, 2),
|
||||||
(2, 4, 0),
|
(2, 4, 0),
|
||||||
@ -91,21 +115,22 @@ SAMPLE_GRAPH = {
|
|||||||
(4, 5, 6, 7, 8, 3, 2),
|
(4, 5, 6, 7, 8, 3, 2),
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
OCTOHEDRON_GRAPH = {
|
class OctohedronGraph(Graph):
|
||||||
"name" : "OctohedronGraph",
|
"""
|
||||||
# 3
|
3
|
||||||
#
|
|
||||||
# 1 0
|
1 0
|
||||||
# 2
|
2
|
||||||
#4 5
|
4 5
|
||||||
"vertices" : [
|
"""
|
||||||
|
def construct(self):
|
||||||
|
self.vertices = [
|
||||||
(r*np.cos(angle), r*np.sin(angle)-1, 0)
|
(r*np.cos(angle), r*np.sin(angle)-1, 0)
|
||||||
for r, s in [(1, 0), (3, 3)]
|
for r, s in [(1, 0), (3, 3)]
|
||||||
for angle in (np.pi/6) * np.array([s, 4 + s, 8 + s])
|
for angle in (np.pi/6) * np.array([s, 4 + s, 8 + s])
|
||||||
],
|
]
|
||||||
"edges" : [
|
self.edges = [
|
||||||
(0, 1),
|
(0, 1),
|
||||||
(1, 2),
|
(1, 2),
|
||||||
(2, 0),
|
(2, 0),
|
||||||
@ -118,8 +143,8 @@ OCTOHEDRON_GRAPH = {
|
|||||||
(4, 2),
|
(4, 2),
|
||||||
(4, 5),
|
(4, 5),
|
||||||
(5, 2),
|
(5, 2),
|
||||||
],
|
]
|
||||||
"region_cycles" : [
|
self.region_cycles = [
|
||||||
(0, 1, 2),
|
(0, 1, 2),
|
||||||
(0, 5, 3),
|
(0, 5, 3),
|
||||||
(3, 1, 0),
|
(3, 1, 0),
|
||||||
@ -129,47 +154,54 @@ OCTOHEDRON_GRAPH = {
|
|||||||
(5, 0, 2),
|
(5, 0, 2),
|
||||||
(3, 4, 5),
|
(3, 4, 5),
|
||||||
]
|
]
|
||||||
}
|
|
||||||
|
|
||||||
def complete_graph(n, radius = 3):
|
|
||||||
return {
|
class CompleteGraph(Graph):
|
||||||
"name" : "Complete%d"%n,
|
def __init__(self, num_vertices, radius = 3):
|
||||||
"vertices" : [
|
self.num_vertices = num_vertices
|
||||||
(radius*np.cos(theta), radius*np.sin(theta), 0)
|
self.radius = radius
|
||||||
for x in range(n)
|
Graph.__init__(self)
|
||||||
for theta in [2*np.pi*x / n]
|
|
||||||
],
|
def construct(self):
|
||||||
"edges" : it.combinations(range(n), 2)
|
self.vertices = [
|
||||||
}
|
(self.radius*np.cos(theta), self.radius*np.sin(theta), 0)
|
||||||
|
for x in range(self.num_vertices)
|
||||||
|
for theta in [2*np.pi*x / self.num_vertices]
|
||||||
|
]
|
||||||
|
self.edges = it.combinations(range(self.num_vertices), 2)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return Graph.__str__(self) + str(self.num_vertices)
|
||||||
|
|
||||||
|
|
||||||
class GraphScene(Scene):
|
class GraphScene(Scene):
|
||||||
args_list = [
|
args_list = [
|
||||||
(CUBE_GRAPH,),
|
(CubeGraph(),),
|
||||||
(SAMPLE_GRAPH,),
|
(SampleGraph(),),
|
||||||
(OCTOHEDRON_GRAPH,),
|
(OctohedronGraph(),),
|
||||||
]
|
]
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def args_to_string(*args):
|
def args_to_string(*args):
|
||||||
return args[0]["name"]
|
return str(args[0])
|
||||||
|
|
||||||
def __init__(self, graph, *args, **kwargs):
|
def __init__(self, graph, *args, **kwargs):
|
||||||
#See CUBE_GRAPH above for format of graph
|
#See CubeGraph() above for format of graph
|
||||||
self.graph = graph
|
self.graph = graph
|
||||||
Scene.__init__(self, *args, **kwargs)
|
Scene.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.points = map(np.array, self.graph["vertices"])
|
self.points = map(np.array, self.graph.vertices)
|
||||||
self.vertices = self.dots = [Dot(p) for p in self.points]
|
self.vertices = self.dots = [Dot(p) for p in self.points]
|
||||||
self.edges = self.lines = [
|
self.edges = self.lines = [
|
||||||
Line(self.points[i], self.points[j])
|
Line(self.points[i], self.points[j])
|
||||||
for i, j in self.graph["edges"]
|
for i, j in self.graph.edges
|
||||||
]
|
]
|
||||||
self.add(*self.dots + self.edges)
|
self.add(*self.dots + self.edges)
|
||||||
|
|
||||||
def generate_regions(self):
|
def generate_regions(self):
|
||||||
regions = [
|
regions = [
|
||||||
self.region_from_cycle(cycle)
|
self.region_from_cycle(cycle)
|
||||||
for cycle in self.graph["region_cycles"]
|
for cycle in self.graph.region_cycles
|
||||||
]
|
]
|
||||||
regions[-1].complement()#Outer region painted outwardly...
|
regions[-1].complement()#Outer region painted outwardly...
|
||||||
self.regions = regions
|
self.regions = regions
|
||||||
@ -242,7 +274,7 @@ class GraphScene(Scene):
|
|||||||
|
|
||||||
def trace_cycle(self, cycle = None, color = "yellow", run_time = 2.0):
|
def trace_cycle(self, cycle = None, color = "yellow", run_time = 2.0):
|
||||||
if cycle == None:
|
if cycle == None:
|
||||||
cycle = self.graph["region_cycles"][0]
|
cycle = self.graph.region_cycles[0]
|
||||||
time_per_edge = run_time / len(cycle)
|
time_per_edge = run_time / len(cycle)
|
||||||
next_in_cycle = it.cycle(cycle)
|
next_in_cycle = it.cycle(cycle)
|
||||||
next_in_cycle.next()#jump one ahead
|
next_in_cycle.next()#jump one ahead
|
||||||
@ -257,7 +289,7 @@ class GraphScene(Scene):
|
|||||||
|
|
||||||
def generate_spanning_tree(self, root = 0, color = "yellow"):
|
def generate_spanning_tree(self, root = 0, color = "yellow"):
|
||||||
self.spanning_tree_root = 0
|
self.spanning_tree_root = 0
|
||||||
pairs = deepcopy(self.graph["edges"])
|
pairs = deepcopy(self.graph.edges)
|
||||||
pairs += [tuple(reversed(pair)) for pair in pairs]
|
pairs += [tuple(reversed(pair)) for pair in pairs]
|
||||||
self.spanning_tree_index_pairs = []
|
self.spanning_tree_index_pairs = []
|
||||||
curr = root
|
curr = root
|
||||||
@ -339,7 +371,7 @@ class GraphScene(Scene):
|
|||||||
|
|
||||||
def generate_dual_graph(self):
|
def generate_dual_graph(self):
|
||||||
point_at_infinity = np.array([np.inf]*3)
|
point_at_infinity = np.array([np.inf]*3)
|
||||||
cycles = self.graph["region_cycles"]
|
cycles = self.graph.region_cycles
|
||||||
self.dual_points = [
|
self.dual_points = [
|
||||||
center_of_mass([
|
center_of_mass([
|
||||||
self.points[index]
|
self.points[index]
|
||||||
@ -355,7 +387,7 @@ class GraphScene(Scene):
|
|||||||
self.dual_points[-1] = point_at_infinity
|
self.dual_points[-1] = point_at_infinity
|
||||||
|
|
||||||
self.dual_edges = []
|
self.dual_edges = []
|
||||||
for pair in self.graph["edges"]:
|
for pair in self.graph.edges:
|
||||||
dual_point_pair = []
|
dual_point_pair = []
|
||||||
for cycle in cycles:
|
for cycle in cycles:
|
||||||
if not (pair[0] in cycle and pair[1] in cycle):
|
if not (pair[0] in cycle and pair[1] in cycle):
|
||||||
|
@ -21,7 +21,139 @@ RANDOLPH_SCALE_VAL = 0.3
|
|||||||
EDGE_ANNOTATION_SCALE_VAL = 0.7
|
EDGE_ANNOTATION_SCALE_VAL = 0.7
|
||||||
DUAL_CYCLE = [3, 4, 5, 6, 1, 0, 2, 3]
|
DUAL_CYCLE = [3, 4, 5, 6, 1, 0, 2, 3]
|
||||||
|
|
||||||
class IntroduceGraphs(GraphScene):
|
class EulersFormulaWords(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(tex_mobject("V-E+F=2"))
|
||||||
|
|
||||||
|
class TheTheoremWords(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(text_mobject("The Theorem:"))
|
||||||
|
|
||||||
|
class ProofAtLastWords(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(text_mobject("The Proof At Last..."))
|
||||||
|
|
||||||
|
class DualSpanningTreeWords(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.add(text_mobject("Spanning trees have duals too!"))
|
||||||
|
|
||||||
|
class PreferOtherProofDialogue(Scene):
|
||||||
|
def construct(self):
|
||||||
|
teacher = Face("talking").shift(2*LEFT)
|
||||||
|
student = Face("straight").shift(2*RIGHT)
|
||||||
|
teacher_bubble = SpeechBubble(LEFT).speak_from(teacher)
|
||||||
|
student_bubble = SpeechBubble(RIGHT).speak_from(student)
|
||||||
|
teacher_bubble.write("Look at this \\\\ elegant proof!")
|
||||||
|
student_bubble.write("I prefer the \\\\ other proof.")
|
||||||
|
|
||||||
|
self.add(student, teacher, teacher_bubble, teacher_bubble.text)
|
||||||
|
self.dither(2)
|
||||||
|
self.animate(Transform(
|
||||||
|
Dot(student_bubble.tip).highlight("black"),
|
||||||
|
CompoundMobject(student_bubble, student_bubble.text)
|
||||||
|
))
|
||||||
|
self.dither(2)
|
||||||
|
self.remove(teacher_bubble.text)
|
||||||
|
teacher_bubble.write("Does that make this \\\\ any less elegant?")
|
||||||
|
self.add(teacher_bubble.text)
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
|
class IllustrateDuality(GraphScene):
|
||||||
|
def construct(self):
|
||||||
|
GraphScene.construct(self)
|
||||||
|
self.generate_dual_graph()
|
||||||
|
|
||||||
|
self.add(text_mobject("Duality").to_edge(UP))
|
||||||
|
self.remove(*self.vertices)
|
||||||
|
def special_alpha(t):
|
||||||
|
if t > 0.5:
|
||||||
|
t = 1 - t
|
||||||
|
if t < 0.25:
|
||||||
|
return high_inflection_0_to_1(4*t)
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
kwargs = {
|
||||||
|
"run_time" : 5.0,
|
||||||
|
"alpha_func" : special_alpha
|
||||||
|
}
|
||||||
|
self.animate(*[
|
||||||
|
Transform(*edge_pair, **kwargs)
|
||||||
|
for edge_pair in zip(self.edges, self.dual_edges)
|
||||||
|
] + [
|
||||||
|
Transform(
|
||||||
|
CompoundMobject(*[
|
||||||
|
self.vertices[index]
|
||||||
|
for index in cycle
|
||||||
|
]),
|
||||||
|
dv,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
for cycle, dv in zip(
|
||||||
|
self.graph.region_cycles,
|
||||||
|
self.dual_vertices
|
||||||
|
)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class IntroduceGraph(GraphScene):
|
||||||
|
def construct(self):
|
||||||
|
GraphScene.construct(self)
|
||||||
|
tweaked_graph = deepcopy(self.graph)
|
||||||
|
for index in 2, 4:
|
||||||
|
tweaked_graph.vertices[index] += 2.8*RIGHT + 1.8*DOWN
|
||||||
|
tweaked_self = GraphScene(tweaked_graph)
|
||||||
|
edges_to_remove = [
|
||||||
|
self.edges[self.graph.edges.index(pair)]
|
||||||
|
for pair in [(4, 5), (0, 5), (1, 5), (7, 1), (8, 3)]
|
||||||
|
]
|
||||||
|
|
||||||
|
connected, planar, graph = CompoundMobject(*text_mobject([
|
||||||
|
"Connected ", "Planar ", "Graph"
|
||||||
|
])).to_edge(UP).split()
|
||||||
|
not_okay = text_mobject("Not Okay").highlight("red")
|
||||||
|
planar_explanation = text_mobject("""
|
||||||
|
(``Planar'' just means we can draw it without
|
||||||
|
intersecting lines)
|
||||||
|
""", size = "\\small")
|
||||||
|
planar_explanation.shift(planar.get_center() + 0.5*DOWN)
|
||||||
|
|
||||||
|
self.draw_vertices()
|
||||||
|
self.draw_edges()
|
||||||
|
self.clear()
|
||||||
|
self.add(*self.vertices + self.edges)
|
||||||
|
self.dither()
|
||||||
|
self.add(graph)
|
||||||
|
self.dither()
|
||||||
|
kwargs = {
|
||||||
|
"alpha_func" : there_and_back,
|
||||||
|
"run_time" : 5.0
|
||||||
|
}
|
||||||
|
self.add(not_okay)
|
||||||
|
self.animate(*[
|
||||||
|
Transform(*pair, **kwargs)
|
||||||
|
for pair in zip(
|
||||||
|
self.edges + self.vertices,
|
||||||
|
tweaked_self.edges + tweaked_self.vertices,
|
||||||
|
)
|
||||||
|
])
|
||||||
|
self.remove(not_okay)
|
||||||
|
self.add(planar, planar_explanation)
|
||||||
|
self.dither(2)
|
||||||
|
self.remove(planar_explanation)
|
||||||
|
self.add(not_okay)
|
||||||
|
self.remove(*edges_to_remove)
|
||||||
|
self.animate(ShowCreation(
|
||||||
|
CompoundMobject(*edges_to_remove),
|
||||||
|
alpha_func = lambda t : 1 - t,
|
||||||
|
run_time = 1.0
|
||||||
|
))
|
||||||
|
self.dither(2)
|
||||||
|
self.remove(not_okay)
|
||||||
|
self.add(connected, *edges_to_remove)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
class OldIntroduceGraphs(GraphScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
self.draw_vertices()
|
self.draw_vertices()
|
||||||
@ -29,7 +161,7 @@ class IntroduceGraphs(GraphScene):
|
|||||||
self.dither()
|
self.dither()
|
||||||
self.clear()
|
self.clear()
|
||||||
self.add(*self.edges)
|
self.add(*self.edges)
|
||||||
self.replace_vertices_with(SimpleFace().scale(0.4))
|
self.replace_vertices_with(Face().scale(0.4))
|
||||||
friends = text_mobject("Friends").scale(EDGE_ANNOTATION_SCALE_VAL)
|
friends = text_mobject("Friends").scale(EDGE_ANNOTATION_SCALE_VAL)
|
||||||
self.annotate_edges(friends.shift((0, friends.get_height()/2, 0)))
|
self.annotate_edges(friends.shift((0, friends.get_height()/2, 0)))
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
@ -55,9 +187,9 @@ class PlanarGraphDefinition(Scene):
|
|||||||
graphs = [
|
graphs = [
|
||||||
CompoundMobject(*GraphScene(g).mobjects)
|
CompoundMobject(*GraphScene(g).mobjects)
|
||||||
for g in [
|
for g in [
|
||||||
CUBE_GRAPH,
|
CubeGraph(),
|
||||||
complete_graph(5),
|
CompleteGraph(5),
|
||||||
OCTOHEDRON_GRAPH
|
OctohedronGraph()
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -84,7 +216,7 @@ class PlanarGraphDefinition(Scene):
|
|||||||
|
|
||||||
|
|
||||||
class TerminologyFromPolyhedra(GraphScene):
|
class TerminologyFromPolyhedra(GraphScene):
|
||||||
args_list = [(CUBE_GRAPH,)]
|
args_list = [(CubeGraph(),)]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
rot_kwargs = {
|
rot_kwargs = {
|
||||||
@ -97,7 +229,7 @@ class TerminologyFromPolyhedra(GraphScene):
|
|||||||
]
|
]
|
||||||
cube = CompoundMobject(*[
|
cube = CompoundMobject(*[
|
||||||
Line(vertices[edge[0]], vertices[edge[1]])
|
Line(vertices[edge[0]], vertices[edge[1]])
|
||||||
for edge in self.graph["edges"]
|
for edge in self.graph.edges
|
||||||
])
|
])
|
||||||
cube.rotate(-np.pi/3, [0, 0, 1])
|
cube.rotate(-np.pi/3, [0, 0, 1])
|
||||||
cube.rotate(-np.pi/3, [0, 1, 0])
|
cube.rotate(-np.pi/3, [0, 1, 0])
|
||||||
@ -106,10 +238,11 @@ class TerminologyFromPolyhedra(GraphScene):
|
|||||||
regions_to_faces = text_mobject("Regions $\\to$ Faces").to_corner()
|
regions_to_faces = text_mobject("Regions $\\to$ Faces").to_corner()
|
||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
self.animate(TransformAnimations(
|
# self.animate(TransformAnimations(
|
||||||
Rotating(Dodecahedron(), **rot_kwargs),
|
# Rotating(Dodecahedron(), **rot_kwargs),
|
||||||
Rotating(cube, **rot_kwargs),
|
# Rotating(cube, **rot_kwargs)
|
||||||
))
|
# ))
|
||||||
|
self.animate(Rotating(cube, **rot_kwargs))
|
||||||
self.clear()
|
self.clear()
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
Transform(l1, l2)
|
Transform(l1, l2)
|
||||||
@ -185,11 +318,11 @@ class ThreePiecesOfTerminology(GraphScene):
|
|||||||
|
|
||||||
class WalkingRandolph(GraphScene):
|
class WalkingRandolph(GraphScene):
|
||||||
args_list = [
|
args_list = [
|
||||||
(SAMPLE_GRAPH, [0, 1, 7, 8]),
|
(SampleGraph(), [0, 1, 7, 8]),
|
||||||
]
|
]
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def args_to_string(graph, path):
|
def args_to_string(graph, path):
|
||||||
return graph["name"] + "".join(map(str, path))
|
return str(graph) + "".join(map(str, path))
|
||||||
|
|
||||||
def __init__(self, graph, path, *args, **kwargs):
|
def __init__(self, graph, path, *args, **kwargs):
|
||||||
self.path = path
|
self.path = path
|
||||||
@ -211,7 +344,7 @@ class WalkingRandolph(GraphScene):
|
|||||||
|
|
||||||
|
|
||||||
class PathExamples(GraphScene):
|
class PathExamples(GraphScene):
|
||||||
args_list = [(SAMPLE_GRAPH,)]
|
args_list = [(SampleGraph(),)]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
paths = [
|
paths = [
|
||||||
@ -257,14 +390,14 @@ class PathExamples(GraphScene):
|
|||||||
|
|
||||||
class IntroduceCycle(WalkingRandolph):
|
class IntroduceCycle(WalkingRandolph):
|
||||||
args_list = [
|
args_list = [
|
||||||
(SAMPLE_GRAPH, [0, 1, 3, 2, 0])
|
(SampleGraph(), [0, 1, 3, 2, 0])
|
||||||
]
|
]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
WalkingRandolph.construct(self)
|
WalkingRandolph.construct(self)
|
||||||
self.remove(self.randy)
|
self.remove(self.randy)
|
||||||
encompassed_cycles = filter(
|
encompassed_cycles = filter(
|
||||||
lambda c : set(c).issubset(self.path),
|
lambda c : set(c).issubset(self.path),
|
||||||
self.graph["region_cycles"]
|
self.graph.region_cycles
|
||||||
)
|
)
|
||||||
regions = [
|
regions = [
|
||||||
self.region_from_cycle(cycle)
|
self.region_from_cycle(cycle)
|
||||||
@ -339,7 +472,7 @@ class DefineSpanningTree(GraphScene):
|
|||||||
))
|
))
|
||||||
self.dither(2)
|
self.dither(2)
|
||||||
|
|
||||||
unneeded_edges = filter(out_of_spanning_set, self.graph["edges"])
|
unneeded_edges = filter(out_of_spanning_set, self.graph.edges)
|
||||||
for edge, limit in zip(unneeded_edges, range(5)):
|
for edge, limit in zip(unneeded_edges, range(5)):
|
||||||
line = Line(self.points[edge[0]], self.points[edge[1]])
|
line = Line(self.points[edge[0]], self.points[edge[1]])
|
||||||
line.highlight("red")
|
line.highlight("red")
|
||||||
@ -453,11 +586,15 @@ class FacebookGraphAsAbstractSet(Scene):
|
|||||||
(1, 3),
|
(1, 3),
|
||||||
(1, 2),
|
(1, 2),
|
||||||
]
|
]
|
||||||
names_mob = text_mobject("\\\\".join(names)).shift(3*LEFT)
|
names_string = "\\\\".join(names + ["$\\vdots$"])
|
||||||
friends_mob = tex_mobject("\\\\".join([
|
friends_string = "\\\\".join([
|
||||||
"\\text{%s}&\\leftrightarrow\\text{%s}"%(names[i],names[j])
|
"\\text{%s}&\\leftrightarrow\\text{%s}"%(names[i],names[j])
|
||||||
for i, j in friend_pairs
|
for i, j in friend_pairs
|
||||||
]), size = "\\Large").shift(3*RIGHT)
|
] + ["\\vdots"])
|
||||||
|
names_mob = text_mobject(names_string).shift(3*LEFT)
|
||||||
|
friends_mob = tex_mobject(
|
||||||
|
friends_string, size = "\\Large"
|
||||||
|
).shift(3*RIGHT)
|
||||||
accounts = text_mobject("\\textbf{Accounts}")
|
accounts = text_mobject("\\textbf{Accounts}")
|
||||||
accounts.shift(3*LEFT).to_edge(UP)
|
accounts.shift(3*LEFT).to_edge(UP)
|
||||||
friendships = text_mobject("\\textbf{Friendships}")
|
friendships = text_mobject("\\textbf{Friendships}")
|
||||||
@ -479,68 +616,147 @@ class FacebookGraphAsAbstractSet(Scene):
|
|||||||
class ExamplesOfGraphs(GraphScene):
|
class ExamplesOfGraphs(GraphScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
buff = 0.5
|
buff = 0.5
|
||||||
self.graph["vertices"] = map(
|
self.graph.vertices = map(
|
||||||
lambda v : v + DOWN + RIGHT,
|
lambda v : v + DOWN + RIGHT,
|
||||||
self.graph["vertices"]
|
self.graph.vertices
|
||||||
)
|
)
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
objects, notion = CompoundMobject(*text_mobject(
|
self.generate_regions()
|
||||||
|
objects, notions = CompoundMobject(*text_mobject(
|
||||||
["Objects \\quad\\quad ", "Thing that connects objects"]
|
["Objects \\quad\\quad ", "Thing that connects objects"]
|
||||||
)).to_corner().shift(0.5*RIGHT).split()
|
)).to_corner().shift(0.5*RIGHT).split()
|
||||||
horizontal_line = Line(
|
horizontal_line = Line(
|
||||||
(-SPACE_WIDTH, SPACE_HEIGHT-1, 0),
|
(-SPACE_WIDTH, SPACE_HEIGHT-1, 0),
|
||||||
(max(notion.points[:,0]), SPACE_HEIGHT-1, 0)
|
(max(notions.points[:,0]), SPACE_HEIGHT-1, 0)
|
||||||
)
|
)
|
||||||
vert_line_x_val = min(notion.points[:,0]) - buff
|
vert_line_x_val = min(notions.points[:,0]) - buff
|
||||||
vertical_line = Line(
|
vertical_line = Line(
|
||||||
(vert_line_x_val, SPACE_HEIGHT, 0),
|
(vert_line_x_val, SPACE_HEIGHT, 0),
|
||||||
(vert_line_x_val,-SPACE_HEIGHT, 0)
|
(vert_line_x_val,-SPACE_HEIGHT, 0)
|
||||||
)
|
)
|
||||||
objects_and_notion_strings = [
|
objects_and_notions = [
|
||||||
("Facebook accounts", "Friendship"),
|
("Facebook accounts", "Friendship"),
|
||||||
("Cities", "Roads between them"),
|
("English Words", "Differ by One Letter"),
|
||||||
("Wikipedia pages", "Links"),
|
("Mathematicians", "Coauthorship"),
|
||||||
("Neurons", "Synapses"),
|
("Neurons", "Synapses"),
|
||||||
(
|
(
|
||||||
"Regions our graph \\\\ cuts the plane into",
|
"Regions our graph \\\\ cuts the plane into",
|
||||||
"Shareed edges"
|
"Shared edges"
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
objects_and_notions = [
|
|
||||||
(
|
|
||||||
text_mobject(obj, size = "\\small").to_edge(LEFT),
|
|
||||||
text_mobject(no, size = "\\small").to_edge(LEFT).shift(
|
|
||||||
(vert_line_x_val + SPACE_WIDTH)*RIGHT
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for obj, no in objects_and_notion_strings
|
|
||||||
]
|
|
||||||
|
|
||||||
self.clear()
|
self.clear()
|
||||||
self.add(objects, notion, horizontal_line, vertical_line)
|
self.add(objects, notions, horizontal_line, vertical_line)
|
||||||
for (obj, notion), height in zip(objects_and_notions, it.count(2, -1)):
|
for (obj, notion), height in zip(objects_and_notions, it.count(2, -1)):
|
||||||
if obj == objects_and_notions[-1][0]:
|
obj_mob = text_mobject(obj, size = "\\small").to_edge(LEFT)
|
||||||
obj.highlight("yellow")
|
not_mob = text_mobject(notion, size = "\\small").to_edge(LEFT)
|
||||||
notion.highlight("yellow")
|
not_mob.shift((vert_line_x_val + SPACE_WIDTH)*RIGHT)
|
||||||
|
obj_mob.shift(height*UP)
|
||||||
|
not_mob.shift(height*UP)
|
||||||
|
|
||||||
|
if obj.startswith("Regions"):
|
||||||
|
self.handle_dual_graph(obj_mob, not_mob)
|
||||||
|
elif obj.startswith("English"):
|
||||||
|
self.handle_english_words(obj_mob, not_mob)
|
||||||
|
else:
|
||||||
|
self.add(obj_mob)
|
||||||
|
self.dither()
|
||||||
|
self.add(not_mob)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def handle_english_words(self, words1, words2):
|
||||||
|
words = map(text_mobject, ["graph", "grape", "gape", "gripe"])
|
||||||
|
words[0].shift(RIGHT)
|
||||||
|
words[1].shift(3*RIGHT)
|
||||||
|
words[2].shift(3*RIGHT + 2*UP)
|
||||||
|
words[3].shift(3*RIGHT + 2*DOWN)
|
||||||
|
lines = [
|
||||||
|
Line(*pair)
|
||||||
|
for pair in [
|
||||||
|
(
|
||||||
|
words[0].get_center() + RIGHT*words[0].get_width()/2,
|
||||||
|
words[1].get_center() + LEFT*words[1].get_width()/2
|
||||||
|
),(
|
||||||
|
words[1].get_center() + UP*words[1].get_height()/2,
|
||||||
|
words[2].get_center() + DOWN*words[2].get_height()/2
|
||||||
|
),(
|
||||||
|
words[1].get_center() + DOWN*words[1].get_height()/2,
|
||||||
|
words[3].get_center() + UP*words[3].get_height()/2
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
comp_words = CompoundMobject(*words)
|
||||||
|
comp_lines = CompoundMobject(*lines)
|
||||||
|
self.add(words1)
|
||||||
|
self.animate(ShowCreation(comp_words, run_time = 1.0))
|
||||||
|
self.dither()
|
||||||
|
self.add(words2)
|
||||||
|
self.animate(ShowCreation(comp_lines, run_time = 1.0))
|
||||||
|
self.dither()
|
||||||
|
self.remove(comp_words, comp_lines)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_dual_graph(self, words1, words2):
|
||||||
|
words1.highlight("yellow")
|
||||||
|
words2.highlight("yellow")
|
||||||
|
connected = text_mobject("Connected")
|
||||||
|
connected.highlight("lightgreen")
|
||||||
|
not_connected = text_mobject("Not Connected")
|
||||||
|
not_connected.highlight("red")
|
||||||
|
for mob in connected, not_connected:
|
||||||
|
mob.shift(self.points[3] + UP)
|
||||||
|
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
ShowCreation(mob, run_time = 1.0)
|
ShowCreation(mob, run_time = 1.0)
|
||||||
for mob in self.edges + self.vertices
|
for mob in self.edges + self.vertices
|
||||||
])
|
])
|
||||||
self.dither()
|
self.dither()
|
||||||
self.generate_regions()
|
|
||||||
for region in self.regions:
|
for region in self.regions:
|
||||||
self.highlight_region(region)
|
self.highlight_region(region)
|
||||||
self.add(obj.shift(height*UP))
|
self.add(words1)
|
||||||
self.dither(1)
|
|
||||||
self.add(notion.shift(height*UP))
|
|
||||||
if obj == objects_and_notions[-1][0]:
|
|
||||||
self.animate(*[
|
|
||||||
ShowCreation(deepcopy(e).highlight(), run_time = 1)
|
|
||||||
for e in self.edges
|
|
||||||
])
|
|
||||||
self.dither()
|
self.dither()
|
||||||
|
self.reset_background()
|
||||||
|
self.add(words2)
|
||||||
|
|
||||||
|
region_pairs = it.combinations(self.graph.region_cycles, 2)
|
||||||
|
for x in range(6):
|
||||||
|
want_matching = (x%2 == 0)
|
||||||
|
found = False
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
cycle1, cycle2 = region_pairs.next()
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
shared = set(cycle1).intersection(cycle2)
|
||||||
|
if len(shared) == 2 and want_matching:
|
||||||
|
break
|
||||||
|
if len(shared) != 2 and not want_matching:
|
||||||
|
break
|
||||||
|
for cycle in cycle1, cycle2:
|
||||||
|
index = self.graph.region_cycles.index(cycle)
|
||||||
|
self.highlight_region(self.regions[index])
|
||||||
|
if want_matching:
|
||||||
|
self.remove(not_connected)
|
||||||
|
self.add(connected)
|
||||||
|
tup = tuple(shared)
|
||||||
|
if tup not in self.graph.edges:
|
||||||
|
tup = tuple(reversed(tup))
|
||||||
|
edge = deepcopy(self.edges[self.graph.edges.index(tup)])
|
||||||
|
edge.highlight("red")
|
||||||
|
self.animate(ShowCreation(edge), run_time = 1.0)
|
||||||
|
self.dither()
|
||||||
|
self.remove(edge)
|
||||||
else:
|
else:
|
||||||
|
self.remove(connected)
|
||||||
|
self.add(not_connected)
|
||||||
self.dither(2)
|
self.dither(2)
|
||||||
|
self.reset_background()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DrawDualGraph(GraphScene):
|
class DrawDualGraph(GraphScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
@ -647,7 +863,7 @@ class ListOfCorrespondances(Scene):
|
|||||||
|
|
||||||
|
|
||||||
class CyclesCorrespondWithConnectedComponents(GraphScene):
|
class CyclesCorrespondWithConnectedComponents(GraphScene):
|
||||||
args_list = [(SAMPLE_GRAPH,)]
|
args_list = [(SampleGraph(),)]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
self.generate_regions()
|
self.generate_regions()
|
||||||
@ -694,7 +910,7 @@ class CyclesCorrespondWithConnectedComponents(GraphScene):
|
|||||||
|
|
||||||
|
|
||||||
class IntroduceMortimer(GraphScene):
|
class IntroduceMortimer(GraphScene):
|
||||||
args_list = [(SAMPLE_GRAPH,)]
|
args_list = [(SampleGraph(),)]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
self.generate_dual_graph()
|
self.generate_dual_graph()
|
||||||
@ -756,7 +972,7 @@ class IntroduceMortimer(GraphScene):
|
|||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
class RandolphMortimerSpanningTreeGame(GraphScene):
|
class RandolphMortimerSpanningTreeGame(GraphScene):
|
||||||
args_list = [(SAMPLE_GRAPH,)]
|
args_list = [(SampleGraph(),)]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
self.generate_spanning_tree()
|
self.generate_spanning_tree()
|
||||||
@ -781,23 +997,23 @@ class RandolphMortimerSpanningTreeGame(GraphScene):
|
|||||||
))
|
))
|
||||||
self.dither()
|
self.dither()
|
||||||
for index in range(len(self.regions)):
|
for index in range(len(self.regions)):
|
||||||
if index > 0:
|
# if index > 0:
|
||||||
edge = self.edges[dual_edges[index-1]]
|
# edge = self.edges[dual_edges[index-1]]
|
||||||
midpoint = edge.get_center()
|
# midpoint = edge.get_center()
|
||||||
self.animate(*[
|
# self.animate(*[
|
||||||
ShowCreation(Line(
|
# ShowCreation(Line(
|
||||||
midpoint,
|
# midpoint,
|
||||||
tip
|
# tip
|
||||||
).highlight("red"))
|
# ).highlight("red"))
|
||||||
for tip in edge.start, edge.end
|
# for tip in edge.start, edge.end
|
||||||
], run_time = time_per_dual_edge)
|
# ], run_time = time_per_dual_edge)
|
||||||
self.highlight_region(self.regions[region_ordering[index]])
|
self.highlight_region(self.regions[region_ordering[index]])
|
||||||
self.dither(time_per_dual_edge)
|
self.dither(time_per_dual_edge)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
cycle_index = region_ordering[-1]
|
cycle_index = region_ordering[-1]
|
||||||
cycle = self.graph["region_cycles"][cycle_index]
|
cycle = self.graph.region_cycles[cycle_index]
|
||||||
self.highlight_region(self.regions[cycle_index], "black")
|
self.highlight_region(self.regions[cycle_index], "black")
|
||||||
self.animate(ShowCreation(CompoundMobject(*[
|
self.animate(ShowCreation(CompoundMobject(*[
|
||||||
Line(self.points[last], self.points[next]).highlight("green")
|
Line(self.points[last], self.points[next]).highlight("green")
|
||||||
@ -806,7 +1022,7 @@ class RandolphMortimerSpanningTreeGame(GraphScene):
|
|||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
class MortimerCannotTraverseCycle(GraphScene):
|
class MortimerCannotTraverseCycle(GraphScene):
|
||||||
args_list = [(SAMPLE_GRAPH,)]
|
args_list = [(SampleGraph(),)]
|
||||||
def construct(self):
|
def construct(self):
|
||||||
GraphScene.construct(self)
|
GraphScene.construct(self)
|
||||||
self.generate_dual_graph()
|
self.generate_dual_graph()
|
||||||
@ -855,24 +1071,86 @@ class MortimerCannotTraverseCycle(GraphScene):
|
|||||||
])
|
])
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
class TwoPropertiesOfSpanningTree(Scene):
|
||||||
|
def construct(self):
|
||||||
|
spanning, tree = text_mobject(["Spanning ", "Tree"], size = "\\Huge")
|
||||||
|
spanning_explanation = text_mobject("""
|
||||||
|
Touches every vertex
|
||||||
|
""").shift(spanning.get_center() + 2*DOWN)
|
||||||
|
tree_explanation = text_mobject("""
|
||||||
|
No Cycles
|
||||||
|
""").shift(tree.get_center() + 2*UP)
|
||||||
|
|
||||||
|
self.add(spanning, tree)
|
||||||
|
self.dither()
|
||||||
|
for word, explanation, vect in [
|
||||||
|
(spanning, spanning_explanation, 0.5*UP),
|
||||||
|
(tree, tree_explanation, 0.5*DOWN)
|
||||||
|
]:
|
||||||
|
self.add(explanation)
|
||||||
|
self.add(Arrow(
|
||||||
|
explanation.get_center() + vect,
|
||||||
|
tail = word.get_center() - vect,
|
||||||
|
))
|
||||||
|
self.animate(ApplyMethod(word.highlight, "yellow"))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
class DualSpanningTree(GraphScene):
|
||||||
|
def construct(self):
|
||||||
|
GraphScene.construct(self)
|
||||||
|
self.generate_dual_graph()
|
||||||
|
self.generate_spanning_tree()
|
||||||
|
randy = Randolph()
|
||||||
|
randy.scale(RANDOLPH_SCALE_VAL)
|
||||||
|
randy.move_to(self.points[0])
|
||||||
|
morty = Mortimer()
|
||||||
|
morty.scale(RANDOLPH_SCALE_VAL)
|
||||||
|
morty.move_to(self.dual_points[0])
|
||||||
|
dual_edges = [1, 3, 4, 7, 11, 9, 13]
|
||||||
|
words = text_mobject("""
|
||||||
|
The red edges form a spanning tree of the dual graph!
|
||||||
|
""").to_edge(UP)
|
||||||
|
|
||||||
|
self.add(self.spanning_tree, randy, morty)
|
||||||
|
self.animate(ShowCreation(CompoundMobject(
|
||||||
|
*np.array(self.edges)[dual_edges]
|
||||||
|
).highlight("red")))
|
||||||
|
self.add(words)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
class TreeCountFormula(Scene):
|
class TreeCountFormula(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
time_per_branch = 0.5
|
time_per_branch = 0.5
|
||||||
self.add(text_mobject("""
|
text = text_mobject("""
|
||||||
In any tree:
|
In any tree:
|
||||||
$$E + 1 = V$$
|
$$E + 1 = V$$
|
||||||
"""))
|
""")
|
||||||
gs = GraphScene(SAMPLE_GRAPH)
|
gs = GraphScene(SampleGraph())
|
||||||
gs.generate_treeified_spanning_tree()
|
gs.generate_treeified_spanning_tree()
|
||||||
branches = gs.treeified_spanning_tree.to_edge(LEFT).split()
|
branches = gs.treeified_spanning_tree.to_edge(LEFT).split()
|
||||||
self.add(Dot(branches[0].points[0]))
|
|
||||||
|
all_dots = [Dot(branches[0].points[0])]
|
||||||
|
self.add(text, all_dots[0])
|
||||||
for branch in branches:
|
for branch in branches:
|
||||||
self.animate(
|
self.animate(
|
||||||
ShowCreation(branch),
|
ShowCreation(branch),
|
||||||
run_time = time_per_branch
|
run_time = time_per_branch
|
||||||
)
|
)
|
||||||
self.add(Dot(branch.points[-1]))
|
dot = Dot(branch.points[-1])
|
||||||
|
self.add(dot)
|
||||||
|
all_dots.append(dot)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
self.remove(*all_dots)
|
||||||
|
self.animate(
|
||||||
|
FadeOut(text),
|
||||||
|
FadeIn(CompoundMobject(*gs.edges + gs.vertices)),
|
||||||
|
*[
|
||||||
|
Transform(*pair)
|
||||||
|
for pair in zip(branches,gs.spanning_tree.split())
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FinalSum(Scene):
|
class FinalSum(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -723,9 +723,9 @@ class DefiningGraph(GraphScene):
|
|||||||
|
|
||||||
#Move to new graph
|
#Move to new graph
|
||||||
# new_graph = deepcopy(self.graph)
|
# new_graph = deepcopy(self.graph)
|
||||||
# new_graph["vertices"] = [
|
# new_graph.vertices = [
|
||||||
# (v[0] + 3*random(), v[1] + 3*random(), 0)
|
# (v[0] + 3*random(), v[1] + 3*random(), 0)
|
||||||
# for v in new_graph["vertices"]
|
# for v in new_graph.vertices
|
||||||
# ]
|
# ]
|
||||||
# new_graph_scene = GraphScene(new_graph)
|
# new_graph_scene = GraphScene(new_graph)
|
||||||
# self.animate(*[
|
# self.animate(*[
|
||||||
@ -739,7 +739,7 @@ class IntersectCubeGraphEdges(GraphScene):
|
|||||||
def args_to_string(*args):
|
def args_to_string(*args):
|
||||||
return ""
|
return ""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
GraphScene.__init__(self, CUBE_GRAPH, *args, **kwargs)
|
GraphScene.__init__(self, CubeGraph(), *args, **kwargs)
|
||||||
self.remove(self.edges[0], self.edges[4])
|
self.remove(self.edges[0], self.edges[4])
|
||||||
self.animate(*[
|
self.animate(*[
|
||||||
Transform(
|
Transform(
|
||||||
@ -803,7 +803,7 @@ class EulersFormula(GraphScene):
|
|||||||
).highlight("red")
|
).highlight("red")
|
||||||
for e in self.edges
|
for e in self.edges
|
||||||
for start, end, midpoint in [
|
for start, end, midpoint in [
|
||||||
(e.start, e.end, (e.start + e.end)/2)
|
(e.start, e.end, (e.start + e.end)/2.0)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
frame_time = 0.3
|
frame_time = 0.3
|
||||||
@ -811,7 +811,7 @@ class EulersFormula(GraphScene):
|
|||||||
self.generate_regions()
|
self.generate_regions()
|
||||||
parameters = [
|
parameters = [
|
||||||
(colored_dots, "V", "mobject", "-", "show"),
|
(colored_dots, "V", "mobject", "-", "show"),
|
||||||
(colored_edges, "E", "mobject", "+", "show_creation"),
|
(colored_edges, "E", "mobject", "+", "show"),
|
||||||
(self.regions, "F", "region", "=2", "show_all")
|
(self.regions, "F", "region", "=2", "show_all")
|
||||||
]
|
]
|
||||||
for items, letter, item_type, symbol, mode in parameters:
|
for items, letter, item_type, symbol, mode in parameters:
|
||||||
@ -877,7 +877,7 @@ class ShowMoserGraphLines(CircleScene):
|
|||||||
radians = list(set(map(lambda x : x%(2*np.pi), radians)))
|
radians = list(set(map(lambda x : x%(2*np.pi), radians)))
|
||||||
radians.sort()
|
radians.sort()
|
||||||
CircleScene.__init__(self, radians, *args, **kwargs)
|
CircleScene.__init__(self, radians, *args, **kwargs)
|
||||||
n, plus_n_choose_4 = tex_mobjects(["n", r"+{n \choose 4}"])
|
n, plus_n_choose_4 = tex_mobjects(["n", "+{n \\choose 4}"])
|
||||||
n_choose_2, plus_2_n_choose_4, plus_n = tex_mobjects([
|
n_choose_2, plus_2_n_choose_4, plus_n = tex_mobjects([
|
||||||
r"{n \choose 2}",r"&+2{n \choose 4}\\",r"&+n"
|
r"{n \choose 2}",r"&+2{n \choose 4}\\",r"&+n"
|
||||||
])
|
])
|
||||||
|
Reference in New Issue
Block a user