from __future__ import absolute_import from big_ol_pile_of_manim_imports import * class Orbiting(ContinualAnimation): CONFIG = { "rate": 0.3, } def __init__(self, planet, star, ellipse, **kwargs): self.planet = planet self.star = star self.ellipse = ellipse # Proportion of the way around the ellipse self.proportion = 0 planet.move_to(ellipse.point_from_proportion(0)) ContinualAnimation.__init__(self, planet, **kwargs) def update_mobject(self, dt): # time = self.internal_time rate = self.rate planet = self.planet star = self.star ellipse = self.ellipse rate *= 1 / np.linalg.norm( planet.get_center() - star.get_center() ) self.proportion += rate * dt self.proportion = self.proportion % 1 planet.move_to(ellipse.point_from_proportion(self.proportion)) class SunAnimation(ContinualAnimation): CONFIG = { "rate": 0.2, "angle": 60 * DEGREES, } def __init__(self, sun, **kwargs): self.sun = sun self.rotated_sun = sun.deepcopy() self.rotated_sun.rotate(60 * DEGREES) ContinualAnimation.__init__( self, Group(sun, self.rotated_sun), **kwargs ) def update_mobject(self, dt): time = self.internal_time a = (np.sin(self.rate * time * TAU) + 1) / 2.0 self.rotated_sun.rotate(-self.angle) self.rotated_sun.move_to(self.sun) self.rotated_sun.rotate(self.angle) self.rotated_sun.pixel_array = np.array( a * self.sun.pixel_array, dtype=self.sun.pixel_array.dtype ) class ShowWord(Animation): CONFIG = { "time_per_char": 0.06, "rate_func": None, } def __init__(self, word, **kwargs): assert(isinstance(word, SingleStringTexMobject)) digest_config(self, kwargs) run_time = kwargs.pop( "run_time", self.time_per_char * len(word) ) self.stroke_width = word.get_stroke_width() Animation.__init__(self, word, run_time=run_time, **kwargs) def update_mobject(self, alpha): word = self.mobject stroke_width = self.stroke_width count = int(alpha * len(word)) remainder = (alpha * len(word)) % 1 word[:count].set_fill(opacity=1) word[:count].set_stroke(width=stroke_width) if count < len(word): word[count].set_fill(opacity=remainder) word[count].set_stroke(width=remainder * stroke_width) word[count + 1:].set_fill(opacity=0) word[count + 1:].set_stroke(width=0) # Animations class ShowEmergingEllipse(Scene): CONFIG = { "circle_radius": 3, "circle_color": BLUE, "num_lines": 150, "lines_stroke_width": 1, "eccentricity_vector": 2 * RIGHT, "ghost_lines_stroke_color": LIGHT_GREY, "ghost_lines_stroke_width": 0.5, "ellipse_color": PINK, } def construct(self): circle = self.get_circle() e_point = self.get_eccentricity_point() e_dot = Dot(e_point, color=YELLOW) lines = self.get_lines() ellipse = self.get_ellipse() fade_rect = FullScreenFadeRectangle() line = lines[len(lines) / 5] line_dot = Dot(line.get_center(), color=YELLOW) line_dot.scale(0.5) ghost_line = self.get_ghost_lines(line) ghost_lines = self.get_ghost_lines(lines) rot_words = TextMobject("Rotate $90^\\circ$ \\\\ about center") rot_words.next_to(line_dot, RIGHT) elbow = VGroup(Line(UP, UL), Line(UL, LEFT)) elbow.set_stroke(width=1) elbow.scale(0.2, about_point=ORIGIN) elbow.rotate( line.get_angle() - 90 * DEGREES, about_point=ORIGIN ) elbow.shift(line.get_center()) eccentric_words = TextMobject("``Eccentric'' point") eccentric_words.next_to(circle.get_center(), DOWN) ellipse_words = TextMobject("Perfect ellipse") ellipse_words.next_to(ellipse, UP, SMALL_BUFF) for text in rot_words, ellipse_words: text.add_to_back(text.copy().set_stroke(BLACK, 5)) shuffled_lines = VGroup(*lines) random.shuffle(shuffled_lines.submobjects) self.play(ShowCreation(circle)) self.play( FadeInAndShiftFromDirection(e_dot, LEFT), Write(eccentric_words, run_time=1) ) self.wait() self.play( LaggedStart(ShowCreation, shuffled_lines), Animation(VGroup(e_dot, circle)), FadeOut(eccentric_words) ) self.add(ghost_lines) self.add(e_dot, circle) self.wait() self.play( FadeIn(fade_rect), Animation(line), GrowFromCenter(line_dot), FadeInFromDown(rot_words), ) self.wait() self.add(ghost_line) self.play( MoveToTarget(line, path_arc=90 * DEGREES), Animation(rot_words), ShowCreation(elbow) ) self.wait() self.play( FadeOut(fade_rect), FadeOut(line_dot), FadeOut(rot_words), FadeOut(elbow), Animation(line), Animation(ghost_line) ) self.play( LaggedStart(MoveToTarget, lines, run_time=4), Animation(VGroup(e_dot, circle)) ) self.wait() self.play( ShowCreation(ellipse), FadeInFromDown(ellipse_words) ) self.wait() def get_circle(self): circle = self.circle = Circle( radius=self.circle_radius, color=self.circle_color ) return circle def get_eccentricity_point(self): return self.circle.get_center() + self.eccentricity_vector def get_lines(self): center = self.circle.get_center() radius = self.circle.get_width() / 2 e_point = self.get_eccentricity_point() lines = VGroup(*[ Line( e_point, center + rotate_vector(radius * RIGHT, angle) ) for angle in np.linspace(0, TAU, self.num_lines) ]) lines.set_stroke(width=self.lines_stroke_width) for line in lines: line.generate_target() line.target.rotate(90 * DEGREES) return lines def get_ghost_lines(self, lines): return lines.copy().set_stroke( color=self.ghost_lines_stroke_color, width=self.ghost_lines_stroke_width ) def get_ellipse(self): center = self.circle.get_center() e_point = self.get_eccentricity_point() radius = self.circle.get_width() / 2 # Ellipse parameters a = radius / 2 c = np.linalg.norm(e_point - center) / 2 b = np.sqrt(a**2 - c**2) result = Circle(radius=b, color=self.ellipse_color) result.stretch(a / b, 0) result.move_to(Line(center, e_point)) return result class FeynmanAndOrbitingPlannetOnEllipseDiagram(ShowEmergingEllipse): def construct(self): circle = self.get_circle() lines = self.get_lines() ghost_lines = self.get_ghost_lines(lines) for line in lines: MoveToTarget(line).update(1) ellipse = self.get_ellipse() e_dot = Dot(self.get_eccentricity_point()) e_dot.set_color(YELLOW) comet = ImageMobject("earth") comet.scale_to_fit_width(0.3) feynman = ImageMobject("Feynman") feynman.scale_to_fit_height(6) feynman.next_to(ORIGIN, LEFT) feynman.to_edge(UP) feynman_name = TextMobject("Richard Feynman") feynman_name.next_to(feynman, DOWN) feynman.save_state() feynman.shift(2 * DOWN) feynman_rect = BackgroundRectangle( feynman, fill_opacity=1 ) group = VGroup(circle, ghost_lines, lines, e_dot, ellipse) self.add(group) self.add(Orbiting(comet, e_dot, ellipse)) self.add_foreground_mobjects(comet) self.wait() self.play( feynman.restore, MaintainPositionRelativeTo(feynman_rect, feynman), VFadeOut(feynman_rect), group.to_edge, RIGHT, ) self.play(Write(feynman_name)) self.wait() self.wait(10) class FeynmanFame(Scene): def construct(self): books = VGroup( ImageMobject("Feynman_QED_cover"), ImageMobject("Surely_Youre_Joking_cover"), ImageMobject("Feynman_Lectures_cover"), ) for book in books: book.scale_to_fit_height(6) book.move_to(FRAME_WIDTH * LEFT / 4) feynman_diagram = self.get_feynman_diagram() feynman_diagram.next_to(ORIGIN, RIGHT) fd_parts = VGroup(*reversed(feynman_diagram.family_members_with_points())) # As a physicist self.play(self.get_book_intro(books[0])) self.play(LaggedStart( Write, feynman_diagram, run_time=4 )) self.wait() self.play( self.get_book_intro(books[1]), self.get_book_outro(books[0]), LaggedStart( ApplyMethod, fd_parts, lambda m: (m.scale, 0), run_time=1 ), ) self.remove(feynman_diagram) self.wait() # As a public figure safe = SVGMobject(file_name="safe", height=2) safe_rect = SurroundingRectangle(safe, buff=0) safe_rect.set_stroke(width=0) safe_rect.set_fill(DARK_GREY, 1) safe.add_to_back(safe_rect) bongo = SVGMobject(file_name="bongo") bongo.scale_to_fit_height(1) bongo.set_color(WHITE) bongo.next_to(safe, RIGHT, LARGE_BUFF) objects = VGroup(safe, bongo) feynman_smile = ImageMobject("Feynman_Los_Alamos") feynman_smile.match_width(objects) feynman_smile.next_to(objects, DOWN) VGroup(objects, feynman_smile).next_to(ORIGIN, RIGHT) joke = TextMobject( "``Science is the belief \\\\ in the ignorance of \\\\ experts.''" ) joke.move_to(objects) self.play(LaggedStart( DrawBorderThenFill, objects, lag_ratio=0.75 )) self.play(self.get_book_intro(feynman_smile)) self.wait() self.play( objects.shift, 2 * UP, VFadeOut(objects) ) self.play(Write(joke)) self.wait(2) self.play( self.get_book_intro(books[2]), self.get_book_outro(books[1]), LaggedStart(FadeOut, joke, run_time=1), ApplyMethod( feynman_smile.shift, FRAME_HEIGHT * DOWN, remover=True ) ) # As a teacher feynman_teacher = ImageMobject("Feynman_teaching") feynman_teacher.scale_to_fit_width(FRAME_WIDTH / 2 - 1) feynman_teacher.next_to(ORIGIN, RIGHT) self.play(self.get_book_intro(feynman_teacher)) self.wait(3) def get_book_animation(self, book, initial_shift, animated_shift, opacity_func ): rect = BackgroundRectangle(book, fill_opacity=1) book.shift(initial_shift) return AnimationGroup( ApplyMethod(book.shift, animated_shift), UpdateFromAlphaFunc( rect, lambda r, a: r.move_to(book).set_fill( opacity=opacity_func(a) ), remover=True ) ) def get_book_intro(self, book): return self.get_book_animation( book, 2 * DOWN, 2 * UP, lambda a: 1 - a ) def get_book_outro(self, book): return ApplyMethod(book.shift, FRAME_HEIGHT * UP, remover=True) def get_feynman_diagram(self): x_min = -1.5 x_max = 1.5 arrow = Arrow(LEFT, RIGHT, buff=0, use_rectangular_stem=False) arrow.tip.move_to(arrow.get_center()) arrows = VGroup(*[ arrow.copy().rotate(angle).next_to(point, vect, buff=0) for (angle, point, vect) in [ (-45 * DEGREES, x_min * RIGHT, UL), (-135 * DEGREES, x_min * RIGHT, DL), (-135 * DEGREES, x_max * RIGHT, UR), (-45 * DEGREES, x_max * RIGHT, DR), ] ]) labels = VGroup(*[ TexMobject(tex) for tex in ["e^-", "e^+", "\\text{\\=q}", "q"] ]) vects = [UR, DR, UL, DL] for arrow, label, vect in zip(arrows, labels, vects): label.next_to(arrow.get_center(), vect, buff=SMALL_BUFF) wave = FunctionGraph( lambda x: 0.2 * np.sin(2 * TAU * x), x_min=x_min, x_max=x_max, ) wave_label = TexMobject("\\gamma") wave_label.next_to(wave, UP, SMALL_BUFF) labels.add(wave_label) squiggle = ParametricFunction( lambda t: np.array([ t + 0.5 * np.sin(TAU * t), 0.5 * np.cos(TAU * t), 0, ]), t_min=0, t_max=4, ) squiggle.scale(0.25) squiggle.set_color(BLUE) squiggle.rotate(-30 * DEGREES) squiggle.next_to( arrows[2].point_from_proportion(0.75), DR, buff=0 ) squiggle_label = TexMobject("g") squiggle_label.next_to(squiggle, UR, buff=-MED_SMALL_BUFF) labels.add(squiggle_label) return VGroup(arrows, wave, squiggle, labels) class FeynmanLecturesScreenCaptureFrame(Scene): def construct(self): url = TextMobject("http://www.feynmanlectures.caltech.edu/") url.to_edge(UP) screen_rect = ScreenRectangle(height=6) screen_rect.next_to(url, DOWN) self.add(url) self.play(ShowCreation(screen_rect)) self.wait() class TheMotionOfPlanets(Scene): CONFIG = { "camera_config": {"background_opacity": 1}, "random_seed": 2, } def construct(self): self.add_title() self.setup_orbits() def add_title(self): title = TextMobject("``The motion of planets around the sun''") title.set_color(YELLOW) title.to_edge(UP) title.add_to_back(title.copy().set_stroke(BLACK, 5)) self.add(title) self.title = title def setup_orbits(self): sun = ImageMobject("sun") sun.scale_to_fit_height(0.7) planets, ellipses, orbits = self.get_planets_ellipses_and_orbits(sun) archivist_words = TextMobject( "Judith Goodstein (Caltech archivist)" ) archivist_words.to_corner(UL) archivist_words.shift(1.5 * DOWN) archivist_words.add_background_rectangle() alt_name = TextMobject("David Goodstein (Caltech physicist)") alt_name.next_to(archivist_words, DOWN, aligned_edge=LEFT) alt_name.add_background_rectangle() book = ImageMobject("Lost_Lecture_cover") book.scale_to_fit_height(4) book.next_to(alt_name, DOWN) self.add(SunAnimation(sun)) self.add(ellipses, planets) self.add(self.title) self.add(*orbits) self.add_foreground_mobjects(planets) self.wait(10) self.play( VGroup(ellipses, sun).shift, 3 * RIGHT, FadeInFromDown(archivist_words), Animation(self.title) ) self.add_foreground_mobjects(archivist_words) self.wait(3) self.play(FadeInFromDown(alt_name)) self.add_foreground_mobjects(alt_name) self.wait() self.play(FadeInFromDown(book)) self.wait(15) def get_planets_ellipses_and_orbits(self, sun): planets = VGroup( ImageMobject("mercury"), ImageMobject("venus"), ImageMobject("earth"), ImageMobject("mars"), ImageMobject("comet") ) sizes = [0.383, 0.95, 1.0, 0.532, 0.3] orbit_radii = [0.254, 0.475, 0.656, 1.0, 3.0] orbit_eccentricies = [0.206, 0.006, 0.0167, 0.0934, 0.967] for planet, size in zip(planets, sizes): planet.scale_to_fit_height(0.5) planet.scale(size) ellipses = VGroup(*[ Circle(radius=r, color=WHITE, stroke_width=1) for r in orbit_radii ]) for circle, ec in zip(ellipses, orbit_eccentricies): a = circle.get_height() / 2 c = ec * a b = np.sqrt(a**2 - c**2) circle.stretch(b / a, 1) c = np.sqrt(a**2 - b**2) circle.shift(c * RIGHT) for circle in ellipses: circle.rotate( TAU * np.random.random(), about_point=ORIGIN ) ellipses.scale(3.5, about_point=ORIGIN) orbits = [ Orbiting( planet, sun, circle, rate=0.25 * r**(2 / 3) ) for planet, circle, r in zip(planets, ellipses, orbit_radii) ] orbits[-1].proportion = 0.15 orbits[-1].rate = 0.5 return planets, ellipses, orbits class AskAboutEllipses(TheMotionOfPlanets): CONFIG = { "camera_config": {"background_opacity": 1}, "animate_sun": True, } def construct(self): self.add_title() self.add_sun() self.add_orbit() self.add_focus_lines() self.add_force_labels() self.comment_on_imperfections() self.set_up_differential_equations() def add_title(self): title = Title("Why are orbits ellipses?") self.add(title) self.title = title def add_sun(self): sun = ImageMobject("sun", height=0.5) self.sun = sun self.add(sun) if self.animate_sun: self.add(SunAnimation(sun)) def add_orbit(self): sun = self.sun comet = ImageMobject("comet") comet.scale_to_fit_height(0.2) ellipse = self.get_ellipse() orbit = Orbiting(comet, sun, ellipse) self.add(ellipse) self.add(orbit) self.ellipse = ellipse self.comet = comet self.orbit = orbit def add_focus_lines(self): f1, f2 = self.focus_points comet = self.comet lines = VGroup(Line(LEFT, RIGHT), Line(LEFT, RIGHT)) lines.set_stroke(LIGHT_GREY, 1) def update_lines(lines): l1, l2 = lines P = comet.get_center() l1.put_start_and_end_on(f1, P) l2.put_start_and_end_on(f2, P) return lines animation = ContinualUpdateFromFunc( lines, update_lines ) self.add(animation) self.wait(8) self.focus_lines = lines self.focus_lines_animation = animation def add_force_labels(self): radial_line = self.focus_lines[0] # Radial line measurement radius_measurement_kwargs = { "num_decimal_places": 3, "color": BLUE, } radius_measurement = DecimalNumber(1, **radius_measurement_kwargs) def update_radial_measurement(measurement): angle = -radial_line.get_angle() + np.pi radial_line.rotate(angle, about_point=ORIGIN) new_decimal = DecimalNumber( radial_line.get_length(), **radius_measurement_kwargs ) max_width = 0.6 * radial_line.get_width() if new_decimal.get_width() > max_width: new_decimal.scale_to_fit_width(max_width) new_decimal.next_to(radial_line, UP, SMALL_BUFF) VGroup(new_decimal, radial_line).rotate( -angle, about_point=ORIGIN ) Transform(measurement, new_decimal).update(1) radius_measurement_animation = ContinualUpdateFromFunc( radius_measurement, update_radial_measurement ) # Force equation force_equation = TexMobject( "F = {GMm \\over (0.000)^2}", tex_to_color_map={ "F": YELLOW, "0.000": BLACK, } ) force_equation.next_to(self.title, DOWN) force_equation.to_edge(RIGHT) radius_in_denominator_ref = force_equation.get_part_by_tex("0.000") radius_in_denominator = DecimalNumber( 0, **radius_measurement_kwargs ) radius_in_denominator.scale(0.95) update_radius_in_denominator = ContinualChangingDecimal( radius_in_denominator, lambda a: radial_line.get_length(), position_update_func=lambda mob: mob.move_to( radius_in_denominator_ref, LEFT ) ) # Force arrow force_arrow = Arrow(LEFT, RIGHT, color=YELLOW) def update_force_arrow(arrow): radius = radial_line.get_length() # target_length = 1 / radius**2 target_length = 1 / radius # Lies! arrow.scale( target_length / arrow.get_length() ) arrow.rotate( np.pi + radial_line.get_angle() - arrow.get_angle() ) arrow.shift( radial_line.get_end() - arrow.get_start() ) force_arrow_animation = ContinualUpdateFromFunc( force_arrow, update_force_arrow ) inverse_square_law_words = TextMobject( "``Inverse square law''" ) inverse_square_law_words.next_to(force_equation, DOWN, MED_LARGE_BUFF) inverse_square_law_words.to_edge(RIGHT) force_equation.next_to(inverse_square_law_words, UP, MED_LARGE_BUFF) def v_fade_in(mobject): return UpdateFromAlphaFunc( mobject, lambda mob, alpha: mob.set_fill(opacity=alpha) ) self.add(update_radius_in_denominator) self.add(radius_measurement_animation) self.play( FadeIn(force_equation), v_fade_in(radius_in_denominator), v_fade_in(radius_measurement) ) self.add(force_arrow_animation) self.play(v_fade_in(force_arrow)) self.wait(8) self.play(Write(inverse_square_law_words)) self.wait(9) self.force_equation = force_equation self.inverse_square_law_words = inverse_square_law_words self.force_arrow = force_arrow self.radius_measurement = radius_measurement def comment_on_imperfections(self): planets, ellipses, orbits = self.get_planets_ellipses_and_orbits(self.sun) orbits.pop(-1) ellipses.submobjects.pop(-1) planets.submobjects.pop(-1) scale_factor = 20 center = self.sun.get_center() ellipses.save_state() ellipses.scale(scale_factor, about_point=center) self.add(*orbits) self.play(ellipses.restore, Animation(planets)) self.wait(7) self.play( ellipses.scale, scale_factor, {"about_point": center}, Animation(planets) ) self.remove(*orbits) self.remove(planets, ellipses) self.wait(2) def set_up_differential_equations(self): d_dt = TexMobject("{d \\over dt}") in_vect = Matrix(np.array([ "x(t)", "y(t)", "\\dot{x}(t)", "\\dot{y}(t)", ])) equals = TexMobject("=") out_vect = Matrix(np.array([ "\\dot{x}(t)", "\\dot{y}(t)", "-x(t) / (x(t)^2 + y(t)^2)^{3/2}", "-y(t) / (x(t)^2 + y(t)^2)^{3/2}", ]), element_alignment_corner=ORIGIN) equation = VGroup(d_dt, in_vect, equals, out_vect) equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF) equation.scale_to_fit_width(6) equation.to_corner(DR, buff=MED_LARGE_BUFF) cross = Cross(equation) self.play(Write(equation)) self.wait(6) self.play(ShowCreation(cross)) self.wait(6) # Helpers def get_ellipse(self): a = 7.0 b = 4.0 c = np.sqrt(a**2 - b**2) ellipse = Circle(radius=a / 2) ellipse.set_stroke(WHITE, 1) ellipse.stretch(b / a, dim=1) ellipse.move_to( self.sun.get_center() + c * LEFT / 2 ) self.focus_points = [ self.sun.get_center(), self.sun.get_center() + c * LEFT, ] return ellipse class FeynmanElementaryQuote(Scene): def construct(self): quote_text = """ \\large I am going to give what I will call an \\emph{elementary} demonstration. But elementary does not mean easy to understand. Elementary means that very little is required to know ahead of time in order to understand it, except to have an infinite amount of intelligence. """ quote_parts = filter(lambda s: s, quote_text.split(" ")) quote = TextMobject( *quote_parts, tex_to_color_map={ "\\emph{elementary}": BLUE, "elementary": BLUE, "Elementary": BLUE, "infinite": YELLOW, "amount": YELLOW, "of": YELLOW, "intelligence": YELLOW, "very": RED, "little": RED, }, alignment="" ) quote[-1].shift(2 * SMALL_BUFF * LEFT) quote.scale_to_fit_width(FRAME_WIDTH - 1) quote.to_edge(UP) quote.get_part_by_tex("of").set_color(WHITE) nothing = TextMobject("nothing") nothing.scale(0.9) very = quote.get_part_by_tex("very") nothing.shift(very[0].get_left() - nothing[0].get_left()) nothing.set_color(RED) for word in quote: if word is very: self.add_foreground_mobjects(nothing) self.play(ShowWord(nothing)) self.wait(0.2) nothing.sort_submobjects(lambda p: -p[0]) self.play(LaggedStart( FadeOut, nothing, run_time=1 )) self.remove_foreground_mobject(nothing) back_word = word.copy().set_stroke(BLACK, 5) self.add_foreground_mobjects(back_word, word) self.play( ShowWord(back_word), ShowWord(word), ) self.wait(0.005 * len(word)**1.5) class LostLecturePicture(TODOStub): CONFIG = {"camera_config": {"background_opacity": 1}} def construct(self): picture = ImageMobject("Feynman_teaching") picture.scale_to_fit_height(FRAME_WIDTH) picture.to_corner(UL, buff=0) picture.fade(0.5) self.play( picture.to_corner, DR, {"buff": 0}, picture.shift, 1.5 * DOWN, path_arc=60 * DEGREES, run_time=20, rate_func=bezier([0, 0, 1, 1]) ) class AskAboutInfiniteIntelligence(TeacherStudentsScene): def construct(self): self.student_says( "Infinite intelligence?", target_mode="confused" ) self.play( self.get_student_changes("horrified", "confused", "sad"), self.teacher.change, "happy", ) self.wait() self.teacher_says( "It's not too bad, \\\\ but stay focused", added_anims=[self.get_student_changes(*["happy"] * 3)] ) self.wait() self.look_at(self.screen) self.wait(5) class ShowEllipseDefiningProperty(Scene): CONFIG = { "ellipse_color": BLUE, "a": 4.0, "b": 3.0, } def construct(self): self.add_ellipse() self.add_focal_lines() self.add_distance_labels() self.show_constant_sum() self.label_foci() self.label_focal_sum() def add_ellipse(self): a = self.a b = self.b c = np.sqrt(a**2 - b**2) ellipse = Circle(radius=a, color=self.ellipse_color) ellipse.stretch(fdiv(b, a), dim=1) self.foci = [c * LEFT, c * RIGHT] self.ellipse = ellipse self.add(ellipse) def add_focal_lines(self): pass def add_distance_labels(self): pass def show_constant_sum(self): pass def label_foci(self): pass def label_focal_sum(self): pass