Finished Counting in Binary project

This commit is contained in:
Grant Sanderson
2015-09-18 14:22:59 -07:00
parent 2a49abc6f3
commit dfe29de518
4 changed files with 275 additions and 26 deletions

View File

@ -100,6 +100,18 @@ class CounterclockwiseTransform(Transform):
interpolation_function = counterclockwise_path, **kwargs interpolation_function = counterclockwise_path, **kwargs
) )
class SpinInFromNothing(Transform):
def __init__(self, mob, **kwargs):
name = "interpolation_function"
interp_func = kwargs[name] if name in kwargs else counterclockwise_path
dot = Point(mob.get_center(), color = "black")
Transform.__init__(
self, dot, mob,
interpolation_function = interp_func,
**kwargs
)
class FadeToColor(Transform): class FadeToColor(Transform):
def __init__(self, mobject, color, *args, **kwargs): def __init__(self, mobject, color, *args, **kwargs):
target = copy.deepcopy(mobject).highlight(color) target = copy.deepcopy(mobject).highlight(color)

View File

@ -132,7 +132,7 @@ class Mobject(object):
Direction just needs to be a vector pointing towards side or Direction just needs to be a vector pointing towards side or
corner in the 2d plane. corner in the 2d plane.
""" """
shift_val = np.array(ORIGIN) shift_val = np.zeros(3)
space_dim = (SPACE_WIDTH, SPACE_HEIGHT) space_dim = (SPACE_WIDTH, SPACE_HEIGHT)
for i in [0, 1]: for i in [0, 1]:
if direction[i] == 0: if direction[i] == 0:

View File

