Additive/multiplicative rules

This commit is contained in:
Grant Sanderson 2016-04-30 15:08:53 -07:00
parent dbb31980da
commit e5dc0db8ef
7 changed files with 320 additions and 33 deletions

View file

@ -130,19 +130,18 @@ class Homotopy(Animation):
class PhaseFlow(Animation): class PhaseFlow(Animation):
CONFIG = { CONFIG = {
"virtual_time" : 1 "virtual_time" : 1,
"rate_func" : None,
} }
def __init__(self, function, mobject, **kwargs): def __init__(self, function, mobject, **kwargs):
digest_config(self, kwargs, locals()) digest_config(self, kwargs, locals())
self.get_nudge_func = lambda alpha_diff : \
lambda point : point + alpha_diff*function(point)
Animation.__init__(self, mobject, **kwargs) Animation.__init__(self, mobject, **kwargs)
def update_mobject(self, alpha): def update_mobject(self, alpha):
if hasattr(self, "last_alpha"): if hasattr(self, "last_alpha"):
nudge = self.virtual_time*(alpha-self.last_alpha) dt = self.virtual_time*(alpha-self.last_alpha)
self.mobject.apply_function( self.mobject.apply_function(
self.get_nudge_func(nudge) lambda p : p + dt*self.function(p)
) )
self.last_alpha = alpha self.last_alpha = alpha
@ -160,19 +159,25 @@ class MoveAlongPath(Animation):
class ApplyToCenters(Animation): class ApplyToCenters(Animation):
def __init__(self, AnimationClass, mobjects, **kwargs): def __init__(self, AnimationClass, mobjects, **kwargs):
centers = [mob.get_center() for mob in mobjects] full_kwargs = AnimationClass.CONFIG
kwargs["mobject"] = Mobject().add_points(centers) full_kwargs.update(kwargs)
self.centers_container = AnimationClass(**kwargs) full_kwargs["mobject"] = Mobject(*[
kwargs.pop("mobject") mob.get_point_mobject()
Animation.__init__(self, Mobject(*mobjects), **kwargs) for mob in mobjects
])
self.centers_container = AnimationClass(**full_kwargs)
full_kwargs.pop("mobject")
Animation.__init__(self, Mobject(*mobjects), **full_kwargs)
self.name = str(self) + AnimationClass.__name__ self.name = str(self) + AnimationClass.__name__
def update_mobject(self, alpha): def update_mobject(self, alpha):
self.centers_container.update_mobject(alpha) self.centers_container.update_mobject(alpha)
points = self.centers_container.mobject.points center_mobs = self.centers_container.mobject.split()
mobjects = self.mobject.split() mobjects = self.mobject.split()
for point, mobject in zip(points, mobjects): for center_mob, mobject in zip(center_mobs, mobjects):
mobject.center().shift(point) mobject.shift(
center_mob.get_center()-mobject.get_center()
)

View file

