From 9a8323cbb7d9cc6525d70b8f049fdb7559c2aef6 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 30 May 2020 16:49:57 -0700 Subject: [PATCH] Changes for recent videos --- from_3b1b/active/bayes/beta1.py | 30 + from_3b1b/active/ctracing.py | 754 +++++++++ .../active/diffyq/part2/fourier_series.py | 2 + from_3b1b/active/ldm.py | 1445 +++++++++++++++++ manimlib/for_3b1b_videos/common_scenes.py | 2 +- 5 files changed, 2232 insertions(+), 1 deletion(-) create mode 100644 from_3b1b/active/ctracing.py create mode 100644 from_3b1b/active/ldm.py diff --git a/from_3b1b/active/bayes/beta1.py b/from_3b1b/active/bayes/beta1.py index 0bb31cdc..2837cd2f 100644 --- a/from_3b1b/active/bayes/beta1.py +++ b/from_3b1b/active/bayes/beta1.py @@ -40,6 +40,36 @@ class Thumbnail1(Scene): self.add(text) +class AltThumbnail1(Scene): + def construct(self): + N = 20 + n_trials = 10000 + p = 0.7 + outcomes = (np.random.random((N, n_trials)) < p).sum(0) + counts = [] + for k in range(N + 1): + counts.append((outcomes == k).sum()) + + hist = Histogram( + counts, + y_max=0.3, + y_tick_freq=0.05, + y_axis_numbers_to_show=[10, 20, 30], + x_label_freq=10, + ) + hist.set_width(FRAME_WIDTH - 1) + hist.bars.set_submobject_colors_by_gradient(YELLOW, YELLOW, GREEN, BLUE) + hist.bars.set_stroke(WHITE, 2) + + title = TextMobject("Binomial distribution") + title.set_width(12) + title.to_corner(UR, buff=0.8) + title.add_background_rectangle() + + self.add(hist) + self.add(title) + + class Thumbnail2(Scene): def construct(self): axes = self.get_axes() diff --git a/from_3b1b/active/ctracing.py b/from_3b1b/active/ctracing.py new file mode 100644 index 00000000..416975f3 --- /dev/null +++ b/from_3b1b/active/ctracing.py @@ -0,0 +1,754 @@ +from manimlib.imports import * +from from_3b1b.active.sir import * + + +class LastFewMonths(Scene): + def construct(self): + words = TextMobject("Last ", "few\\\\", "months:") + words.set_height(4) + underlines = VGroup() + for word in words: + underline = Line(LEFT, RIGHT) + underline.match_width(word) + underline.next_to(word, DOWN, SMALL_BUFF) + underlines.add(underline) + underlines[0].stretch(1.4, 0, about_edge=LEFT) + underlines.set_color(BLUE) + + # self.play(ShowCreation(underlines)) + self.play(ShowIncreasingSubsets(words, run_time=0.75, rate_func=linear)) + self.wait() + + +class UnemploymentTitle(Scene): + def construct(self): + words = TextMobject("Unemployment claims\\\\per week in the US")[0] + words.set_width(FRAME_WIDTH - 1) + words.to_edge(UP) + arrow = Arrow( + words.get_bottom(), + words.get_bottom() + 3 * RIGHT + 3 * DOWN, + stroke_width=10, + tip_length=0.5, + ) + arrow.set_color(BLUE_E) + words.set_color(BLACK) + self.play( + ShowIncreasingSubsets(words), + ShowCreation(arrow), + ) + self.wait() + + +class ExplainTracing(Scene): + def construct(self): + # Words + words = VGroup( + TextMobject("Testing, ", "Testing, ", "Testing!"), + TextMobject("Contact Tracing"), + ) + words[0].set_color(GREEN) + words[1].set_color(BLUE_B) + words.set_width(FRAME_WIDTH - 2) + words.arrange(DOWN, buff=1) + + self.play(ShowIncreasingSubsets(words[0], rate_func=linear)) + self.wait() + self.play(Write(words[1], run_time=1)) + self.wait() + + self.play( + words[1].to_edge, UP, + FadeOutAndShift(words[0], 6 * UP) + ) + + ct_word = words[1][0] + + # Groups + clusters = VGroup() + for x in range(4): + cluster = VGroup() + for y in range(4): + cluster.add(Randolph()) + cluster.arrange_in_grid(buff=LARGE_BUFF) + clusters.add(cluster) + clusters.scale(0.5) + clusters.arrange_in_grid(buff=2) + clusters.set_height(4) + + self.play(FadeIn(clusters)) + + pis = VGroup() + boxes = VGroup() + for cluster in clusters: + for pi in cluster: + pis.add(pi) + box = SurroundingRectangle(pi, buff=0.05) + boxes.add(box) + pi.box = box + + boxes.set_stroke(WHITE, 1) + + sicky = clusters[0][2] + covid_words = TextMobject("COVID-19\\\\Positive!") + covid_words.set_color(RED) + arrow = Vector(RIGHT, color=RED) + arrow.next_to(sicky, LEFT) + covid_words.next_to(arrow, LEFT, SMALL_BUFF) + + self.play( + sicky.change, "sick", + sicky.set_color, "#9BBD37", + FadeInFrom(covid_words, RIGHT), + GrowArrow(arrow), + ) + self.play(ShowCreation(sicky.box)) + self.wait(2) + anims = [] + for pi in clusters[0]: + if pi is not sicky: + anims.append(ApplyMethod(pi.change, "tired")) + anims.append(ShowCreation(pi.box)) + self.play(*anims) + self.wait() + + self.play(VFadeIn( + boxes[4:], + run_time=2, + rate_func=there_and_back_with_pause, + )) + self.wait() + + self.play(FadeOut( + VGroup( + covid_words, + arrow, + *boxes[:4], + *pis, + ), + lag_ratio=0.1, + run_time=3, + )) + self.play(ct_word.move_to, 2 * UP) + + # Underlines + implies = TexMobject("\\Downarrow") + implies.scale(2) + implies.next_to(ct_word, DOWN, MED_LARGE_BUFF) + loc_tracking = TextMobject("Location Tracking") + loc_tracking.set_color(GREY_BROWN) + loc_tracking.match_height(ct_word) + loc_tracking.next_to(implies, DOWN, MED_LARGE_BUFF) + + q_marks = TexMobject("???") + q_marks.scale(2) + q_marks.next_to(implies, RIGHT) + + cross = Cross(implies) + cross.set_stroke(RED, 7) + + self.play( + Write(implies), + FadeInFrom(loc_tracking, UP) + ) + self.play(FadeIn(q_marks, lag_ratio=0.1)) + self.wait() + + parts = VGroup(ct_word[:7], ct_word[7:]) + lines = VGroup() + for part in parts: + line = Line(part.get_left(), part.get_right()) + line.align_to(part[0], DOWN) + line.shift(0.1 * DOWN) + lines.add(line) + + ct_word.set_stroke(BLACK, 2, background=True) + self.add(lines[1], ct_word) + self.play(ShowCreation(lines[1])) + self.wait() + self.play(ShowCreation(lines[0])) + self.wait() + + self.play( + ShowCreation(cross), + FadeOutAndShift(q_marks, RIGHT), + FadeOut(lines), + ) + self.wait() + + dp_3t = TextMobject("DP-3T") + dp_3t.match_height(ct_word) + dp_3t.move_to(loc_tracking) + dp_3t_long = TextMobject("Decentralized Privacy-Preserving Proximity Tracing") + dp_3t_long.next_to(dp_3t, DOWN, LARGE_BUFF) + + arrow = Vector(UP) + arrow.set_stroke(width=8) + arrow.move_to(implies) + + self.play( + FadeInFromDown(dp_3t), + FadeOut(loc_tracking), + FadeOut(implies), + FadeOut(cross), + ShowCreation(arrow) + ) + self.play(Write(dp_3t_long)) + self.wait() + + +class ContactTracingMisnomer(Scene): + def construct(self): + # Word play + words = TextMobject("Contact ", "Tracing") + words.scale(2) + rects = VGroup(*[ + SurroundingRectangle(word, buff=0.2) + for word in words + ]) + expl1 = TextMobject("Doesn't ``trace'' you...") + expl2 = TextMobject("...or your contacts") + expls = VGroup(expl1, expl2) + colors = [RED, BLUE] + + self.add(words) + for vect, rect, expl, color in zip([UP, DOWN], reversed(rects), expls, colors): + arrow = Vector(-vect) + arrow.next_to(rect, vect, SMALL_BUFF) + expl.next_to(arrow, vect, SMALL_BUFF) + rect.set_color(color) + arrow.set_color(color) + expl.set_color(color) + + self.play( + FadeInFrom(expl, -vect), + GrowArrow(arrow), + ShowCreation(rect), + ) + self.wait() + + self.play(Write( + VGroup(*self.mobjects), + rate_func=lambda t: smooth(1 - t), + run_time=3, + )) + + +class ContactTracingWords(Scene): + def construct(self): + words = TextMobject("Contact\\\\", "Tracing") + words.set_height(4) + for word in words: + self.add(word) + self.wait() + self.wait() + return + self.play(ShowIncreasingSubsets(words)) + self.wait() + self.play( + words.set_height, 1, + words.to_corner, UL, + ) + self.wait() + + +class WanderingDotsWithLines(Scene): + def construct(self): + sim = SIRSimulation( + city_population=20, + person_type=DotPerson, + person_config={ + "color_map": { + "S": GREY, + "I": GREY, + "R": GREY, + }, + "infection_ring_style": { + "stroke_color": YELLOW, + }, + "max_speed": 0.5, + }, + infection_time=100, + ) + + for person in sim.people: + person.set_status("S") + person.infection_start_time += random.random() + + lines = VGroup() + + max_dist = 1.25 + + def update_lines(lines): + lines.remove(*lines.submobjects) + for p1 in sim.people: + for p2 in sim.people: + if p1 is p2: + continue + dist = get_norm(p1.get_center() - p2.get_center()) + if dist < max_dist: + line = Line(p1.get_center(), p2.get_center()) + alpha = (max_dist - dist) / max_dist + line.set_stroke( + interpolate_color(WHITE, RED, alpha), + width=4 * alpha + ) + lines.add(line) + + lines.add_updater(update_lines) + + self.add(lines) + self.add(sim) + self.wait(10) + for person in sim.people: + person.set_status("I") + person.infection_start_time += random.random() + self.wait(50) + + +class WhatAboutPeopleWithoutPhones(TeacherStudentsScene): + def construct(self): + self.student_says( + "What about people\\\\without phones?", + target_mode="sassy", + added_anims=[self.teacher.change, "guilty"] + ) + self.change_student_modes("angry", "angry", "sassy") + self.wait() + self.play(self.teacher.change, "tease") + self.wait() + + words = VectorizedPoint() + words.scale(1.5) + words.to_corner(UL) + + self.play( + FadeInFromDown(words), + RemovePiCreatureBubble(self.students[2]), + *[ + ApplyMethod(pi.change, "pondering", words) + for pi in self.pi_creatures + ] + ) + self.wait(5) + + +class PiGesture1(Scene): + def construct(self): + randy = Randolph(mode="raise_right_hand", height=2) + bubble = randy.get_bubble( + bubble_class=SpeechBubble, + height=2, width=3, + ) + bubble.write("This one's\\\\great") + bubble.content.scale(0.8) + bubble.content.set_color(BLACK) + bubble.set_color(BLACK) + bubble.set_fill(opacity=0) + randy.set_stroke(BLACK, 5, background=True) + self.add(randy, bubble, bubble.content) + + +class PiGesture2(Scene): + def construct(self): + randy = Randolph(mode="raise_left_hand", height=2) + randy.look(UL) + # randy.flip() + randy.set_color(GREY_BROWN) + bubble = randy.get_bubble( + bubble_class=SpeechBubble, + height=2, width=3, + direction=LEFT, + ) + bubble.write("So is\\\\this one") + bubble.content.scale(0.8) + bubble.content.set_color(BLACK) + bubble.set_color(BLACK) + bubble.set_fill(opacity=0) + randy.set_stroke(BLACK, 5, background=True) + self.add(randy, bubble, bubble.content) + + +class PiGesture3(Scene): + def construct(self): + randy = Randolph(mode="hooray", height=2) + randy.flip() + bubble = randy.get_bubble( + bubble_class=SpeechBubble, + height=2, width=3, + direction=LEFT, + ) + bubble.write("And this\\\\one") + bubble.content.scale(0.8) + bubble.content.set_color(BLACK) + bubble.set_color(BLACK) + bubble.set_fill(opacity=0) + randy.set_stroke(BLACK, 5, background=True) + self.add(randy, bubble, bubble.content) + + +class AppleGoogleCoop(Scene): + def construct(self): + logos = Group( + self.get_apple_logo(), + self.get_google_logo(), + ) + for logo in logos: + logo.set_height(2) + apple, google = logos + + logos.arrange(RIGHT, buff=3) + + arrows = VGroup() + for vect, u in zip([UP, DOWN], [0, 1]): + m1, m2 = logos[u], logos[1 - u] + arrows.add(Arrow( + m1.get_edge_center(vect), + m2.get_edge_center(vect), + path_arc=-90 * DEGREES, + buff=MED_LARGE_BUFF, + stroke_width=10, + )) + + self.play(LaggedStart( + Write(apple), + FadeIn(google), + lag_ratio=0.7, + )) + self.wait() + self.play(ShowCreation(arrows, run_time=2)) + self.wait() + + def get_apple_logo(self): + result = SVGMobject("apple_logo") + result.set_color("#b3b3b3") + return result + + def get_google_logo(self): + result = ImageMobject("google_logo_black") + return result + + +class LocationTracking(Scene): + def construct(self): + question = TextMobject( + "Would you like this company to track\\\\", + "and occasionally sell your location?" + ) + question.to_edge(UP, buff=LARGE_BUFF) + + slider = Rectangle(width=1.25, height=0.5) + slider.round_corners(radius=0.25) + slider.set_fill(GREEN, 1) + slider.next_to(question, DOWN, buff=MED_LARGE_BUFF) + + dot = Dot(radius=0.25) + dot.set_fill(GREY_C, 1) + dot.set_stroke(WHITE, 3) + dot.move_to(slider, RIGHT) + + morty = Mortimer() + morty.next_to(slider, RIGHT) + morty.to_edge(DOWN) + + bubble = morty.get_bubble( + height=2, + width=3, + direction=LEFT, + ) + + answer = TextMobject("Um...", "no.") + answer.set_height(0.4) + answer.set_color(YELLOW) + bubble.add_content(answer) + + self.add(morty) + + self.play( + FadeInFromDown(question), + Write(slider), + FadeIn(dot), + ) + self.play(morty.change, "confused", slider) + self.play(Blink(morty)) + self.play( + FadeIn(bubble), + Write(answer[0]), + ) + self.wait() + self.play( + dot.move_to, slider, LEFT, + slider.set_fill, {"opacity": 0}, + FadeIn(answer[1]), + morty.change, "sassy" + ) + self.play(Blink(morty)) + self.wait(2) + self.play(Blink(morty)) + self.wait(2) + + +class MoreLinks(Scene): + def construct(self): + words = TextMobject("See more links\\\\in the description.") + words.scale(2) + words.to_edge(UP, buff=2) + arrows = VGroup(*[ + Vector(1.5 * DOWN, stroke_width=10) + for x in range(4) + ]) + arrows.arrange(RIGHT, buff=0.75) + arrows.next_to(words, DOWN, buff=0.5) + for arrow, color in zip(arrows, [BLUE_D, BLUE_C, BLUE_E, GREY_BROWN]): + arrow.set_color(color) + self.play(Write(words)) + self.play(LaggedStartMap(ShowCreation, arrows)) + self.wait() + + +class LDMEndScreen(PatreonEndScreen): + CONFIG = { + "scroll_time": 20, + "specific_patrons": [ + "1stViewMaths", + "Aaron", + "Adam Dřínek", + "Adam Margulies", + "Aidan Shenkman", + "Alan Stein", + "Albin Egasse", + "Alex Mijalis", + "Alexander Mai", + "Alexis Olson", + "Ali Yahya", + "Andreas Snekloth Kongsgaard", + "Andrew Busey", + "Andrew Cary", + "Andrew R. Whalley", + "Aravind C V", + "Arjun Chakroborty", + "Arthur Zey", + "Ashwin Siddarth", + "Augustine Lim", + "Austin Goodman", + "Avi Finkel", + "Awoo", + "Axel Ericsson", + "Ayan Doss", + "AZsorcerer", + "Barry Fam", + "Bartosz Burclaf", + "Ben Delo", + "Benjamin Bailey", + "Bernd Sing", + "Bill Gatliff", + "Boris Veselinovich", + "Bradley Pirtle", + "Brandon Huang", + "Brendan Shah", + "Brian Cloutier", + "Brian Staroselsky", + "Britt Selvitelle", + "Britton Finley", + "Burt Humburg", + "Calvin Lin", + "Carl-Johan R. Nordangård", + "Charles Southerland", + "Charlie N", + "Chris Connett", + "Chris Druta", + "Christian Kaiser", + "cinterloper", + "Clark Gaebel", + "Colwyn Fritze-Moor", + "Corey Ogburn", + "D. Sivakumar", + "Dan Herbatschek", + "Daniel Brown", + "Daniel Herrera C", + "Darrell Thomas", + "Dave B", + "Dave Cole", + "Dave Kester", + "dave nicponski", + "David B. Hill", + "David Clark", + "David Gow", + "Delton Ding", + "Dominik Wagner", + "Eduardo Rodriguez", + "Emilio Mendoza", + "emptymachine", + "Eric Younge", + "Eryq Ouithaqueue", + "Federico Lebron", + "Fernando Via Canel", + "Frank R. Brown, Jr.", + "gary", + "Giovanni Filippi", + "Goodwine", + "Hal Hildebrand", + "Heptonion", + "Hitoshi Yamauchi", + "Isaac Gubernick", + "Ivan Sorokin", + "Jacob Baxter", + "Jacob Harmon", + "Jacob Hartmann", + "Jacob Magnuson", + "Jalex Stark", + "Jameel Syed", + "James Beall", + "Jason Hise", + "Jayne Gabriele", + "Jean-Manuel Izaret", + "Jeff Dodds", + "Jeff Linse", + "Jeff Straathof", + "Jeffrey Wolberg", + "Jimmy Yang", + "Joe Pregracke", + "Johan Auster", + "John C. Vesey", + "John Camp", + "John Haley", + "John Le", + "John Luttig", + "John Rizzo", + "John V Wertheim", + "jonas.app", + "Jonathan Heckerman", + "Jonathan Wilson", + "Joseph John Cox", + "Joseph Kelly", + "Josh Kinnear", + "Joshua Claeys", + "Joshua Ouellette", + "Juan Benet", + "Julien Dubois", + "Kai-Siang Ang", + "Kanan Gill", + "Karl Niu", + "Kartik Cating-Subramanian", + "Kaustuv DeBiswas", + "Killian McGuinness", + "kkm", + "Klaas Moerman", + "Kristoffer Börebäck", + "Kros Dai", + "L0j1k", + "Lael S Costa", + "LAI Oscar", + "Lambda GPU Workstations", + "Laura Gast", + "Lee Redden", + "Linh Tran", + "Luc Ritchie", + "Ludwig Schubert", + "Lukas Biewald", + "Lukas Zenick", + "Magister Mugit", + "Magnus Dahlström", + "Magnus Hiie", + "Manoj Rewatkar - RITEK SOLUTIONS", + "Mark B Bahu", + "Mark Heising", + "Mark Hopkins", + "Mark Mann", + "Martin Price", + "Mathias Jansson", + "Matt Godbolt", + "Matt Langford", + "Matt Roveto", + "Matt Russell", + "Matteo Delabre", + "Matthew Bouchard", + "Matthew Cocke", + "Maxim Nitsche", + "Michael Bos", + "Michael Hardel", + "Michael W White", + "Mirik Gogri", + "Molly Mackinlay", + "Mustafa Mahdi", + "Márton Vaitkus", + "Nero Li", + "Nicholas Cahill", + "Nikita Lesnikov", + "Nitu Kitchloo", + "Oleg Leonov", + "Oliver Steele", + "Omar Zrien", + "Omer Tuchfeld", + "Patrick Gibson", + "Patrick Lucas", + "Pavel Dubov", + "Pesho Ivanov", + "Petar Veličković", + "Peter Ehrnstrom", + "Peter Francis", + "Peter Mcinerney", + "Pierre Lancien", + "Pradeep Gollakota", + "Rafael Bove Barrios", + "Raghavendra Kotikalapudi", + "Randy C. Will", + "rehmi post", + "Rex Godby", + "Ripta Pasay", + "Rish Kundalia", + "Roman Sergeychik", + "Roobie", + "Ryan Atallah", + "Samuel Judge", + "SansWord Huang", + "Scott Gray", + "Scott Walter, Ph.D.", + "soekul", + "Solara570", + "Spyridon Michalakis", + "Stephen Shanahan", + "Steve Huynh", + "Steve Muench", + "Steve Sperandeo", + "Steven Siddals", + "Stevie Metke", + "Sundar Subbarayan", + "supershabam", + "Suteerth Vishnu", + "Suthen Thomas", + "Tal Einav", + "Taras Bobrovytsky", + "Tauba Auerbach", + "Ted Suzman", + "Terry Hayes", + "THIS IS THE point OF NO RE tUUurRrhghgGHhhnnn", + "Thomas J Sargent", + "Thomas Tarler", + "Tianyu Ge", + "Tihan Seale", + "Tim Erbes", + "Tim Kazik", + "Tomasz Legutko", + "Tyler Herrmann", + "Tyler Parcell", + "Tyler VanValkenburg", + "Tyler Veness", + "Ubiquity Ventures", + "Vassili Philippov", + "Vasu Dubey", + "Veritasium", + "Vignesh Ganapathi Subramanian", + "Vinicius Reis", + "Vladimir Solomatin", + "Wooyong Ee", + "Xuanji Li", + "Yana Chernobilsky", + "Yavor Ivanov", + "Yetinother", + "YinYangBalance.Asia", + "Yu Jun", + "Yurii Monastyrshyn", + "Zachariah Rosenberg", + ], + } diff --git a/from_3b1b/active/diffyq/part2/fourier_series.py b/from_3b1b/active/diffyq/part2/fourier_series.py index 8ca76b28..ea58f9c0 100644 --- a/from_3b1b/active/diffyq/part2/fourier_series.py +++ b/from_3b1b/active/diffyq/part2/fourier_series.py @@ -331,6 +331,8 @@ class FourierOfPiSymbol(FourierCirclesScene): def add_vectors_circles_path(self): path = self.get_path() coefs = self.get_coefficients_of_path(path) + for coef in coefs: + print(coef) vectors = self.get_rotating_vectors(coefficients=coefs) circles = self.get_circles(vectors) self.set_decreasing_stroke_widths(circles) diff --git a/from_3b1b/active/ldm.py b/from_3b1b/active/ldm.py new file mode 100644 index 00000000..d6880eb4 --- /dev/null +++ b/from_3b1b/active/ldm.py @@ -0,0 +1,1445 @@ +from manimlib.imports import * +from from_3b1b.active.bayes.beta_helpers import * +import math + + +class StreamIntro(Scene): + def construct(self): + # Add logo + logo = Logo() + spikes = VGroup(*[ + spike + for layer in logo.spike_layers + for spike in layer + ]) + self.add(*logo.family_members_with_points()) + + # Add label + label = TextMobject("The lesson will\\\\begin shortly") + label.scale(2) + label.next_to(logo, DOWN) + self.add(label) + + self.camera.frame.move_to(DOWN) + + for spike in spikes: + point = spike.get_start() + spike.angle = angle_of_vector(point) + + anims = [] + for spike in spikes: + anims.append(Rotate( + spike, spike.angle * 28 * 2, + about_point=ORIGIN, + rate_func=linear, + )) + self.play(*anims, run_time=60 * 5) + self.wait(20) + + +class OldStreamIntro(Scene): + def construct(self): + morty = Mortimer() + morty.flip() + morty.set_height(2) + morty.to_corner(DL) + self.play(PiCreatureSays( + morty, "The lesson will\\\\begin soon.", + bubble_kwargs={ + "height": 2, + "width": 3, + }, + target_mode="hooray", + )) + bound = AnimatedBoundary(morty.bubble.content, max_stroke_width=1) + self.add(bound, morty.bubble, morty.bubble.content) + self.remove(morty.bubble.content) + morty.bubble.set_fill(opacity=0) + + self.camera.frame.scale(0.6, about_edge=DL) + + self.play(Blink(morty)) + self.wait(5) + self.play(Blink(morty)) + self.wait(3) + return + + text = TextMobject("The lesson will\\\\begin soon.") + text.set_height(1.5) + text.to_corner(DL, buff=LARGE_BUFF) + self.add(text) + + +class QuadraticFormula(TeacherStudentsScene): + def construct(self): + formula = TexMobject( + "\\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}", + ) + formula.next_to(self.students, UP, buff=MED_LARGE_BUFF, aligned_edge=LEFT) + self.add(formula) + + self.change_student_modes( + "angry", "tired", "sad", + look_at_arg=formula, + ) + self.teacher_says( + "It doesn't have\\\\to be this way.", + bubble_kwargs={ + "width": 4, + "height": 3, + } + ) + self.wait(5) + self.change_student_modes( + "pondering", "thinking", "erm", + look_at_arg=formula + ) + self.wait(12) + + +class SimplerQuadratic(Scene): + def construct(self): + tex = TexMobject("m \\pm \\sqrt{m^2 - p}") + tex.set_stroke(BLACK, 12, background=True) + tex.scale(1.5) + self.add(tex) + + +class CosGraphs(Scene): + def construct(self): + axes = Axes( + x_min=-0.75 * TAU, + x_max=0.75 * TAU, + y_min=-1.5, + y_max=1.5, + x_axis_config={ + "tick_frequency": PI / 4, + "include_tip": False, + }, + y_axis_config={ + "tick_frequency": 0.5, + "include_tip": False, + "unit_size": 1.5, + } + ) + + graph1 = axes.get_graph(np.cos) + graph2 = axes.get_graph(lambda x: np.cos(x)**2) + + graph1.set_stroke(YELLOW, 5) + graph2.set_stroke(BLUE, 5) + + label1 = TexMobject("\\cos(x)") + label2 = TexMobject("\\cos^2(x)") + + label1.match_color(graph1) + label1.set_height(0.75) + label1.next_to(axes.input_to_graph_point(-PI, graph1), DOWN) + + label2.match_color(graph2) + label2.set_height(0.75) + label2.next_to(axes.input_to_graph_point(PI, graph2), UP) + + for mob in [graph1, graph2, label1, label2]: + mc = mob.copy() + mc.set_stroke(BLACK, 10, background=True) + self.add(mc) + + self.add(axes) + self.add(graph1) + self.add(graph2) + self.add(label1) + self.add(label2) + + self.embed() + + +class SineWave(Scene): + def construct(self): + w_axes = self.get_wave_axes() + square, circle, c_axes = self.get_edge_group() + + self.add(w_axes) + self.add(square, circle, c_axes) + + theta_tracker = ValueTracker(0) + c_dot = Dot(color=YELLOW) + c_line = Line(DOWN, UP, color=GREEN) + w_dot = Dot(color=YELLOW) + w_line = Line(DOWN, UP, color=GREEN) + + def update_c_dot(dot, axes=c_axes, tracker=theta_tracker): + theta = tracker.get_value() + dot.move_to(axes.c2p( + np.cos(theta), + np.sin(theta), + )) + + def update_c_line(line, axes=c_axes, tracker=theta_tracker): + theta = tracker.get_value() + x = np.cos(theta) + y = np.sin(theta) + if y == 0: + y = 1e-6 + line.put_start_and_end_on( + axes.c2p(x, 0), + axes.c2p(x, y), + ) + + def update_w_dot(dot, axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + dot.move_to(axes.c2p(theta, np.sin(theta))) + + def update_w_line(line, axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + x = theta + y = np.sin(theta) + if y == 0: + y = 1e-6 + line.put_start_and_end_on( + axes.c2p(x, 0), + axes.c2p(x, y), + ) + + def get_partial_circle(circle=circle, tracker=theta_tracker): + result = circle.copy() + theta = tracker.get_value() + result.pointwise_become_partial( + circle, 0, clip(theta / TAU, 0, 1), + ) + result.set_stroke(RED, width=3) + return result + + def get_partial_wave(axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + graph = axes.get_graph(np.sin, x_min=0, x_max=theta, step_size=0.025) + graph.set_stroke(BLUE, 3) + return graph + + def get_h_line(axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + return Line( + axes.c2p(0, 0), + axes.c2p(theta, 0), + stroke_color=RED + ) + + c_dot.add_updater(update_c_dot) + c_line.add_updater(update_c_line) + w_dot.add_updater(update_w_dot) + w_line.add_updater(update_w_line) + partial_circle = always_redraw(get_partial_circle) + partial_wave = always_redraw(get_partial_wave) + h_line = always_redraw(get_h_line) + + self.add(partial_circle) + self.add(partial_wave) + self.add(h_line) + self.add(c_line, c_dot) + self.add(w_line, w_dot) + + sin_label = TexMobject( + "\\sin\\left(\\theta\\right)", + tex_to_color_map={"\\theta": RED} + ) + sin_label.next_to(w_axes.get_top(), UR) + self.add(sin_label) + + self.play( + theta_tracker.set_value, 1.25 * TAU, + run_time=15, + rate_func=linear, + ) + + def get_wave_axes(self): + wave_axes = Axes( + x_min=0, + x_max=1.25 * TAU, + y_min=-1.0, + y_max=1.0, + x_axis_config={ + "tick_frequency": TAU / 8, + "unit_size": 1.0, + }, + y_axis_config={ + "tick_frequency": 0.5, + "include_tip": False, + "unit_size": 1.5, + } + ) + wave_axes.y_axis.add_numbers( + -1, 1, number_config={"num_decimal_places": 1} + ) + wave_axes.to_edge(RIGHT, buff=MED_SMALL_BUFF) + + pairs = [ + (PI / 2, "\\frac{\\pi}{2}"), + (PI, "\\pi"), + (3 * PI / 2, "\\frac{3\\pi}{2}"), + (2 * PI, "2\\pi"), + ] + syms = VGroup() + for val, tex in pairs: + sym = TexMobject(tex) + sym.scale(0.5) + sym.next_to(wave_axes.c2p(val, 0), DOWN, MED_SMALL_BUFF) + syms.add(sym) + wave_axes.add(syms) + + theta = TexMobject("\\theta") + theta.set_color(RED) + theta.next_to(wave_axes.x_axis.get_end(), UP) + wave_axes.add(theta) + + return wave_axes + + def get_edge_group(self): + axes_max = 1.25 + radius = 1.5 + axes = Axes( + x_min=-axes_max, + x_max=axes_max, + y_min=-axes_max, + y_max=axes_max, + axis_config={ + "tick_frequency": 0.5, + "include_tip": False, + "numbers_with_elongated_ticks": [-1, 1], + "tick_size": 0.05, + "unit_size": radius, + }, + ) + axes.to_edge(LEFT, buff=MED_LARGE_BUFF) + + background = SurroundingRectangle(axes, buff=MED_SMALL_BUFF) + background.set_stroke(WHITE, 1) + background.set_fill(GREY_E, 1) + + circle = Circle(radius=radius) + circle.move_to(axes) + circle.set_stroke(WHITE, 1) + + nums = VGroup() + for u in 1, -1: + num = Integer(u) + num.set_height(0.2) + num.set_stroke(BLACK, 3, background=True) + num.next_to(axes.c2p(u, 0), DOWN + u * RIGHT, SMALL_BUFF) + nums.add(num) + + axes.add(nums) + + return background, circle, axes + + +class CosWave(SineWave): + CONFIG = { + "include_square": False, + } + + def construct(self): + w_axes = self.get_wave_axes() + square, circle, c_axes = self.get_edge_group() + + self.add(w_axes) + self.add(square, circle, c_axes) + + theta_tracker = ValueTracker(0) + c_dot = Dot(color=YELLOW) + c_line = Line(DOWN, UP, color=GREEN) + w_dot = Dot(color=YELLOW) + w_line = Line(DOWN, UP, color=GREEN) + + def update_c_dot(dot, axes=c_axes, tracker=theta_tracker): + theta = tracker.get_value() + dot.move_to(axes.c2p( + np.cos(theta), + np.sin(theta), + )) + + def update_c_line(line, axes=c_axes, tracker=theta_tracker): + theta = tracker.get_value() + x = np.cos(theta) + y = np.sin(theta) + line.set_points_as_corners([ + axes.c2p(0, y), + axes.c2p(x, y), + ]) + + def update_w_dot(dot, axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + dot.move_to(axes.c2p(theta, np.cos(theta))) + + def update_w_line(line, axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + x = theta + y = np.cos(theta) + if y == 0: + y = 1e-6 + line.set_points_as_corners([ + axes.c2p(x, 0), + axes.c2p(x, y), + ]) + + def get_partial_circle(circle=circle, tracker=theta_tracker): + result = circle.copy() + theta = tracker.get_value() + result.pointwise_become_partial( + circle, 0, clip(theta / TAU, 0, 1), + ) + result.set_stroke(RED, width=3) + return result + + def get_partial_wave(axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + graph = axes.get_graph(np.cos, x_min=0, x_max=theta, step_size=0.025) + graph.set_stroke(PINK, 3) + return graph + + def get_h_line(axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + return Line( + axes.c2p(0, 0), + axes.c2p(theta, 0), + stroke_color=RED + ) + + def get_square(line=c_line): + square = Square() + square.set_stroke(WHITE, 1) + square.set_fill(MAROON_B, opacity=0.5) + square.match_width(line) + square.move_to(line, DOWN) + return square + + def get_square_graph(axes=w_axes, tracker=theta_tracker): + theta = tracker.get_value() + graph = axes.get_graph( + lambda x: np.cos(x)**2, x_min=0, x_max=theta, step_size=0.025 + ) + graph.set_stroke(MAROON_B, 3) + return graph + + c_dot.add_updater(update_c_dot) + c_line.add_updater(update_c_line) + w_dot.add_updater(update_w_dot) + w_line.add_updater(update_w_line) + h_line = always_redraw(get_h_line) + partial_circle = always_redraw(get_partial_circle) + partial_wave = always_redraw(get_partial_wave) + + self.add(partial_circle) + self.add(partial_wave) + self.add(h_line) + self.add(c_line, c_dot) + self.add(w_line, w_dot) + + if self.include_square: + self.add(always_redraw(get_square)) + self.add(always_redraw(get_square_graph)) + + cos_label = TexMobject( + "\\cos\\left(\\theta\\right)", + tex_to_color_map={"\\theta": RED} + ) + cos_label.next_to(w_axes.get_top(), UR) + self.add(cos_label) + + self.play( + theta_tracker.set_value, 1.25 * TAU, + run_time=15, + rate_func=linear, + ) + + +class CosSquare(CosWave): + CONFIG = { + "include_square": True + } + + +class ComplexNumberPreview(Scene): + def construct(self): + plane = ComplexPlane(axis_config={"stroke_width": 4}) + plane.add_coordinates() + + z = complex(2, 1) + dot = Dot() + dot.move_to(plane.n2p(z)) + label = TexMobject("2+i") + label.set_color(YELLOW) + dot.set_color(YELLOW) + label.next_to(dot, UR, SMALL_BUFF) + label.set_stroke(BLACK, 5, background=True) + + line = Line(plane.n2p(0), plane.n2p(z)) + arc = Arc(start_angle=0, angle=np.log(z).imag, radius=0.5) + + self.add(plane) + self.add(line, arc) + self.add(dot) + self.add(label) + + self.embed() + + +class ComplexMultiplication(Scene): + def construct(self): + # Add plane + plane = ComplexPlane() + plane.add_coordinates() + + z = complex(2, 1) + z_dot = Dot(color=PINK) + z_dot.move_to(plane.n2p(z)) + z_label = TexMobject("z") + z_label.next_to(z_dot, UR, buff=0.5 * SMALL_BUFF) + z_label.match_color(z_dot) + + self.add(plane) + self.add(z_dot) + self.add(z_label) + + # Show 1 + one_vect = Vector(RIGHT) + one_vect.set_color(YELLOW) + one_vect.target = Vector(plane.n2p(z)) + one_vect.target.match_style(one_vect) + + z_rhs = TexMobject("=", "z \\cdot 1") + z_rhs[1].match_color(one_vect) + z_rhs.next_to(z_label, RIGHT, 1.5 * SMALL_BUFF, aligned_edge=DOWN) + z_rhs.set_stroke(BLACK, 3, background=True) + + one_label, i_label = [l for l in plane.coordinate_labels if l.get_value() == 1] + + self.play(GrowArrow(one_vect)) + self.wait() + self.add(one_vect, z_dot) + self.play( + MoveToTarget(one_vect), + TransformFromCopy(one_label, z_rhs), + ) + self.wait() + + # Show i + i_vect = Vector(UP, color=GREEN) + zi_point = plane.n2p(z * complex(0, 1)) + i_vect.target = Vector(zi_point) + i_vect.target.match_style(i_vect) + i_vect_label = TexMobject("z \\cdot i") + i_vect_label.match_color(i_vect) + i_vect_label.set_stroke(BLACK, 3, background=True) + i_vect_label.next_to(zi_point, UL, SMALL_BUFF) + + self.play(GrowArrow(i_vect)) + self.wait() + self.play( + MoveToTarget(i_vect), + TransformFromCopy(i_label, i_vect_label), + run_time=1, + ) + self.wait() + + self.play( + TransformFromCopy(one_vect, i_vect.target, path_arc=-90 * DEGREES), + ) + self.wait() + + # Transform plane + plane.generate_target() + for mob in plane.target.family_members_with_points(): + if isinstance(mob, Line): + mob.set_stroke(GREY, opacity=0.5) + new_plane = ComplexPlane(faded_line_ratio=0) + + self.remove(plane) + self.add(plane, new_plane, *self.mobjects) + + new_plane.generate_target() + new_plane.target.apply_complex_function(lambda w, z=z: w * z) + + self.play( + MoveToTarget(plane), + MoveToTarget(new_plane), + run_time=6, + rate_func=there_and_back_with_pause + ) + self.wait() + + # Show Example Point + w = complex(2, -1) + w_dot = Dot(plane.n2p(w), color=WHITE) + one_vects = VGroup(*[Vector(RIGHT) for x in range(2)]) + one_vects.arrange(RIGHT, buff=0) + one_vects.move_to(plane.n2p(0), LEFT) + one_vects.set_color(YELLOW) + new_i_vect = Vector(DOWN) + new_i_vect.move_to(plane.n2p(2), UP) + new_i_vect.set_color(GREEN) + vects = VGroup(*one_vects, new_i_vect) + vects.set_opacity(0.8) + + w_group = VGroup(*vects, w_dot) + w_group.target = VGroup( + one_vect.copy().set_opacity(0.8), + one_vect.copy().shift(plane.n2p(z)).set_opacity(0.8), + i_vect.copy().rotate(PI, about_point=ORIGIN).shift(2 * plane.n2p(z)).set_opacity(0.8), + Dot(plane.n2p(w * z), color=WHITE) + ) + + self.play(FadeInFromLarge(w_dot)) + self.wait() + self.play(ShowCreation(vects)) + self.wait() + + self.play( + MoveToTarget(plane), + MoveToTarget(new_plane), + MoveToTarget(w_group), + run_time=2, + path_arc=np.log(z).imag, + ) + self.wait() + + +class RotatePiCreature(Scene): + def construct(self): + randy = Randolph(mode="thinking") + randy.set_height(6) + + plane = ComplexPlane(x_min=-12, x_max=12) + plane.add_coordinates() + + self.camera.frame.move_to(3 * RIGHT) + + self.add(randy) + self.wait() + self.play(Rotate(randy, 30 * DEGREES, run_time=3)) + self.wait() + self.play(Rotate(randy, -30 * DEGREES)) + + self.add(plane, randy) + self.play( + ShowCreation(plane), + randy.set_opacity, 0.75, + ) + self.wait() + + dots = VGroup() + for mob in randy.family_members_with_points(): + for point in mob.get_anchors(): + dot = Dot(point) + dot.set_height(0.05) + dots.add(dot) + + self.play(ShowIncreasingSubsets(dots)) + self.wait() + + label = VGroup( + TexMobject("(x + iy)"), + Vector(DOWN), + TexMobject("(\\cos(30^\\circ) + i\\sin(30^\\circ))", "(x + iy)"), + ) + label[2][0].set_color(YELLOW) + label.arrange(DOWN) + label.to_corner(DR) + label.shift(3 * RIGHT) + + for mob in label: + mob.add_background_rectangle() + + self.play(FadeIn(label)) + self.wait() + + randy.add(dots) + self.play(Rotate(randy, 30 * DEGREES), run_time=3) + self.wait() + + +class ExpMeaning(Scene): + CONFIG = { + "include_circle": True + } + + def construct(self): + # Plane + plane = ComplexPlane(y_min=-6, y_max=6) + plane.shift(1.5 * DOWN) + plane.add_coordinates() + if self.include_circle: + circle = Circle(radius=1) + circle.set_stroke(RED, 1) + circle.move_to(plane.n2p(0)) + plane.add(circle) + + # Equation + equation = TexMobject( + "\\text{exp}(i\\theta) = ", + "1 + ", + "i\\theta + ", + "{(i\\theta)^2 \\over 2} + ", + "{(i\\theta)^3 \\over 6} + ", + "{(i\\theta)^4 \\over 24} + ", + "\\cdots", + tex_to_color_map={ + "\\theta": YELLOW, + "i": GREEN, + }, + ) + equation.add_background_rectangle(buff=MED_SMALL_BUFF, opacity=1) + equation.to_edge(UL, buff=0) + + # Label + theta_tracker = ValueTracker(0) + theta_label = VGroup( + TexMobject("\\theta = "), + DecimalNumber(0, num_decimal_places=4) + ) + theta_decimal = theta_label[1] + theta_decimal.add_updater( + lambda m, tt=theta_tracker: m.set_value(tt.get_value()) + ) + theta_label.arrange(RIGHT, buff=SMALL_BUFF) + theta_label.set_color(YELLOW) + theta_label.add_to_back(BackgroundRectangle( + theta_label, + buff=MED_SMALL_BUFF, + fill_opacity=1, + )) + theta_label.next_to(equation, DOWN, aligned_edge=LEFT, buff=0) + + # Vectors + def get_vectors(n_vectors=20, plane=plane, tracker=theta_tracker): + last_tip = plane.n2p(0) + z = complex(0, tracker.get_value()) + vects = VGroup() + colors = color_gradient([GREEN, YELLOW, RED], 6) + for i, color in zip(range(n_vectors), it.cycle(colors)): + vect = Vector(complex_to_R3(z**i / math.factorial(i))) + vect.set_color(color) + vect.shift(last_tip) + last_tip = vect.get_end() + vects.add(vect) + return vects + + vectors = always_redraw(get_vectors) + dot = Dot() + dot.set_height(0.03) + dot.add_updater(lambda m, vs=vectors: m.move_to(vs[-1].get_end())) + + self.add(plane) + self.add(vectors) + self.add(dot) + self.add(equation) + self.add(theta_label) + + self.play( + theta_tracker.set_value, 1, + run_time=3, + rate_func=smooth, + ) + self.wait() + for target in PI, TAU: + self.play( + theta_tracker.set_value, target, + run_time=10, + ) + self.wait() + + self.embed() + + +class ExpMeaningWithoutCircle(ExpMeaning): + CONFIG = { + "include_circle": False, + } + + +class PositionAndVelocityExample(Scene): + def construct(self): + plane = NumberPlane() + + self.add(plane) + + self.embed() + + +class EulersFormula(Scene): + def construct(self): + kw = {"tex_to_color_map": {"\\theta": YELLOW}} + formula = TexMobject( + "&e^{i\\theta} = \\\\ &\\cos\\left(\\theta\\right) + i\\cdot\\sin\\left(\\theta\\right)", + )[0] + formula[:4].scale(2, about_edge=UL) + formula[:4].shift(SMALL_BUFF * RIGHT + MED_LARGE_BUFF * UP) + VGroup(formula[2], formula[8], formula[17]).set_color(YELLOW) + formula.scale(1.5) + formula.set_stroke(BLACK, 5, background=True) + self.add(formula) + + +class EtoILimit(Scene): + def construct(self): + tex = TexMobject( + "\\lim_{n \\to \\infty} \\left(1 + \\frac{it}{n}\\right)^n", + )[0] + VGroup(tex[3], tex[12], tex[14]).set_color(YELLOW) + tex[9].set_color(BLUE) + tex.scale(1.5) + tex.set_stroke(BLACK, 5, background=True) + # self.add(tex) + + text = TextMobject("Interest rate\\\\of ", "$\\sqrt{-1}$") + text[1].set_color(BLUE) + text.scale(1.5) + text.set_stroke(BLACK, 5, background=True) + self.add(text) + + +class ImaginaryInterestRates(Scene): + def construct(self): + plane = ComplexPlane(x_min=-20, x_max=20, y_min=-20, y_max=20) + plane.add_coordinates() + circle = Circle(radius=1) + circle.set_stroke(YELLOW, 1) + self.add(plane, circle) + + frame = self.camera.frame + frame.save_state() + frame.generate_target() + frame.target.set_width(25) + frame.target.move_to(8 * RIGHT + 2 * DOWN) + + dt_tracker = ValueTracker(1) + + def get_vectors(tracker=dt_tracker, plane=plane, T=8): + dt = tracker.get_value() + last_z = 1 + vects = VGroup() + for t in np.arange(0, T, dt): + next_z = last_z + complex(0, 1) * last_z * dt + vects.add(Arrow( + plane.n2p(last_z), + plane.n2p(next_z), + buff=0, + )) + last_z = next_z + vects.set_submobject_colors_by_gradient(YELLOW, GREEN, BLUE) + return vects + + vects = get_vectors() + + line = Line() + line.add_updater(lambda m, v=vects: m.put_start_and_end_on( + ORIGIN, v[-1].get_start() if len(v) > 0 else RIGHT, + )) + + self.add(line) + self.play( + ShowIncreasingSubsets( + vects, + rate_func=linear, + int_func=np.ceil, + ), + MoveToTarget( + frame, + rate_func=squish_rate_func(smooth, 0.5, 1), + ), + run_time=8, + ) + self.wait() + self.play(FadeOut(line)) + + self.remove(vects) + vects = always_redraw(get_vectors) + self.add(vects) + self.play( + Restore(frame), + dt_tracker.set_value, 0.2, + run_time=5, + ) + self.wait() + self.play(dt_tracker.set_value, 0.01, run_time=3) + vects.clear_updaters() + self.wait() + + theta_tracker = ValueTracker(0) + + def get_arc(tracker=theta_tracker): + theta = tracker.get_value() + arc = Arc( + radius=1, + stroke_width=3, + stroke_color=RED, + start_angle=0, + angle=theta + ) + return arc + + arc = always_redraw(get_arc) + dot = Dot() + dot.add_updater(lambda m, arc=arc: m.move_to(arc.get_end())) + + label = VGroup( + DecimalNumber(0, num_decimal_places=3), + TextMobject("Years") + ) + label.arrange(RIGHT, aligned_edge=DOWN) + label.move_to(3 * LEFT + 1.5 * UP) + + label[0].set_color(RED) + label[0].add_updater(lambda m, tt=theta_tracker: m.set_value(tt.get_value())) + + self.add(BackgroundRectangle(label), label, arc, dot) + for n in range(1, 5): + target = n * PI / 2 + self.play( + theta_tracker.set_value, target, + run_time=3 + ) + self.wait(2) + + +class Logs(Scene): + def construct(self): + log = TexMobject( + "&\\text{log}(ab) = \\\\ &\\text{log}(a) + \\text{log}(b)", + tex_to_color_map={"a": BLUE, "b": YELLOW}, + alignment="", + ) + + log.scale(1.5) + log.set_stroke(BLACK, 5, background=True) + + self.add(log) + + +class LnX(Scene): + def construct(self): + sym = TexMobject("\\ln(x)") + sym.scale(3) + sym.shift(UP) + sym.set_stroke(BLACK, 5, background=True) + + word = TextMobject("Natural?") + word.scale(1.5) + word.set_color(YELLOW) + word.set_stroke(BLACK, 5, background=True) + word.next_to(sym, DOWN, buff=0.5) + arrow = Arrow(word.get_top(), sym[0][1].get_bottom()) + + self.add(sym, word, arrow) + + +class HarmonicSum(Scene): + def construct(self): + axes = Axes( + x_min=0, + x_max=13, + y_min=0, + y_max=1.25, + y_axis_config={ + "unit_size": 4, + "tick_frequency": 0.25, + } + ) + axes.to_corner(DL, buff=1) + axes.x_axis.add_numbers() + axes.y_axis.add_numbers( + *np.arange(0.25, 1.25, 0.25), + number_config={"num_decimal_places": 2}, + ) + self.add(axes) + + graph = axes.get_graph(lambda x: 1 / x, x_min=0.1, x_max=15) + graph_fill = graph.copy() + graph_fill.add_line_to(axes.c2p(15, 0)) + graph_fill.add_line_to(axes.c2p(1, 0)) + graph_fill.add_line_to(axes.c2p(1, 1)) + graph.set_stroke(WHITE, 3) + graph_fill.set_fill(BLUE_E, 0.5) + graph_fill.set_stroke(width=0) + self.add(graph, graph_fill) + + bars = VGroup() + bar_labels = VGroup() + for x in range(1, 15): + line = Line(axes.c2p(x, 0), axes.c2p(x + 1, 1 / x)) + bar = Rectangle() + bar.set_fill(GREEN_E, 1) + bar.replace(line, stretch=True) + bars.add(bar) + + label = TexMobject(f"1 \\over {x}") + label.set_height(0.7) + label.next_to(bar, UP, SMALL_BUFF) + bar_labels.add(label) + + bars.set_submobject_colors_by_gradient(GREEN_C, GREEN_E) + bars.set_stroke(WHITE, 1) + bars.set_fill(opacity=0.25) + + self.add(bars) + self.add(bar_labels) + + + self.embed() + + +class PowerTower(Scene): + def construct(self): + mob = TexMobject("4 = x^{x^{{x^{x^{x^{\cdot^{\cdot^{\cdot}}}}}}}}") + mob[0][-1].shift(0.1 * DL) + mob[0][-2].shift(0.05 * DL) + + mob.set_height(4) + mob.set_stroke(BLACK, 5, background=True) + + self.add(mob) + + +class ItoTheI(Scene): + def construct(self): + tex = TexMobject("i^i") + # tex = TexMobject("\\sqrt{-1}^{\\sqrt{-1}}") + tex.set_height(3) + tex.set_stroke(BLACK, 8, background=True) + self.add(tex) + + +class ComplexExponentialPlay(Scene): + def setup(self): + self.transform_alpha = 0 + + def construct(self): + # Plane + plane = ComplexPlane( + x_min=-2 * FRAME_WIDTH, + x_max=2 * FRAME_WIDTH, + y_min=-2 * FRAME_HEIGHT, + y_max=2 * FRAME_HEIGHT, + ) + plane.add_coordinates() + self.add(plane) + + # R Dot + r_dot = Dot(color=YELLOW) + + def update_r_dot(dot, point_tracker=self.mouse_drag_point): + point = point_tracker.get_location() + if abs(point[0]) < 0.1: + point[0] = 0 + if abs(point[1]) < 0.1: + point[1] = 0 + dot.move_to(point) + + r_dot.add_updater(update_r_dot) + self.mouse_drag_point.move_to(plane.n2p(1)) + + # Transformed sample dots + def func(z, dot=r_dot, plane=plane): + r = plane.p2n(dot.get_center()) + result = np.exp(r * z) + if abs(result) > 20: + result *= 20 / abs(result) + return result + + sample_dots = VGroup() + dot_template = Dot(radius=0.05) + dot_template.set_opacity(0.8) + spacing = 0.05 + for x in np.arange(-7, 7, spacing): + dot = dot_template.copy() + dot.set_color(TEAL) + dot.z = x + dot.move_to(plane.n2p(dot.z)) + sample_dots.add(dot) + for y in np.arange(-6, 6, spacing): + dot = dot_template.copy() + dot.set_color(MAROON) + dot.z = complex(0, y) + dot.move_to(plane.n2p(dot.z)) + sample_dots.add(dot) + + special_values = [1, complex(0, 1), -1, complex(0, -1)] + special_dots = VGroup(*[ + list(filter(lambda d: abs(d.z - x) < 0.01, sample_dots))[0] + for x in special_values + ]) + for dot in special_dots: + dot.set_fill(opacity=1) + dot.scale(1.2) + dot.set_stroke(WHITE, 2) + + sample_dots.save_state() + + def update_sample(sample, f=func, plane=plane, scene=self): + sample.restore() + sample.apply_function_to_submobject_positions( + lambda p: interpolate( + p, + plane.n2p(f(plane.p2n(p))), + scene.transform_alpha, + ) + ) + return sample + + sample_dots.add_updater(update_sample) + + # Sample lines + x_line = Line(plane.n2p(plane.x_min), plane.n2p(plane.x_max)) + y_line = Line(plane.n2p(plane.y_min), plane.n2p(plane.y_max)) + y_line.rotate(90 * DEGREES) + x_line.set_color(GREEN) + y_line.set_color(PINK) + axis_lines = VGroup(x_line, y_line) + for line in axis_lines: + line.insert_n_curves(50) + axis_lines.save_state() + + def update_axis_liens(lines=axis_lines, f=func, plane=plane, scene=self): + lines.restore() + lines.apply_function( + lambda p: interpolate( + p, + plane.n2p(f(plane.p2n(p))), + scene.transform_alpha, + ) + ) + lines.make_smooth() + + axis_lines.add_updater(update_axis_liens) + + # Labels + labels = VGroup( + TexMobject("f(1)"), + TexMobject("f(i)"), + TexMobject("f(-1)"), + TexMobject("f(-i)"), + ) + for label, dot in zip(labels, special_dots): + label.set_height(0.3) + label.match_color(dot) + label.set_stroke(BLACK, 3, background=True) + label.add_background_rectangle(opacity=0.5) + + def update_labels(labels, dots=special_dots, scene=self): + for label, dot in zip(labels, dots): + label.next_to(dot, UR, 0.5 * SMALL_BUFF) + label.set_opacity(self.transform_alpha) + + labels.add_updater(update_labels) + + # Titles + title = TexMobject( + "f(x) =", "\\text{exp}(r\\cdot x)", + tex_to_color_map={"r": YELLOW} + ) + title.to_corner(UL) + title.set_stroke(BLACK, 5, background=True) + brace = Brace(title[1:], UP, buff=SMALL_BUFF) + e_pow = TexMobject("e^{rx}", tex_to_color_map={"r": YELLOW}) + e_pow.add_background_rectangle() + e_pow.next_to(brace, UP, buff=SMALL_BUFF) + title.add(brace, e_pow) + + r_eq = VGroup( + TexMobject("r=", tex_to_color_map={"r": YELLOW}), + DecimalNumber(1) + ) + r_eq.arrange(RIGHT, aligned_edge=DOWN) + r_eq.next_to(title, DOWN, aligned_edge=LEFT) + r_eq[0].set_stroke(BLACK, 5, background=True) + r_eq[1].set_color(YELLOW) + r_eq[1].add_updater(lambda m: m.set_value(plane.p2n(r_dot.get_center()))) + + self.add(title) + self.add(r_eq) + + # self.add(axis_lines) + self.add(sample_dots) + self.add(r_dot) + self.add(labels) + + # Animations + def update_transform_alpha(mob, alpha, scene=self): + scene.transform_alpha = alpha + + frame = self.camera.frame + frame.set_height(10) + r_dot.clear_updaters() + r_dot.move_to(plane.n2p(1)) + + self.play( + UpdateFromAlphaFunc( + VectorizedPoint(), + update_transform_alpha, + ) + ) + self.play(r_dot.move_to, plane.n2p(2)) + self.wait() + self.play(r_dot.move_to, plane.n2p(PI)) + self.wait() + self.play(r_dot.move_to, plane.n2p(np.log(2))) + self.wait() + self.play(r_dot.move_to, plane.n2p(complex(0, np.log(2))), path_arc=90 * DEGREES, run_time=2) + self.wait() + self.play(r_dot.move_to, plane.n2p(complex(0, PI / 2))) + self.wait() + self.play(r_dot.move_to, plane.n2p(np.log(2)), run_time=2) + self.wait() + self.play(frame.set_height, 14) + self.play(r_dot.move_to, plane.n2p(complex(np.log(2), PI)), run_time=3) + self.wait() + self.play(r_dot.move_to, plane.n2p(complex(np.log(2), TAU)), run_time=3) + self.wait() + + self.embed() + + def on_mouse_scroll(self, point, offset): + frame = self.camera.frame + if self.zoom_on_scroll: + factor = 1 + np.arctan(10 * offset[1]) + frame.scale(factor, about_point=ORIGIN) + else: + self.transform_alpha = clip(self.transform_alpha + 5 * offset[1], 0, 1) + + +class LDMEndScreen(PatreonEndScreen): + CONFIG = { + "scroll_time": 20, + "specific_patrons": [ + "1stViewMaths", + "Adam Dřínek", + "Adam Margulies", + "Aidan Shenkman", + "Alan Stein", + "Alex Mijalis", + "Alexander Mai", + "Alexis Olson", + "Ali Yahya", + "Andreas Snekloth Kongsgaard", + "Andrew Busey", + "Andrew Cary", + "Andrew R. Whalley", + "Anthony Losego", + "Aravind C V", + "Arjun Chakroborty", + "Arthur Zey", + "Ashwin Siddarth", + "Augustine Lim", + "Austin Goodman", + "Avi Finkel", + "Awoo", + "Axel Ericsson", + "Ayan Doss", + "AZsorcerer", + "Barry Fam", + "Bartosz Burclaf", + "Ben Delo", + "Bernd Sing", + "Bill Gatliff", + "Bob Sanderson", + "Boris Veselinovich", + "Bradley Pirtle", + "Brandon Huang", + "Brendan Shah", + "Brian Cloutier", + "Brian Staroselsky", + "Britt Selvitelle", + "Britton Finley", + "Burt Humburg", + "Calvin Lin", + "Charles Southerland", + "Charlie N", + "Chenna Kautilya", + "Chris Connett", + "Chris Druta", + "Christian Kaiser", + "cinterloper", + "Clark Gaebel", + "Colwyn Fritze-Moor", + "Cooper Jones", + "Corey Ogburn", + "D. Sivakumar", + "Dan Herbatschek", + "Daniel Herrera C", + "Darrell Thomas", + "Dave B", + "Dave Cole", + "Dave Kester", + "dave nicponski", + "David B. Hill", + "David Clark", + "David Gow", + "Delton Ding", + "Eduardo Rodriguez", + "Emilio Mendoza Palafox", + "emptymachine", + "Eric Younge", + "Eryq Ouithaqueue", + "Federico Lebron", + "Fernando Via Canel", + "Frank R. Brown, Jr.", + "Giovanni Filippi", + "Goodwine", + "Hal Hildebrand", + "Heptonion", + "Hitoshi Yamauchi", + "Ivan Sorokin", + "Jacob Baxter", + "Jacob Harmon", + "Jacob Hartmann", + "Jacob Magnuson", + "Jalex Stark", + "Jameel Syed", + "James Beall", + "Jason Hise", + "Jayne Gabriele", + "Jean-Manuel Izaret", + "Jeff Dodds", + "Jeff Linse", + "Jeff Straathof", + "Jeffrey Wolberg", + "Jimmy Yang", + "Joe Pregracke", + "Johan Auster", + "John C. Vesey", + "John Camp", + "John Haley", + "John Le", + "John Luttig", + "John Rizzo", + "John V Wertheim", + "Jonathan Heckerman", + "Jonathan Wilson", + "Joseph John Cox", + "Joseph Kelly", + "Josh Kinnear", + "Joshua Claeys", + "Joshua Ouellette", + "Juan Benet", + "Julien Dubois", + "Kai-Siang Ang", + "Kanan Gill", + "Karl Niu", + "Kartik Cating-Subramanian", + "Kaustuv DeBiswas", + "Killian McGuinness", + "kkm", + "Klaas Moerman", + "Kristoffer Börebäck", + "Kros Dai", + "L0j1k", + "Lael S Costa", + "LAI Oscar", + "Lambda GPU Workstations", + "Laura Gast", + "Lee Redden", + "Linh Tran", + "Luc Ritchie", + "Ludwig Schubert", + "Lukas Biewald", + "Lukas Zenick", + "Magister Mugit", + "Magnus Dahlström", + "Magnus Hiie", + "Manoj Rewatkar", + "Mark B Bahu", + "Mark Heising", + "Mark Hopkins", + "Mark Mann", + "Martin Price", + "Mathias Jansson", + "Matt Godbolt", + "Matt Langford", + "Matt Roveto", + "Matt Russell", + "Matteo Delabre", + "Matthew Bouchard", + "Matthew Cocke", + "Maxim Nitsche", + "Michael Bos", + "Michael Day", + "Michael Hardel", + "Michael W White", + "Mihran Vardanyan", + "Mirik Gogri", + "Molly Mackinlay", + "Mustafa Mahdi", + "Márton Vaitkus", + "Nate Heckmann", + "Nero Li", + "Nicholas Cahill", + "Nikita Lesnikov", + "Oleg Leonov", + "Oliver Steele", + "Omar Zrien", + "Omer Tuchfeld", + "Patrick Lucas", + "Pavel Dubov", + "Pesho Ivanov", + "Petar Veličković", + "Peter Ehrnstrom", + "Peter Francis", + "Peter Mcinerney", + "Pierre Lancien", + "Pradeep Gollakota", + "Rafael Bove Barrios", + "Raghavendra Kotikalapudi", + "Randy C. Will", + "rehmi post", + "Rex Godby", + "Ripta Pasay", + "Rish Kundalia", + "Roman Sergeychik", + "Roobie", + "Ryan Atallah", + "Samuel Judge", + "SansWord Huang", + "Scott Gray", + "Scott Walter, Ph.D.", + "soekul", + "Solara570", + "Stephen Shanahan", + "Steve Huynh", + "Steve Muench", + "Steve Sperandeo", + "Steven Siddals", + "Stevie Metke", + "Sundar Subbarayan", + "Sunil Nagaraj", + "supershabam", + "Suteerth Vishnu", + "Suthen Thomas", + "Tal Einav", + "Taras Bobrovytsky", + "Tauba Auerbach", + "Ted Suzman", + "Thomas J Sargent", + "Thomas Tarler", + "Tianyu Ge", + "Tihan Seale", + "Tim Erbes", + "Tim Kazik", + "Tomasz Legutko", + "Tyler Herrmann", + "Tyler Parcell", + "Tyler VanValkenburg", + "Tyler Veness", + "Vassili Philippov", + "Vasu Dubey", + "Veritasium", + "Vignesh Ganapathi Subramanian", + "Vinicius Reis", + "Vladimir Solomatin", + "Wooyong Ee", + "Xuanji Li", + "Yana Chernobilsky", + "Yavor Ivanov", + "YinYangBalance.Asia", + "Yu Jun", + "Yurii Monastyrshyn", + ], + } diff --git a/manimlib/for_3b1b_videos/common_scenes.py b/manimlib/for_3b1b_videos/common_scenes.py index 91a6d053..83eaa1cb 100644 --- a/manimlib/for_3b1b_videos/common_scenes.py +++ b/manimlib/for_3b1b_videos/common_scenes.py @@ -268,8 +268,8 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene): "akostrikov": "Aleksandr Kostrikov", "Jacob Baxter": "Will Fleshman", "Sansword Huang": "SansWord@TW", - "Still working on an upcoming skeptical humanist SciFi novels- Elux Luc": "Uber Miguel", "Sunil Nagaraj": "Ubiquity Ventures", + "Nitu Kitchloo": "Ish Kitchloo", } for n1, n2 in modification_map.items(): if name.lower() == n1.lower():