@ -71,24 +71,41 @@ COUNT_TO_FRAME_NUM = {
30 : 1184, 30 : 1184,
31 : 1219, 31 : 1219,
} }
COUNT_TO_TIP_POS = {
0 : [5.0, 0.5, 0.0],
1 : [3.1, 2.5, 0.0],
2 : [1.5, 2.3, 0.0],
3 : [0.7, 2.0, 0.0],
4 : [0.0, 1.0, 0.0],
}
def finger_tip_power_of_2(finger_no):
return tex_mobject(str(2**finger_no)).shift(COUNT_TO_TIP_POS[finger_no])
class Hand(ImageMobject): class Hand(ImageMobject):
def __init__(self, num, **kwargs): STARTING_BOTTOM_RIGHT = [4.61111111e+00, -3.98888889e+00, 9.80454690e-16]
def __init__(self, num, small = False, **kwargs):
Mobject2D.__init__(self, **kwargs) Mobject2D.__init__(self, **kwargs)
path = os.path.join( path = os.path.join(
MOVIE_DIR, MOVIE_PREFIX, "images", "Hand%d.png"%num MOVIE_DIR, MOVIE_PREFIX, "images", "Hand%d.png"%num
) )
invert = False invert = False
if self.read_in_cached_attrs(path, invert): if not self.read_in_cached_attrs(path, invert):
return ImageMobject.__init__(self, path, invert = invert)
ImageMobject.__init__(self, path, invert = invert) center = self.get_center()
center = self.get_center() self.center()
self.center() self.rotate(np.pi, axis = RIGHT+UP)
self.rotate(np.pi, axis = RIGHT+UP) self.sort_points(lambda p : np.log(complex(*p[:2])).imag)
self.sort_points(lambda p : np.log(complex(*p[:2])).imag) self.rotate(np.pi, axis = RIGHT+UP)
self.rotate(np.pi, axis = RIGHT+UP) self.shift(center)
self.shift(center) self.cache_attrs(path, invert = False)
self.cache_attrs(path, invert = False) self.shift(self.STARTING_BOTTOM_RIGHT-self.get_boundary_point(DOWN+RIGHT))
if small:
self.shrink()
def shrink(self):
self.scale_in_place(0.8).to_edge(DOWN, buff = 0.0)
# def highlight_thumb(self, color = "yellow"): # def highlight_thumb(self, color = "yellow"):
# self.highlight( # self.highlight(
@ -120,7 +137,7 @@ class LeftHand(Hand):
**kwargs **kwargs
) )
self.rotate(np.pi, UP) self.rotate(np.pi, UP)
self.to_edge(LEFT) self.shift(LEFT)
def get_hand_map(which_hand = "right"): def get_hand_map(which_hand = "right"):
if which_hand == "right": if which_hand == "right":
@ -131,7 +148,7 @@ def get_hand_map(which_hand = "right"):
print "Bad arg, bro" print "Bad arg, bro"
return return
return dict([ return dict([
(num, Class(num)) (num, Class(num, small=True))
for num in range(32) for num in range(32)
]) ])
@ -194,13 +211,6 @@ class CountTo1023(Scene):
def construct(self): def construct(self):
rh_map = get_hand_map("right") rh_map = get_hand_map("right")
lh_map = get_hand_map("left") lh_map = get_hand_map("left")
for mob in rh_map.values()+lh_map.values():
mob.scale(0.9)
mob.to_edge(DOWN, buff = 0)
for mob in rh_map.values():
mob.to_edge(RIGHT)
for mob in lh_map.values():
mob.to_edge(LEFT)
def get_num(count): def get_num(count):
return CompoundMobject(*[ return CompoundMobject(*[
tex_mobject(char).shift(0.35*x*RIGHT) tex_mobject(char).shift(0.35*x*RIGHT)
@ -228,19 +238,244 @@ class Introduction(Scene):
self.animate(DelayByOrder(Transform(words, hand))) self.animate(DelayByOrder(Transform(words, hand)))
self.dither() self.dither()
class ShowReadingRule(Scene): class ShowReadingRule(Scene):
def construct(self): def construct(self):
pass sample_counts = [6, 17, 27, 31]
question = text_mobject("""
How do you recognize what number a given configuration represents?
""", size = "\\Huge").scale(0.75).to_corner(UP+LEFT)
answer = text_mobject([
"Think of each finger as representing a power of 2, ",
"then add up the numbers represented by the standing fingers."
], size = "\\Huge").scale(0.75).to_corner(UP+LEFT).split()
self.add(question)
for count in sample_counts:
hand = Hand(count, small = True)
self.add(hand)
self.dither()
self.remove(hand)
self.add(hand)
self.dither()
self.remove(question)
self.add(answer[0])
counts = map(finger_tip_power_of_2, range(5))
for count in counts:
self.animate(SpinInFromNothing(count, run_time = 0.3))
self.dither()
self.animate(ShimmerIn(answer[1]))
for count in sample_counts:
self.clear()
self.add(*answer)
self.read_sample(count)
def read_sample(self, num):
hand = Hand(num, small = True)
bool_arr = [c == '1' for c in five_char_binary(num)]
counts = [4-count for count in range(5) if bool_arr[count]]
count_mobs = map(finger_tip_power_of_2, counts)
if num in [6, 27]:
count_mobs[1].shift(0.2*DOWN + 0.2*LEFT)
if num in [6, 17]:
hand.shift(0.8*LEFT)
sum_mobs = tex_mobject(
" + ".join([str(2**c) for c in counts]).split(" ") + ["=%d"%num]
).to_corner(UP+RIGHT).split()
self.add(hand, *count_mobs)
self.dither()
self.animate(*[
Transform(count_mobs[n/2], sum_mobs[n])
if n%2 == 0 and n/2 < len(counts)
else FadeIn(sum_mobs[n])
for n in range(len(sum_mobs))
])
self.dither(2.0)
class CountWithReadingRule(ShowCounting):
def get_counting_mob(self, count):
pass
class ShowIncrementRule(Scene): class ShowIncrementRule(Scene):
def construct(self): def construct(self):
pass #First count from 18 to 22
def to_left(words):
return "\\begin{flushleft}" + words + "\\end{flushleft}"
phrases = [
text_mobject(to_left(words), size = "\\Huge").scale(0.75).to_corner(UP+LEFT)
for words in [
"But while you're counting, you don't need to think about powers of 2.",
"Can you see the pattern for incrementing?",
"If the thumb is down, turn it up.",
"If the thumb is up, but the forefinger is down, flip them both.",
"If the thumb and forefinger are up, but the middle finger is down, flip all three.",
"In general, flip all of the fingers up to the rightmost one which is down.",
"After practicing for a minute or two, you're mind starts doing it automatically.",
"Question: Why does this rule for incrementation work?",
]
]
ranges = [
(0, 14, False),
(14, 28, False),
(12, 13, True),
(29, 31, True),
(27, 29, True),
(23, 24, True),
(14, 20, False),
(20, 26, False)
]
oh = OverHand()
for phrase, (start, end, pause) in zip(phrases, ranges):
if pause:
self.background = oh.frames[COUNT_TO_FRAME_NUM[start]]
self.add(phrase)
self.animate(ShimmerIn(self.get_arrow_set(start)))
self.dither()
self.clear()
self.reset_background()
self.frames += [
disp.paint_mobject(phrase, frame)
for frame in oh.frames[COUNT_TO_FRAME_NUM[start]:COUNT_TO_FRAME_NUM[end]]
]
if pause:
self.frames += [self.frames[-1]]*int(1.0/self.frame_duration)
def get_arrow_set(self, num):
arrow = tex_mobject("\\downarrow", size = "\\Huge")
arrow.highlight("green")
arrow.shift(-arrow.get_bottom())
if num == 12:
tips = [(4, 1, 0)]
elif num == 29:
tips = [
(6, 1.5, 0),
(3, 1.5, 0),
]
elif num == 27:
tips = [
(5.5, 1.5, 0),
(2.75, 3.5, 0),
(2, 1.0, 0),
]
elif num == 23:
tips = [
(6, 1, 0),
(3.25, 3.5, 0),
(2.25, 3.5, 0),
(1.5, 0.75, 0),
]
return CompoundMobject(*[
deepcopy(arrow).shift(tip)
for tip in tips
])
class MindFindsShortcuts(Scene):
def construct(self):
words1 = text_mobject("""
Before long, your mind starts to recognize certain
patterns without needing to perform the addition.
""", size = "\\Huge").scale(0.75).to_corner(LEFT+UP)
words2 = text_mobject("""
Which makes it faster to recognize other patterns...
""", size = "\\Huge").scale(0.75).to_corner(LEFT+UP)
hand = Hand(7).scale(0.5).center().shift(DOWN+2*LEFT)
sum421 = tex_mobject("4+2+1").shift(DOWN+2*RIGHT)
seven = tex_mobject("7").shift(DOWN+6*RIGHT)
compound = CompoundMobject(
Arrow(hand, sum421),
sum421,
Arrow(sum421, seven)
)
self.add(
words1,
hand,
compound,
seven
)
self.dither()
self.animate(
Transform(compound, Arrow(hand, seven).highlight("yellow")),
ShimmerIn(text_mobject("Directly recognize").shift(1.5*DOWN+2*RIGHT))
)
self.dither()
self.clear()
hands = dict([
(num, Hand(num).center().scale(0.5).shift(DOWN))
for num in [23, 16, 7]
])
hands[23].shift(5*LEFT)
hands[16].shift(LEFT)
hands[7].shift(3*RIGHT)
for num in 7, 16:
hands[num].add(tex_mobject(str(num)).shift(hands[num].get_top()+0.5*UP))
plus = tex_mobject("+").shift(DOWN + RIGHT)
equals = tex_mobject("=").shift(DOWN + 2.5*LEFT)
equals23 = tex_mobject("=23").shift(DOWN + 5.5*RIGHT)
self.add(words2, hands[23])
self.dither()
self.animate(
Transform(
deepcopy(hands[16]).highlight("black").center().shift(hands[23].get_center()),
hands[16]
),
Transform(
deepcopy(hands[7]).highlight("black").center().shift(hands[23].get_center()),
hands[7]
),
Animation(hands[23]),
FadeIn(equals),
FadeIn(plus)
)
self.dither()
self.animate(ShimmerIn(equals23))
self.dither()
class CountingExampleSentence(ShowCounting):
def construct(self):
words = "As an example, this is me counting the number of words in this sentence on just one hand!"
self.words = text_mobject(words.split(), size = "\\Huge").scale(0.7).to_corner(UP+LEFT, buff = 0.25).split()
ShowCounting.construct(self)
def get_counting_mob(self, num):
return CompoundMobject(*self.words[:num])
class FinishCountingExampleSentence(Scene):
def construct(self):
words = "As an example, this is me counting the number of words in this sentence on just one hand!"
words = text_mobject(words, size = "\\Huge").scale(0.7).to_corner(UP+LEFT, buff = 0.25)
hand = Hand(18)
sixteen = tex_mobject("16").shift([0, 2.25, 0])
two = tex_mobject("2").shift([3, 3.65, 0])
eightteen = tex_mobject("18").shift([1.5, 2.5, 0])
eightteen.sort_points()
comp = CompoundMobject(sixteen, two)
self.add(hand, comp, words)
self.dither()
self.animate(Transform(comp, eightteen))
self.dither()
class Question(Scene):
def construct(self):
self.add(text_mobject("Left to ponder: Why does this rule for incrementing work?"))
class TwoHandStatement(Scene):
def construct(self):
self.add(text_mobject(
"With 10 fingers, you can count up to $2^{10} - 1 = 1023$..."
))
class WithToes(Scene):
def construct(self):
words = text_mobject([
"If you were dexterous enough to use your toes as well,",
"you could count to 1,048,575"
]).split()
self.add(words[0])
self.dither()
self.animate(ShimmerIn(words[1]))
self.dither()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -21,8 +21,10 @@ class LogoGeneration(Scene):
SPHERE_DENSITY = 50 SPHERE_DENSITY = 50
SPHERE_BLUE = DARK_BLUE SPHERE_BLUE = DARK_BLUE
CIRCLE_SPHERE_INTERPOLATION = 0.3 CIRCLE_SPHERE_INTERPOLATION = 0.3
FRAME_DURATION = 0.01
def construct(self): def construct(self):
self.frame_duration = FRAME_DURATION
circle = Circle( circle = Circle(
density = self.CIRCLE_DENSITY, density = self.CIRCLE_DENSITY,
color = self.CIRCLE_BLUE color = self.CIRCLE_BLUE