@ -129,6 +129,8 @@ class Camera(object):
if len(points) == 0: if len(points) == 0:
continue continue
coords = self.points_to_pixel_coords(points) coords = self.points_to_pixel_coords(points)
if np.all(~self.on_screen_pixels(coords)):
return result
start = "M%d %d"%tuple(coords[0]) start = "M%d %d"%tuple(coords[0])
#(handle1, handle2, anchor) tripletes #(handle1, handle2, anchor) tripletes
triplets = zip(*[ triplets = zip(*[

View file

@ -1,7 +1,6 @@
import numpy as np import numpy as np
import itertools as it import itertools as it
from PIL import Image from PIL import Image
import cv2
from copy import deepcopy from copy import deepcopy
from mobject import Mobject from mobject import Mobject

View file

@ -87,9 +87,11 @@ class VMobject(Mobject):
def get_stroke_color(self): def get_stroke_color(self):
try: try:
self.stroke_rgb[self.stroke_rgb<0] = 0
self.stroke_rgb[self.stroke_rgb>1] = 1
return Color(rgb = self.stroke_rgb) return Color(rgb = self.stroke_rgb)
except: except:
return Color(rgb = 0.99*self.stroke_rgb) return Color(WHITE)
#TODO, get color? Specify if stroke or fill #TODO, get color? Specify if stroke or fill
#is the predominant color attribute? #is the predominant color attribute?
@ -192,6 +194,16 @@ class VMobject(Mobject):
self.submobjects self.submobjects
) )
def arrange_submobjects(self,
direction = RIGHT,
buff = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
center = True):
for m1, m2 in zip(self.submobjects, self.submobjects[1:]):
m2.next_to(m1, direction, buff = buff)
if center:
self.center()
return self
## Information about line ## Information about line
def component_curves(self): def component_curves(self):

View file

@ -56,10 +56,10 @@ class PiCreature(SVGMobject):
self.set_stroke(color = BLACK, width = self.stroke_width) self.set_stroke(color = BLACK, width = self.stroke_width)
if not self.parts_named: if not self.parts_named:
self.name_parts() self.name_parts()
self.mouth.set_fill(BLACK) self.mouth.set_fill(BLACK, opacity = 1)
self.body.set_fill(self.color) self.body.set_fill(self.color, opacity = 1)
self.pupils.set_fill(BLACK) self.pupils.set_fill(BLACK, opacity = 1)
self.eyes.set_fill(WHITE) self.eyes.set_fill(WHITE, opacity = 1)
return self return self

View file

@ -98,6 +98,9 @@ class UnitInterval(NumberLine):
class Axes(VMobject): class Axes(VMobject):
CONFIG = {
"propogate_style_to_family" : True
}
def generate_points(self, **kwargs): def generate_points(self, **kwargs):
self.x_axis = NumberLine(**kwargs) self.x_axis = NumberLine(**kwargs)
self.y_axis = NumberLine(**kwargs).rotate(np.pi/2) self.y_axis = NumberLine(**kwargs).rotate(np.pi/2)
@ -116,9 +119,10 @@ class NumberPlane(VMobject):
"x_line_frequency" : 1, "x_line_frequency" : 1,
"y_line_frequency" : 1, "y_line_frequency" : 1,
"secondary_line_ratio" : 1, "secondary_line_ratio" : 1,
"written_coordinate_height" : 0.5, "written_coordinate_height" : 0.2,
"written_coordinate_nudge" : 0.1*(DOWN+RIGHT), "written_coordinate_nudge" : 0.1*(DOWN+RIGHT),
"num_pair_at_center" : (0, 0), "num_pair_at_center" : (0, 0),
"propogate_style_to_family" : False,
} }
def generate_points(self): def generate_points(self):
@ -160,7 +164,7 @@ class NumberPlane(VMobject):
def init_colors(self): def init_colors(self):
VMobject.init_colors(self) VMobject.init_colors(self)
self.axes.set_stroke(self.axes_color) self.axes.set_stroke(self.axes_color)
# self.main_lines.set_stroke(self.color) self.main_lines.set_stroke(self.color)
self.secondary_lines.set_stroke(self.secondary_color, 1) self.secondary_lines.set_stroke(self.secondary_color, 1)
return self return self

View file

@ -82,7 +82,7 @@ def get_top_inverse(i, j):
class TOP(VMobject): class TOP(VMobject):
CONFIG = { CONFIG = {
"triangle_height_to_number_height" : 3, "triangle_height_to_number_height" : 3,
"offset_multiple" : 1.2, "offset_multiple" : 1.5,
"radius" : 1.5, "radius" : 1.5,
"propogate_style_to_family" : False, "propogate_style_to_family" : False,
} }
@ -121,7 +121,7 @@ class TOP(VMobject):
value = TexMobject(value) value = TexMobject(value)
if isinstance(value, TOP): if isinstance(value, TOP):
return self.put_top_on_vertix(index, value) return self.put_top_on_vertix(index, value)
value.scale_to_fit_height(self.get_value_height()) self.rescale_corner_mobject(value)
value.center() value.center()
if index == 0: if index == 0:
offset = -value.get_corner(UP+RIGHT) offset = -value.get_corner(UP+RIGHT)
@ -134,6 +134,26 @@ class TOP(VMobject):
value.shift(anchors[index]) value.shift(anchors[index])
return value return value
def put_top_on_vertix(self, index, top):
top.scale_to_fit_height(2*self.get_value_height())
vertices = top.get_vertices()
vertices[index] = 0
start = reduce(op.add, vertices)/2
end = self.triangle.get_anchors_and_handles()[0][index]
top.shift(end-start)
return top
def put_in_vertex(self, index, mobject):
self.rescale_corner_mobject(mobject)
mobject.center()
mobject.shift(interpolate(
self.get_center(),
self.get_vertices()[index],
0.7
))
return mobject
def get_surrounding_circle(self, color = YELLOW): def get_surrounding_circle(self, color = YELLOW):
return Circle( return Circle(
radius = 1.7*self.radius, radius = 1.7*self.radius,
@ -143,20 +163,20 @@ class TOP(VMobject):
(self.triangle.get_height()/6)*DOWN (self.triangle.get_height()/6)*DOWN
) )
def put_top_on_vertix(self, index, top): def rescale_corner_mobject(self, mobject):
top.scale_to_fit_height(2*self.get_value_height()) if mobject.get_height() > self.get_value_height():
top_anchors = list( mobject.scale_to_fit_height(self.get_value_height())
top.triangle.get_anchors_and_handles()[0][:3] return self
)
top_anchors[index] = 0
start = reduce(op.add, top_anchors)/2
end = self.triangle.get_anchors_and_handles()[0][index]
top.shift(end-start)
return top
def get_value_height(self): def get_value_height(self):
return self.triangle.get_height()/self.triangle_height_to_number_height return self.triangle.get_height()/self.triangle_height_to_number_height
def get_center(self):
return center_of_mass(self.get_vertices())
def get_vertices(self):
return self.triangle.get_anchors_and_handles()[0][:3]
def reset_submobjects(self): def reset_submobjects(self):
self.submobjects = [self.triangle] + self.values self.submobjects = [self.triangle] + self.values
return self return self
@ -501,6 +521,251 @@ class AdditiveProperty(Scene):
self.remove(copies) self.remove(copies)
class DrawInsideTriangle(Scene):
def construct(self):
top = TOP()
top.scale(2)
dot = top.put_in_vertex(0, Dot())
plus = top.put_in_vertex(1, TexMobject("+"))
times = top.put_in_vertex(2, TexMobject("\\times"))
plus.highlight(GREEN)
times.highlight(YELLOW)
self.add(top)
self.dither()
for mob in dot, plus, times:
self.play(Write(mob, run_time = 1))
self.dither()
class ConstantOnTop(Scene):
def construct(self):
top = TOP()
dot = top.put_in_vertex(1, Dot())
times1 = top.put_in_vertex(0, TexMobject("\\times"))
times2 = top.put_in_vertex(2, TexMobject("\\times"))
times1.highlight(YELLOW)
times2.highlight(YELLOW)
three = top.put_on_vertex(1, "3")
lower_left_x = top.put_on_vertex(0, "x")
lower_right_x = top.put_on_vertex(2, "x")
x_cubed = TexMobject("x^3").to_edge(UP)
x_cubed.submobjects.reverse() #To align better
cube_root_x = TexMobject("\\sqrt[3]{x}").to_edge(UP)
self.add(top)
self.play(ShowCreation(three))
self.play(
FadeIn(lower_left_x),
Write(x_cubed),
run_time = 1
)
self.dither()
self.play(*[
Transform(*pair, path_arc = np.pi)
for pair in [
(lower_left_x, lower_right_x),
(x_cubed, cube_root_x),
]
])
self.dither(2)
for mob in dot, times1, times2:
self.play(ShowCreation(mob))
self.dither()
def get_const_top_TOP(*args):
top = TOP(*args)
dot = top.put_in_vertex(1, Dot())
times1 = top.put_in_vertex(0, TexMobject("\\times"))
times2 = top.put_in_vertex(2, TexMobject("\\times"))
times1.highlight(YELLOW)
times2.highlight(YELLOW)
top.add(dot, times1, times2)
return top
class MultiplyWithConstantTop(Scene):
def construct(self):
top1 = get_const_top_TOP("x", "3")
top2 = get_const_top_TOP("y", "3")
top3 = get_const_top_TOP("xy", "3")
times = TexMobject("\\times")
equals = TexMobject("=")
top_exp_equation = VMobject(
top1, times, top2, equals, top3
)
top_exp_equation.arrange_submobjects()
old_style_exp = TexMobject("(x^3)(y^3) = (xy)^3")
old_style_exp.to_edge(UP)
old_style_exp.highlight(GREEN)
old_style_rad = TexMobject("\\sqrt[3]{x} \\sqrt[3]{y} = \\sqrt[3]{xy}")
old_style_rad.to_edge(UP)
old_style_rad.highlight(RED)
self.add(top_exp_equation, old_style_exp)
self.dither(3)
old_tops = [top1, top2, top3]
new_tops = []
for top in old_tops:
new_top = top.copy()
new_top.put_on_vertex(2, new_top.values[0])
new_top.shift(0.5*LEFT)
new_tops.append(new_top)
self.play(
Transform(old_style_exp, old_style_rad),
Transform(
VMobject(*old_tops),
VMobject(*new_tops),
path_arc = np.pi/2
)
)
self.dither(3)
class RightStaysConstantQ(Scene):
def construct(self):
top1, top2, top3 = old_tops = [
TOP(None, s, "8")
for s in "x", "y", TexMobject("x?y")
]
q_mark = TexMobject("?").scale(2)
equation = VMobject(
top1, q_mark, top2, TexMobject("="), top3
)
equation.arrange_submobjects(buff = 0.7)
symbols_at_top = VMobject(*[
top.values[1]
for top in top1, top2, top3
])
symbols_at_lower_right = VMobject(*[
top.put_on_vertex(0, top.values[1].copy())
for top in top1, top2, top3
])
old_style_eq1 = TexMobject("\\sqrt[x]{8} ? \\sqrt[y]{8} = \\sqrt[x?y]{8}")
old_style_eq1.highlight(BLUE)
old_style_eq2 = TexMobject("\\log_x(8) ? \\log_y(8) = \\log_{x?y}(8)")
old_style_eq2.highlight(YELLOW)
for eq in old_style_eq1, old_style_eq2:
eq.to_edge(UP)
randy = Randolph()
randy.to_corner()
bubble = ThoughtBubble().pin_to(randy)
bubble.add_content(TOP(None, None, "8"))
self.add(randy, bubble)
self.play(ApplyMethod(randy.change_mode, "pondering"))
self.dither(3)
triangle = bubble.content.triangle
eight = bubble.content.values[2]
bubble.clear()
self.play(
Transform(triangle, equation),
FadeOut(eight),
ApplyPointwiseFunction(
lambda p : (p+2*DOWN)*15/np.linalg.norm(p+2*DOWN),
bubble
),
FadeIn(old_style_eq1),
ApplyMethod(randy.shift, 3*DOWN + 3*LEFT),
run_time = 2
)
self.remove(triangle)
self.add(equation)
self.dither(4)
self.play(
Transform(
symbols_at_top, symbols_at_lower_right,
path_arc = np.pi/2
),
Transform(old_style_eq1, old_style_eq2)
)
self.dither(2)
class AOplusB(Scene):
def construct(self):
self.add(TexMobject(
"a \\oplus b = \\dfrac{1}{\\frac{1}{a} + \\frac{1}{b}}"
).scale(2))
self.dither()
class ConstantLowerRight(Scene):
def construct(self):
top = TOP()
times = top.put_in_vertex(0, TexMobject("\\times"))
times.highlight(YELLOW)
oplus = top.put_in_vertex(1, TexMobject("\\oplus"))
oplus.highlight(BLUE)
dot = top.put_in_vertex(2, Dot())
eight = top.put_on_vertex(2, TexMobject("8"))
self.add(top)
self.play(ShowCreation(eight))
for mob in dot, oplus, times:
self.play(ShowCreation(mob))
self.dither()
top.add(eight)
top.add(times, oplus, dot)
top1, top2, top3 = tops = [
top.copy() for i in range(3)
]
big_oplus = TexMobject("\\oplus").scale(2).highlight(BLUE)
equals = TexMobject("=")
equation = VMobject(
top1, big_oplus, top2, equals, top3
)
equation.arrange_submobjects()
top3.shift(0.5*RIGHT)
x, y, xy = [
t.put_on_vertex(0, s)
for t, s in zip(tops, ["x", "y", "xy"])
]
old_style_eq = TexMobject(
"\\dfrac{1}{\\frac{1}{\\log_x(8)} + \\frac{1}{\\log_y(8)}} = \\log_{xy}(8)"
)
old_style_eq.to_edge(UP).highlight(RED)
triple_top_copy = VMobject(*[
top.copy() for i in range(3)
])
self.clear()
self.play(
Transform(triple_top_copy, VMobject(*tops)),
FadeIn(VMobject(x, y, xy, big_oplus, equals))
)
self.remove(triple_top_copy)
self.add(*tops)
self.play(Write(old_style_eq))
self.dither(3)
syms = VMobject(x, y, xy)
new_syms = VMobject(*[
t.put_on_vertex(1, s)
for t, s in zip(tops, ["x", "y", "x \\oplus y"])
])
new_old_style_eq = TexMobject(
"\\sqrt[x]{8} \\sqrt[y]{8} = \\sqrt[X]{8}"
)
X = new_old_style_eq.split()[-4]
frac = TexMobject("\\frac{1}{\\frac{1}{x} + \\frac{1}{y}}")
frac.replace(X)
frac_lower_right = frac.get_corner(DOWN+RIGHT)
frac.scale(2)
frac.shift(frac_lower_right - frac.get_corner(DOWN+RIGHT))
new_old_style_eq.submobjects[-4] = frac
new_old_style_eq.to_edge(UP)
new_old_style_eq.highlight(RED)
big_times = TexMobject("\\times").highlight(YELLOW)
big_times.shift(big_oplus.get_center())
self.play(
Transform(old_style_eq, new_old_style_eq),
Transform(syms, new_syms, path_arc = np.pi/2),
Transform(big_oplus, big_times)
)
self.dither(4)
class Test(Scene): class Test(Scene):