Beispiel #1
0
    def __init__(self, **kwargs):
        SVGMobject.__init__(self, **kwargs)
        self.scale_to_fit_height(self.height)
        self.set_stroke(color = WHITE, width = 0)
        self.set_fill(self.color, opacity = 1)

        from topics.characters import Randolph
        randy = Randolph(mode = "happy")
        randy.scale_to_fit_height(0.6*self.get_height())
        randy.stretch(0.8, 0)
        randy.look(RIGHT)
        randy.move_to(self)
        randy.shift(0.07*self.height*(RIGHT+UP))
        self.randy = self.pi_creature = randy
        self.add_to_back(randy)

        orientation_line = Line(self.get_left(), self.get_right())
        orientation_line.set_stroke(width = 0)
        self.add(orientation_line)
        self.orientation_line = orientation_line

        for light, color in zip(self.get_lights(), self.light_colors):
            light.set_fill(color, 1)
            light.is_subpath = False

        self.add_treds_to_tires()
Beispiel #2
0
class TransformOverIncreasingOrders(SpaceFillingCurveScene):
    def setup(self, CurveClass):
        sample = CurveClass(order = 1)
        self.curve = Line(3*LEFT, 3*RIGHT)
        self.curve.gradient_highlight(
            sample.start_color, 
            sample.end_color
        )
        self.CurveClass = CurveClass
        self.order = 0

    def construct(self, CurveClass, max_order):
        self.setup(CurveClass)
        while self.order < max_order:
            self.increase_order()
        self.dither()

    def increase_order(self, *other_anims):
        self.order += 1
        new_curve = self.CurveClass(order = self.order)
        self.play(
            Transform(self.curve, new_curve),
            *other_anims,
            run_time = 3/np.sqrt(self.order)
        )
Beispiel #3
0
class TransformOverIncreasingOrders(SpaceFillingCurveScene):
    def setup(self, CurveClass):
        sample = CurveClass(order = 1)
        self.curve = Line(3*LEFT, 3*RIGHT)
        self.curve.gradient_highlight(
            sample.start_color, 
            sample.end_color
        )
        self.CurveClass = CurveClass
        self.order = 0

    def construct(self, CurveClass, max_order):
        self.setup(CurveClass)
        while self.order < max_order:
            self.increase_order()
        self.dither()

    def increase_order(self, *other_anims):
        self.order += 1
        new_curve = self.CurveClass(order = self.order)
        self.play(
            Transform(self.curve, new_curve),
            *other_anims,
            run_time = 3/np.sqrt(self.order)
        )
Beispiel #4
0
    def construct(self):
        curve = HilbertCurve(order = 6)
        curve.highlight(WHITE)
        colored_curve = curve.copy()
        colored_curve.thin_out(3)
        lion = ImageMobject("lion", invert = False)
        lion.replace(curve, stretch = True)
        sparce_lion = lion.copy()
        sparce_lion.thin_out(100)
        distance_matrix = cdist(colored_curve.points, sparce_lion.points)
        closest_point_indices = np.apply_along_axis(
            np.argmin, 1, distance_matrix
        )
        colored_curve.rgbas = sparce_lion.rgbas[closest_point_indices]
        line = Line(5*LEFT, 5*RIGHT)
        Mobject.align_data(line, colored_curve)
        line.rgbas = colored_curve.rgbas

        self.add(lion)
        self.play(ShowCreation(curve, run_time = 3))
        self.play(
            FadeOut(lion),
            Transform(curve, colored_curve),
            run_time = 3
        )
        self.wait()
        self.play(Transform(curve, line, run_time = 5))
        self.wait()
Beispiel #5
0
 def generate_points(self):
     self.axes = VMobject()
     self.main_lines = VMobject()
     self.secondary_lines = VMobject()
     tuples = [
         (self.x_radius, self.x_line_frequency, self.y_radius * DOWN,
          self.y_radius * UP, RIGHT),
         (
             self.y_radius,
             self.y_line_frequency,
             self.x_radius * LEFT,
             self.x_radius * RIGHT,
             UP,
         ),
     ]
     for radius, freq, start, end, unit in tuples:
         main_range = np.arange(0, radius, freq)
         step = freq / float(freq + self.secondary_line_ratio)
         for v in np.arange(0, radius, step):
             line1 = Line(start + v * unit, end + v * unit)
             line2 = Line(start - v * unit, end - v * unit)
             if v == 0:
                 self.axes.add(line1)
             elif v in main_range:
                 self.main_lines.add(line1, line2)
             else:
                 self.secondary_lines.add(line1, line2)
     self.add(self.secondary_lines, self.main_lines, self.axes)
     self.stretch(self.space_unit_to_x_unit, 0)
     self.stretch(self.space_unit_to_y_unit, 1)
     #Put x_axis before y_axis
     y_axis, x_axis = self.axes.split()
     self.axes = VMobject(x_axis, y_axis)
Beispiel #6
0
    def construct(self):
        curve = HilbertCurve(order = 6)
        curve.highlight(WHITE)
        colored_curve = curve.copy()
        colored_curve.thin_out(3)
        lion = ImageMobject("lion", invert = False)
        lion.replace(curve, stretch = True)
        sparce_lion = lion.copy()
        sparce_lion.thin_out(100)
        distance_matrix = cdist(colored_curve.points, sparce_lion.points)
        closest_point_indices = np.apply_along_axis(
            np.argmin, 1, distance_matrix
        )
        colored_curve.rgbs = sparce_lion.rgbs[closest_point_indices]
        line = Line(5*LEFT, 5*RIGHT)
        Mobject.align_data(line, colored_curve)
        line.rgbs = colored_curve.rgbs

        self.add(lion)
        self.play(ShowCreation(curve, run_time = 3))
        self.play(
            FadeOut(lion),
            Transform(curve, colored_curve),
            run_time = 3
        )
        self.dither()
        self.play(Transform(curve, line, run_time = 5))
        self.dither()
Beispiel #7
0
    def construct(self):
        grid = get_grid()
        grid.sort_points(np.linalg.norm)        
        freq_line = get_freq_line()
        freq_line.sort_points(lambda p : p[0])
        red, blue = Color(RED), Color(BLUE)
        freq_line.gradient_highlight(red, blue)

        colors = [
            Color(rgb = interpolate(
                np.array(red.rgb),
                np.array(blue.rgb),
                alpha
            ))
            for alpha in np.arange(4)/3.
        ]
        string = Line(3*LEFT, 3*RIGHT, color = colors[1])
        vibration = Vibrate(string)
        vibration_copy = vibration.copy()
        vibration_copy.mobject.stroke_width = 1
        sub_vibrations = [
            Vibrate(
                string.copy().shift((n-1)*UP).highlight(colors[n]),
                overtones = 1,
                spatial_period = 6./(n+1),
                temporal_period = 1./(n+1),
                amplitude = 0.5/(n+1)
            )
            for n in range(4)
        ]
        words = TexMobject("&\\vdots \\\\ \\text{thousands }& \\text{of frequencies} \\\\ &\\vdots")
        words.to_edge(UP, buff = 0.1)

        self.add(grid)
        self.dither()
        self.play(DelayByOrder(ApplyMethod(
            grid.gradient_highlight, red, blue
        )))
        self.play(Transform(grid, freq_line))
        self.dither()
        self.play(
            ShimmerIn(
                words,
                rate_func = squish_rate_func(smooth, 0, 0.2)
            ),
            *sub_vibrations,
            run_time = 5
        )
        self.play(
            *[
                TransformAnimations(
                    sub_vib, vibration
                )
                for sub_vib in sub_vibrations
            ]+[FadeOut(words)]
        )
        self.clear()
        self.add(freq_line)
        self.play(vibration)
Beispiel #8
0
 def setup(self, CurveClass):
     sample = CurveClass(order = 1)
     self.curve = Line(3*LEFT, 3*RIGHT)
     self.curve.gradient_highlight(
         sample.start_color, 
         sample.end_color
     )
     self.CurveClass = CurveClass
     self.order = 0
Beispiel #9
0
    def construct(self, order):
        if order == 2:
            result_tex = "(0.125, 0.75)"
        elif order == 3:
            result_tex = "(0.0758,  0.6875)"

        phc, arg, result = TexMobject([
            "\\text{PHC}_%d"%order, 
            "(0.3)", 
            "= %s"%result_tex
        ]).to_edge(UP).split()
        function = TextMobject("Function", size = "\\normal")
        function.shift(phc.get_center()+DOWN+2*LEFT)
        function_arrow = Arrow(function, phc)

        line = Line(5*LEFT, 5*RIGHT)
        curve = HilbertCurve(order = order)
        line.match_colors(curve)
        grid = Grid(2**order, 2**order)
        grid.fade()
        for mob in curve, grid:
            mob.scale(0.7)
        index = int(0.3*line.get_num_points())
        dot1 = Dot(line.points[index])
        arrow1 = Arrow(arg, dot1, buff = 0.1)
        dot2 = Dot(curve.points[index])
        arrow2 = Arrow(result.get_bottom(), dot2, buff = 0.1)

        self.add(phc)
        self.play(
            ShimmerIn(function),
            ShowCreation(function_arrow)
        )
        self.dither()
        self.remove(function_arrow, function)
        self.play(ShowCreation(line))
        self.dither()
        self.play(
            ShimmerIn(arg),
            ShowCreation(arrow1),
            ShowCreation(dot1)
        )
        self.dither()
        self.remove(arrow1)
        self.play(
            FadeIn(grid),            
            Transform(line, curve),
            Transform(dot1, dot2),
            run_time = 2
        )
        self.dither()
        self.play(
            ShimmerIn(result),
            ShowCreation(arrow2)
        )
        self.dither()
Beispiel #10
0
    def construct(self, order):
        if order == 2:
            result_tex = "(0.125, 0.75)"
        elif order == 3:
            result_tex = "(0.0758,  0.6875)"

        phc, arg, result = TexMobject([
            "\\text{PHC}_%d"%order, 
            "(0.3)", 
            "= %s"%result_tex
        ]).to_edge(UP).split()
        function = TextMobject("Function", size = "\\normal")
        function.shift(phc.get_center()+DOWN+2*LEFT)
        function_arrow = Arrow(function, phc)

        line = Line(5*LEFT, 5*RIGHT)
        curve = HilbertCurve(order = order)
        line.match_colors(curve)
        grid = Grid(2**order, 2**order)
        grid.fade()
        for mob in curve, grid:
            mob.scale(0.7)
        index = int(0.3*line.get_num_points())
        dot1 = Dot(line.points[index])
        arrow1 = Arrow(arg, dot1, buff = 0.1)
        dot2 = Dot(curve.points[index])
        arrow2 = Arrow(result.get_bottom(), dot2, buff = 0.1)

        self.add(phc)
        self.play(
            ShimmerIn(function),
            ShowCreation(function_arrow)
        )
        self.wait()
        self.remove(function_arrow, function)
        self.play(ShowCreation(line))
        self.wait()
        self.play(
            ShimmerIn(arg),
            ShowCreation(arrow1),
            ShowCreation(dot1)
        )
        self.wait()
        self.remove(arrow1)
        self.play(
            FadeIn(grid),            
            Transform(line, curve),
            Transform(dot1, dot2),
            run_time = 2
        )
        self.wait()
        self.play(
            ShimmerIn(result),
            ShowCreation(arrow2)
        )
        self.wait()
Beispiel #11
0
 def generate_points(self):
     self.main_line = Line(self.x_min * RIGHT, self.x_max * RIGHT)
     self.tick_marks = VMobject()
     self.add(self.main_line, self.tick_marks)
     for x in self.get_tick_numbers():
         self.add_tick(x, self.tick_size)
     for x in self.numbers_with_elongated_ticks:
         self.add_tick(x, self.longer_tick_multiple * self.tick_size)
     self.stretch(self.space_unit_to_num, 0)
     self.shift(-self.number_to_point(self.number_at_center))
Beispiel #12
0
    def coords_to_vector(self,
                         vector,
                         coords_start=2 * RIGHT + 2 * UP,
                         clean_up=True):
        starting_mobjects = list(self.mobjects)
        array = Matrix(vector)
        array.shift(coords_start)
        arrow = Vector(vector)
        x_line = Line(ORIGIN, vector[0] * RIGHT)
        y_line = Line(x_line.get_end(), arrow.get_end())
        x_line.highlight(X_COLOR)
        y_line.highlight(Y_COLOR)
        x_coord, y_coord = array.get_mob_matrix().flatten()

        self.play(Write(array, run_time=1))
        self.dither()
        self.play(
            ApplyFunction(
                lambda x: self.position_x_coordinate(x, x_line, vector),
                x_coord))
        self.play(ShowCreation(x_line))
        self.play(
            ApplyFunction(
                lambda y: self.position_y_coordinate(y, y_line, vector),
                y_coord), FadeOut(array.get_brackets()))
        y_coord, brackets = self.get_mobjects_from_last_animation()
        self.play(ShowCreation(y_line))
        self.play(ShowCreation(arrow))
        self.dither()
        if clean_up:
            self.clear()
            self.add(*starting_mobjects)
Beispiel #13
0
    def construct(self, order):
        start_color, end_color = RED, GREEN
        curve = HilbertCurve(order = order)
        line = Line(5*LEFT, 5*RIGHT)
        for mob in curve, line:
            mob.gradient_highlight(start_color, end_color)
        freq_line = get_freq_line()
        freq_line.replace(line, stretch = True)

        unit = 6./(2**order) #sidelength of pixel
        up = unit*UP
        right = unit*RIGHT
        lower_left = 3*(LEFT+DOWN)
        squares = Mobject(*[
            Square(
                side_length = unit, 
                color = WHITE
            ).shift(x*right+y*up)
            for x, y in it.product(range(2**order), range(2**order))
        ])
        squares.center()
        targets = Mobject()
        for square in squares.submobjects:
            center = square.get_center()
            distances = np.apply_along_axis(
                lambda p : np.linalg.norm(p-center),
                1,
                curve.points
            )
            index_along_curve = np.argmin(distances)
            fraction_along_curve = index_along_curve/float(curve.get_num_points())
            target = square.copy().center().scale(0.8/(2**order))
            line_index = int(fraction_along_curve*line.get_num_points())
            target.shift(line.points[line_index])
            targets.add(target)


        self.add(squares)
        self.play(ShowCreation(
            curve,
            run_time = 5, 
            rate_func = None
        ))
        self.dither()
        self.play(
            Transform(curve, line),
            Transform(squares, targets),
            run_time = 3
        )
        self.dither()
        self.play(ShowCreation(freq_line))
        self.dither()
Beispiel #14
0
 def construct(self, max_order):
     sample = self.curve_class(order = 1)
     curve = Line(sample.radius*LEFT, sample.radius*RIGHT)
     curve.gradient_highlight(
         sample.start_color, 
         sample.end_color
     )
     for order in range(1, max_order):
         new_curve = self.curve_class(order = order)
         self.play(
             Transform(curve, new_curve),
             run_time = 3/np.sqrt(order),
         )
     self.dither()
Beispiel #15
0
    def generate_points(self):
        self.main_line = Line(self.x_min*RIGHT, self.x_max*RIGHT)
        self.tick_marks = VGroup()
        self.add(self.main_line, self.tick_marks)

        for x in self.get_tick_numbers():
            if x in self.numbers_with_elongated_ticks:
                tick_size_used = self.longer_tick_multiple*self.tick_size
            else:
                tick_size_used = self.tick_size
            self.add_tick(x, tick_size_used)

        self.stretch(self.unit_size, 0)
        self.shift(-self.number_to_point(self.number_at_center))
Beispiel #16
0
    def add_lines(self, left, right):
        line_kwargs = {
            "color": BLUE,
            "stroke_width": 2,
        }
        left_rows = [VMobject(*row) for row in left.get_mob_matrix()]
        h_lines = VMobject()
        for row in left_rows[:-1]:
            h_line = Line(row.get_left(), row.get_right(), **line_kwargs)
            h_line.next_to(row, DOWN, buff=left.v_buff / 2.)
            h_lines.add(h_line)

        right_cols = [
            VMobject(*col) for col in np.transpose(right.get_mob_matrix())
        ]
        v_lines = VMobject()
        for col in right_cols[:-1]:
            v_line = Line(col.get_top(), col.get_bottom(), **line_kwargs)
            v_line.next_to(col, RIGHT, buff=right.h_buff / 2.)
            v_lines.add(v_line)

        self.play(ShowCreation(h_lines))
        self.play(ShowCreation(v_lines))
        self.wait()
        self.show_frame()
Beispiel #17
0
    def __init__(self, **kwargs):
        circle = Circle()
        ticks = []
        for x in range(12):
            alpha = x / 12.
            point = complex_to_R3(np.exp(2 * np.pi * alpha * complex(0, 1)))
            length = 0.2 if x % 3 == 0 else 0.1
            ticks.append(Line(point, (1 - length) * point))
        self.hour_hand = Line(ORIGIN, 0.3 * UP)
        self.minute_hand = Line(ORIGIN, 0.6 * UP)
        # for hand in self.hour_hand, self.minute_hand:
        #     #Balance out where the center is
        #     hand.add(VectorizedPoint(-hand.get_end()))

        VGroup.__init__(self, circle, self.hour_hand, self.minute_hand, *ticks)
Beispiel #18
0
 def add_treds_to_tires(self):
     for tire in self.get_tires():
         radius = tire.get_width() / 2
         center = tire.get_center()
         tred = Line(0.9 * radius * RIGHT,
                     1.4 * radius * RIGHT,
                     stroke_width=2,
                     color=BLACK)
         tred.rotate_in_place(np.pi / 4)
         for theta in np.arange(0, 2 * np.pi, np.pi / 4):
             new_tred = tred.copy()
             new_tred.rotate(theta, about_point=ORIGIN)
             new_tred.shift(center)
             tire.add(new_tred)
     return self
Beispiel #19
0
    def construct(self):
        big_grid_dim = 20.
        small_grid_dim = 6.
        big_grid = Grid(64, 64, height=big_grid_dim, width=big_grid_dim)
        big_grid.to_corner(UP + RIGHT, buff=2)
        small_grid = big_grid.copy()
        small_grid.scale(small_grid_dim / big_grid_dim)
        small_grid.center()
        pixel = MobjectFromRegion(
            region_from_polygon_vertices(
                *0.2 *
                np.array([RIGHT + DOWN, RIGHT + UP, LEFT + UP, LEFT + DOWN])))
        pixel.set_color(WHITE)
        pixel_width = big_grid.width / big_grid.columns
        pixel.scale_to_fit_width(pixel_width)
        pixel.to_corner(UP + RIGHT, buff=2)
        pixel.shift(5 * pixel_width * (2 * LEFT + DOWN))

        freq_line = get_freq_line()
        dot = Dot()
        dot.shift(freq_line.get_center() + 2 * RIGHT)
        string = Line(LEFT, RIGHT, color=GREEN)
        arrow = Arrow(dot, string.get_center())
        vibration_config = {
            "overtones": 1,
            "spatial_period": 2,
        }
        vibration, loud_vibration, quiet_vibration = [
            Vibrate(string.copy(), amplitude=a, **vibration_config)
            for a in [0.5, 1., 0.25]
        ]

        self.add(small_grid)
        self.dither()
        self.play(Transform(small_grid, big_grid))
        self.play(FadeIn(pixel))
        self.dither()
        self.play(FadeOut(small_grid), ShowCreation(freq_line))
        self.remove(small_grid)
        self.play(Transform(pixel, dot), )
        self.dither()
        self.play(ShowCreation(arrow))
        self.play(loud_vibration)
        self.play(TransformAnimations(loud_vibration, quiet_vibration),
                  ApplyMethod(dot.fade, 0.9))
        self.clear()
        self.add(freq_line, dot, arrow)
        self.play(quiet_vibration)
Beispiel #20
0
    def construct(self):
        words = TextMobject("Order 2 Pseudo-Hilbert Curve")
        words.to_edge(UP, buff=0.3)
        words.highlight(GREEN)
        grid2 = Grid(2, 2)
        grid4 = Grid(4, 4, point_thickness=2)
        # order_1_curve = HilbertCurve(order = 1)
        # squaggle_curve = order_1_curve.copy().apply_function(
        #     lambda (x, y, z) : (x + np.cos(3*y), y + np.sin(3*x), z)
        # )
        # squaggle_curve.show()
        mini_curves = [
            HilbertCurve(order=1).scale(0.5).shift(1.5 * vect)
            for vect in [LEFT + DOWN, LEFT + UP, RIGHT + UP, RIGHT + DOWN]
        ]
        last_curve = mini_curves[0]
        naive_curve = Mobject(last_curve)
        for mini_curve in mini_curves[1:]:
            line = Line(last_curve.points[-1], mini_curve.points[0])
            naive_curve.add(line, mini_curve)
            last_curve = mini_curve
        naive_curve.ingest_sub_mobjects()
        naive_curve.gradient_highlight(RED, GREEN)
        order_2_curve = HilbertCurve(order=2)

        self.add(words, grid2)
        self.dither()
        self.play(ShowCreation(grid4))
        self.play(*[ShowCreation(mini_curve) for mini_curve in mini_curves])
        self.dither()
        self.play(ShowCreation(naive_curve, run_time=5))
        self.remove(*mini_curves)
        self.dither()
        self.play(Transform(naive_curve, order_2_curve))
        self.dither()
Beispiel #21
0
    def __init__(self, source, target):
        matching_line = Line(0.5 * DOWN, 0.5 * UP)

        def matched_objects(source, target):
            matched = VGroup(source.copy(), matching_line.copy(),
                             target.copy())
            matched.arrange_submobjects(direction=DOWN)
            return matched

        final_mobj = VGroup(
            *(matched_objects(s, t)
              for (s, t) in zip(source.submobjects, target.submobjects)))
        self.buff = 2 * DEFAULT_MOBJECT_TO_MOBJECT_BUFFER
        final_mobj.arrange_submobjects(buff=self.buff)
        final_mobj.center()

        def match_animation(source, target, matched):
            return AnimationGroup(Transform(source, matched.submobjects[0]),
                                  ShowCreation(matched.submobjects[1]),
                                  Transform(target, matched.submobjects[2]))

        self.match_animations = [
            match_animation(s, t, m) for (s, t, m) in zip(
                source.submobjects, target.submobjects, final_mobj.submobjects)
        ]
        self.matching = VGroup(*[mob[1] for mob in final_mobj])
Beispiel #22
0
 def add_tick(self, x, size):
     self.tick_marks.add(
         Line(
             x * RIGHT + size * DOWN,
             x * RIGHT + size * UP,
         ))
     return self
Beispiel #23
0
    def generate_points(self):
        start_angle = np.pi / 2 + self.arc_angle / 2
        end_angle = np.pi / 2 - self.arc_angle / 2
        self.add(Arc(start_angle=start_angle, angle=-self.arc_angle))
        tick_angle_range = np.linspace(start_angle, end_angle, self.num_ticks)
        for index, angle in enumerate(tick_angle_range):
            vect = rotate_vector(RIGHT, angle)
            tick = Line((1 - self.tick_length) * vect, vect)
            label = TexMobject(str(10 * index))
            label.scale_to_fit_height(self.tick_length)
            label.shift((1 + self.tick_length) * vect)
            self.add(tick, label)

        needle = Polygon(LEFT,
                         UP,
                         RIGHT,
                         stroke_width=0,
                         fill_opacity=1,
                         fill_color=self.needle_color)
        needle.stretch_to_fit_width(self.needle_width)
        needle.stretch_to_fit_height(self.needle_height)
        needle.rotate(start_angle - np.pi / 2)
        self.add(needle)
        self.needle = needle

        self.center_offset = self.get_center()
Beispiel #24
0
    def get_circles_and_points(self, min_input, max_input):
        input_left, input_right = [
            self.interval.number_to_point(num)
            for num in min_input, max_input
        ]
        input_circle = Circle(
            radius = np.linalg.norm(input_left-input_right)/2,
            color = WHITE
        )
        input_circle.shift((input_left+input_right)/2)

        input_points = Line(
            input_left, input_right, 
            color = self.input_color
        )
        output_points = Mobject(color = self.output_color)
        n = self.output.get_num_points()
        output_points.add_points(
            self.output.points[int(min_input*n):int(max_input*n)]
        )
        output_center = output_points.points[int(0.5*output_points.get_num_points())]
        max_distance = np.linalg.norm(output_center-output_points.points[-1])
        output_circle = Circle(
            radius = max_distance, 
            color = WHITE
        )
        output_circle.shift(output_center)
        return (
            input_circle, 
            input_points, 
            output_circle, 
            output_points
        )
Beispiel #25
0
    def __init__(self, camera):
        VMobject.__init__(self)

        stroke = 20
        factor = sum(PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_shape"]) / sum(
            camera.pixel_shape)
        stroke /= factor
        self.add(Circle())
        h1, h2, x = 1, 1.5, 0.5
        y = -np.sqrt(1 - x**2) + h1
        self.add(Line([-x, y, 0], [-x, y - h1, 0]))
        self.add(Line([0, 1, 0], [0, 1 - h2, 0]))
        self.add(Line([x, y, 0], [x, y - h1, 0]))

        self.set_stroke(color="#00ffff", width=stroke)
        self.to_corner(RIGHT + DOWN)
Beispiel #26
0
    def coords_to_vector(self, vector, coords_start = 2*RIGHT+2*UP, clean_up = True):
        starting_mobjects = list(self.mobjects)
        array = Matrix(vector)
        array.shift(coords_start)
        arrow = Vector(vector)
        x_line = Line(ORIGIN, vector[0]*RIGHT)
        y_line = Line(x_line.get_end(), arrow.get_end())
        x_line.highlight(X_COLOR)
        y_line.highlight(Y_COLOR)
        x_coord, y_coord = array.get_mob_matrix().flatten()

        self.play(Write(array, run_time = 1))
        self.dither()
        self.play(ApplyFunction(
            lambda x : self.position_x_coordinate(x, x_line, vector),
            x_coord
        ))
        self.play(ShowCreation(x_line))
        self.play(
            ApplyFunction(
                lambda y : self.position_y_coordinate(y, y_line, vector),
                y_coord
            ),
            FadeOut(array.get_brackets())
        )
        y_coord, brackets = self.get_mobjects_from_last_animation()
        self.play(ShowCreation(y_line))
        self.play(ShowCreation(arrow))
        self.dither()
        if clean_up:
            self.clear()
            self.add(*starting_mobjects)
Beispiel #27
0
 def generate_points(self):
     if self.x_radius is None:
         center_to_edge = (SPACE_WIDTH + abs(self.center_point[0])) 
         self.x_radius = center_to_edge / self.x_unit_size
     if self.y_radius is None:
         center_to_edge = (SPACE_HEIGHT + abs(self.center_point[1])) 
         self.y_radius = center_to_edge / self.y_unit_size
     self.axes = VMobject()
     self.main_lines = VMobject()
     self.secondary_lines = VMobject()
     tuples = [
         (
             self.x_radius, 
             self.x_line_frequency, 
             self.y_radius*DOWN, 
             self.y_radius*UP,
             RIGHT
         ),
         (
             self.y_radius, 
             self.y_line_frequency, 
             self.x_radius*LEFT, 
             self.x_radius*RIGHT,
             UP,
         ),
     ]
     for radius, freq, start, end, unit in tuples:
         main_range = np.arange(0, radius, freq)
         step = freq/float(freq + self.secondary_line_ratio)
         for v in np.arange(0, radius, step):
             line1 = Line(start+v*unit, end+v*unit)
             line2 = Line(start-v*unit, end-v*unit)                
             if v == 0:
                 self.axes.add(line1)
             elif v in main_range:
                 self.main_lines.add(line1, line2)
             else:
                 self.secondary_lines.add(line1, line2)
     self.add(self.secondary_lines, self.main_lines, self.axes)
     self.stretch(self.x_unit_size, 0)
     self.stretch(self.y_unit_size, 1)
     self.shift(self.center_point)
     #Put x_axis before y_axis
     y_axis, x_axis = self.axes.split()
     self.axes = VMobject(x_axis, y_axis)
Beispiel #28
0
    def generate_points(self):
        self.main_line = Line(self.x_min * RIGHT, self.x_max * RIGHT)
        self.tick_marks = VGroup()
        self.add(self.main_line, self.tick_marks)
        rounding_value = int(-np.log10(0.1 * self.tick_frequency))
        rounded_numbers_with_elongated_ticks = np.round(
            self.numbers_with_elongated_ticks, rounding_value)

        for x in self.get_tick_numbers():
            rounded_x = np.round(x, rounding_value)
            if rounded_x in rounded_numbers_with_elongated_ticks:
                tick_size_used = self.longer_tick_multiple * self.tick_size
            else:
                tick_size_used = self.tick_size
            self.add_tick(x, tick_size_used)

        self.stretch(self.unit_size, 0)
        self.shift(-self.number_to_point(self.number_at_center))
Beispiel #29
0
 def setup(self, CurveClass):
     sample = CurveClass(order = 1)
     self.curve = Line(3*LEFT, 3*RIGHT)
     self.curve.gradient_highlight(
         sample.start_color, 
         sample.end_color
     )
     self.CurveClass = CurveClass
     self.order = 0
Beispiel #30
0
 def generate_points(self):
     self.main_line = Line(self.x_min*RIGHT, self.x_max*RIGHT)
     self.tick_marks = VMobject()
     self.add(self.main_line, self.tick_marks)
     for x in self.get_tick_numbers():
         self.add_tick(x, self.tick_size)
     for x in self.numbers_with_elongated_ticks:
         self.add_tick(x, self.longer_tick_multiple*self.tick_size)
     self.stretch(self.unit_size, 0)
     self.shift(-self.number_to_point(self.number_at_center))
Beispiel #31
0
 def generate_points(self):
     complex_power = 0.9
     radius = self.initial_width / 2
     circle = Circle(radius=radius)
     circle.scale(1.0 / radius)
     circle.apply_complex_function(lambda z: z**complex_power)
     circle.scale(radius)
     boundary_point_as_complex = radius * complex(-1)**complex_power
     boundary_points = [[
         boundary_point_as_complex.real,
         unit * boundary_point_as_complex.imag, 0
     ] for unit in -1, 1]
     tip = radius * (1.5 * LEFT + UP)
     self.little_line = Line(boundary_points[0], tip)
     self.circle = circle
     self.add(circle, self.little_line, Line(boundary_points[1], tip))
     self.highlight("white")
     self.rotate(np.pi / 2)
     self.stretch_to_fit_height(self.initial_height)
Beispiel #32
0
    def zoom_in_on(self, number, zoom_factor, run_time=2.0):
        unit_length_to_spatial_width = self.number_line.unit_length_to_spatial_width * zoom_factor
        radius = SPACE_WIDTH / unit_length_to_spatial_width
        tick_frequency = 10**(np.floor(np.log10(radius)))
        left_tick = tick_frequency * (np.ceil(
            (number - radius) / tick_frequency))
        new_number_line = NumberLine(
            numerical_radius=radius,
            unit_length_to_spatial_width=unit_length_to_spatial_width,
            tick_frequency=tick_frequency,
            leftmost_tick=left_tick,
            number_at_center=number)
        new_displayed_numbers = new_number_line.default_numbers_to_display()
        new_number_mobs = new_number_line.get_number_mobjects(
            *new_displayed_numbers)

        transforms = []
        additional_mobjects = []
        squished_new_line = new_number_line.copy()
        squished_new_line.scale(1.0 / zoom_factor)
        squished_new_line.shift(self.number_line.number_to_point(number))
        squished_new_line.points[:, 1] = self.number_line.number_to_point(0)[1]
        transforms.append(Transform(squished_new_line, new_number_line))
        for mob, num in zip(new_number_mobs, new_displayed_numbers):
            point = Point(self.number_line.number_to_point(num))
            point.shift(new_number_line.get_vertical_number_offset())
            transforms.append(Transform(point, mob))
        for mob in self.mobjects:
            if mob == self.number_line:
                new_mob = mob.copy()
                new_mob.shift(-self.number_line.number_to_point(number))
                new_mob.stretch(zoom_factor, 0)
                transforms.append(Transform(mob, new_mob))
                continue
            mob_center = mob.get_center()
            number_under_center = self.number_line.point_to_number(mob_center)
            new_point = new_number_line.number_to_point(number_under_center)
            new_point += mob_center[1] * UP
            if mob in self.number_mobs:
                transforms.append(Transform(mob, Point(new_point)))
            else:
                transforms.append(
                    ApplyMethod(mob.shift, new_point - mob_center))
                additional_mobjects.append(mob)
        line_to_hide_pixelation = Line(self.number_line.get_left(),
                                       self.number_line.get_right(),
                                       color=self.number_line.get_color())
        self.add(line_to_hide_pixelation)
        self.play(*transforms, run_time=run_time)
        self.clear()
        self.number_line = new_number_line
        self.displayed_numbers = new_displayed_numbers
        self.number_mobs = new_number_mobs
        self.add(self.number_line, *self.number_mobs)
        self.add(*additional_mobjects)
Beispiel #33
0
    def generate_points(self):
        body = Cube(side_length = 1)
        for dim, scale_factor in enumerate(self.body_dimensions):
            body.stretch(scale_factor, dim = dim)
        body.scale_to_fit_width(self.width)
        body.set_fill(self.shaded_body_color, opacity = 1)
        body.sort_submobjects(lambda p : p[2])
        body[-1].set_fill(self.body_color)
        keyboard = VGroup(*[
            VGroup(*[
                Square(**self.key_color_kwargs)
                for x in range(12-y%2)
            ]).arrange_submobjects(RIGHT, buff = SMALL_BUFF)
            for y in range(4)
        ]).arrange_submobjects(DOWN, buff = MED_SMALL_BUFF)
        keyboard.stretch_to_fit_width(
            self.keyboard_width_to_body_width*body.get_width(),
        )
        keyboard.stretch_to_fit_height(
            self.keyboard_height_to_body_height*body.get_height(),
        )
        keyboard.next_to(body, OUT, buff = 0.1*SMALL_BUFF)
        keyboard.shift(MED_SMALL_BUFF*UP)
        body.add(keyboard)

        screen_plate = body.copy()
        screen_plate.stretch(self.screen_thickness/self.body_dimensions[2], dim = 2)
        screen = Rectangle(
            stroke_width = 0,
            fill_color = BLACK,
            fill_opacity = 1,
        )
        screen.replace(screen_plate, stretch = True)
        screen.scale_in_place(self.screen_width_to_screen_plate_width)
        screen.next_to(screen_plate, OUT, buff = 0.1*SMALL_BUFF)
        screen_plate.add(screen)
        screen_plate.next_to(body, UP, buff = 0)
        screen_plate.rotate(
            self.open_angle, RIGHT, 
            about_point = screen_plate.get_bottom()
        )
        self.screen_plate = screen_plate
        self.screen = screen

        axis = Line(
            body.get_corner(UP+LEFT+OUT),
            body.get_corner(UP+RIGHT+OUT),
            color = BLACK,
            stroke_width = 2
        )
        self.axis = axis

        self.add(body, screen_plate, axis)
        self.rotate(5*np.pi/12, LEFT)
        self.rotate(np.pi/6, DOWN)
Beispiel #34
0
    def add_lines(self, left, right):
        line_kwargs = {
            "color" : BLUE,
            "stroke_width" : 2,
        }
        left_rows = [
            VMobject(*row) for row in left.get_mob_matrix()
        ]
        h_lines = VMobject()
        for row in left_rows[:-1]:
            h_line = Line(row.get_left(), row.get_right(), **line_kwargs)
            h_line.next_to(row, DOWN, buff = left.v_buff/2.)
            h_lines.add(h_line)

        right_cols = [
            VMobject(*col) for col in np.transpose(right.get_mob_matrix())
        ]
        v_lines = VMobject()
        for col in right_cols[:-1]:
            v_line = Line(col.get_top(), col.get_bottom(), **line_kwargs)
            v_line.next_to(col, RIGHT, buff = right.h_buff/2.)
            v_lines.add(v_line)

        self.play(ShowCreation(h_lines))
        self.play(ShowCreation(v_lines))
        self.dither()
        self.show_frame()
Beispiel #35
0
    def construct(self):
        grid = Grid(64, 64)
        space_region = Region()
        space_mobject = MobjectFromRegion(space_region, DARK_GREY)
        curve = PeanoCurve(order = 5).replace(space_mobject)
        line = Line(5*LEFT, 5*RIGHT)
        line.gradient_highlight(curve.start_color, curve.end_color)
        for mob in grid, space_mobject:
            mob.sort_points(np.linalg.norm)
        infinitely = TextMobject("Infinitely")
        detailed = TextMobject("detailed")
        extending = TextMobject("extending")
        detailed.next_to(infinitely, RIGHT)
        extending.next_to(infinitely, RIGHT)
        Mobject(extending, infinitely, detailed).center()
        arrows = Mobject(*[
            Arrow(2*p, 4*p)
            for theta in np.arange(np.pi/6, 2*np.pi, np.pi/3)
            for p in [rotate_vector(RIGHT, theta)]
        ])

        self.add(grid)
        self.wait()
        self.play(Transform(grid, space_mobject, run_time = 5))
        self.remove(grid)
        self.highlight_region(space_region, DARK_GREY)
        self.wait()
        self.add(infinitely, detailed)
        self.wait()
        self.play(DelayByOrder(Transform(detailed, extending)))
        self.play(ShowCreation(arrows))
        self.wait()
        self.clear()
        self.highlight_region(space_region, DARK_GREY)
        self.play(ShowCreation(line))
        self.play(Transform(line, curve, run_time = 5))
Beispiel #36
0
    def construct(self):
        grid = Grid(64, 64)
        space_region = Region()
        space_mobject = MobjectFromRegion(space_region, DARK_GREY)
        curve = PeanoCurve(order = 5).replace(space_mobject)
        line = Line(5*LEFT, 5*RIGHT)
        line.gradient_highlight(curve.start_color, curve.end_color)
        for mob in grid, space_mobject:
            mob.sort_points(np.linalg.norm)
        infinitely = TextMobject("Infinitely")
        detailed = TextMobject("detailed")
        extending = TextMobject("extending")
        detailed.next_to(infinitely, RIGHT)
        extending.next_to(infinitely, RIGHT)
        Mobject(extending, infinitely, detailed).center()
        arrows = Mobject(*[
            Arrow(2*p, 4*p)
            for theta in np.arange(np.pi/6, 2*np.pi, np.pi/3)
            for p in [rotate_vector(RIGHT, theta)]
        ])

        self.add(grid)
        self.dither()
        self.play(Transform(grid, space_mobject, run_time = 5))
        self.remove(grid)
        self.highlight_region(space_region, DARK_GREY)
        self.dither()
        self.add(infinitely, detailed)
        self.dither()
        self.play(DelayByOrder(Transform(detailed, extending)))
        self.play(ShowCreation(arrows))
        self.dither()
        self.clear()
        self.highlight_region(space_region, DARK_GREY)
        self.play(ShowCreation(line))
        self.play(Transform(line, curve, run_time = 5))
Beispiel #37
0
 def label_spaces(self):
     input_space = TextMobject("Input Space")
     input_space.to_edge(UP)
     input_space.shift(LEFT * SPACE_WIDTH / 2)
     output_space = TextMobject("Output Space")
     output_space.to_edge(UP)
     output_space.shift(RIGHT * SPACE_WIDTH / 2)
     line = Line(UP * SPACE_HEIGHT, DOWN * SPACE_HEIGHT, color=WHITE)
     self.play(
         ShimmerIn(input_space),
         ShimmerIn(output_space),
         ShowCreation(line),
         ShowCreation(self.interval),
     )
     self.dither()
Beispiel #38
0
    def vector_to_coords(self, vector, integer_labels = True, clean_up = True):
        starting_mobjects = list(self.mobjects)
        show_creation = False
        if isinstance(vector, Arrow):
            arrow = vector
            vector = arrow.get_end()[:2]
        else:
            arrow = Vector(vector)
            show_creation = True
        array = vector_coordinate_label(arrow, integer_labels = integer_labels)
        x_line = Line(ORIGIN, vector[0]*RIGHT)
        y_line = Line(x_line.get_end(), arrow.get_end())
        x_line.highlight(X_COLOR)
        y_line.highlight(Y_COLOR)
        x_coord, y_coord = array.get_mob_matrix().flatten()
        x_coord_start = self.position_x_coordinate(
            x_coord.copy(), x_line, vector
        )
        y_coord_start = self.position_y_coordinate(
            y_coord.copy(), y_line, vector
        )
        brackets = array.get_brackets()

        if show_creation:
            self.play(ShowCreation(arrow))
        self.play(
            ShowCreation(x_line),
            Write(x_coord_start),
            run_time = 1
        )
        self.play(
            ShowCreation(y_line),
            Write(y_coord_start),
            run_time = 1
        )
        self.dither()
        self.play(
            Transform(x_coord_start, x_coord, submobject_mode = "all_at_once"),
            Transform(y_coord_start, y_coord, submobject_mode = "all_at_once"),
            Write(brackets, run_time = 1),
        )
        self.dither()

        self.remove(x_coord_start, y_coord_start, brackets)
        self.add(array)
        if clean_up:
            self.clear()
            self.add(*starting_mobjects)
        return array, x_line, y_line
Beispiel #39
0
    def vector_to_coords(self, vector, integer_labels=True, clean_up=True):
        starting_mobjects = list(self.mobjects)
        show_creation = False
        if isinstance(vector, Arrow):
            arrow = vector
            vector = arrow.get_end()[:2]
        else:
            arrow = Vector(vector)
            show_creation = True
        array = vector_coordinate_label(arrow, integer_labels=integer_labels)
        x_line = Line(ORIGIN, vector[0] * RIGHT)
        y_line = Line(x_line.get_end(), arrow.get_end())
        x_line.highlight(X_COLOR)
        y_line.highlight(Y_COLOR)
        x_coord, y_coord = array.get_mob_matrix().flatten()
        x_coord_start = self.position_x_coordinate(x_coord.copy(), x_line,
                                                   vector)
        y_coord_start = self.position_y_coordinate(y_coord.copy(), y_line,
                                                   vector)
        brackets = array.get_brackets()

        if show_creation:
            self.play(ShowCreation(arrow))
        self.play(ShowCreation(x_line), Write(x_coord_start), run_time=1)
        self.play(ShowCreation(y_line), Write(y_coord_start), run_time=1)
        self.dither()
        self.play(
            Transform(x_coord_start, x_coord, submobject_mode="all_at_once"),
            Transform(y_coord_start, y_coord, submobject_mode="all_at_once"),
            Write(brackets, run_time=1),
        )
        self.dither()

        self.remove(x_coord_start, y_coord_start, brackets)
        self.add(array)
        if clean_up:
            self.clear()
            self.add(*starting_mobjects)
        return array, x_line, y_line
Beispiel #40
0
    def construct(self):
        def special_rotate(mob):
            mob.rotate(0.9*np.pi/2, RIGHT)
            mob.rotate(-np.pi/4, UP)
            return mob
        plane = NumberPlane()
        copies = [
            special_rotate(plane.copy().shift(u*n*OUT))
            for n in range(1, 3)
            for u in -1, 1
        ]
        line = Line(4*IN, 4*OUT)


        self.add(plane)
        self.play(*[
            ApplyFunction(special_rotate, mob, run_time = 3)
            for mob in plane, line
        ])
        self.dither()
        for copy in copies:
            self.play(Transform(plane.copy(), copy))
        self.dither()
Beispiel #41
0
    def construct(self):
        big_grid_dim = 20.
        small_grid_dim = 6.
        big_grid = Grid(64, 64, height = big_grid_dim, width = big_grid_dim)
        big_grid.to_corner(UP+RIGHT, buff = 2)
        small_grid = big_grid.copy()
        small_grid.scale(small_grid_dim/big_grid_dim)
        small_grid.center()
        pixel = MobjectFromRegion(
            region_from_polygon_vertices(*0.2*np.array([
                RIGHT+DOWN,
                RIGHT+UP,
                LEFT+UP,
                LEFT+DOWN
            ]))
        )
        pixel.set_color(WHITE)
        pixel_width = big_grid.width/big_grid.columns
        pixel.scale_to_fit_width(pixel_width)
        pixel.to_corner(UP+RIGHT, buff = 2)
        pixel.shift(5*pixel_width*(2*LEFT+DOWN))

        freq_line = get_freq_line()
        dot = Dot()
        dot.shift(freq_line.get_center() + 2*RIGHT)
        string = Line(LEFT, RIGHT, color = GREEN)
        arrow = Arrow(dot, string.get_center())
        vibration_config = {
            "overtones"      : 1,
            "spatial_period" : 2,
        }
        vibration, loud_vibration, quiet_vibration = [
            Vibrate(string.copy(), amplitude = a, **vibration_config)
            for a in [0.5, 1., 0.25]
        ]

        self.add(small_grid)
        self.dither()
        self.play(
            Transform(small_grid, big_grid)
        )
        self.play(FadeIn(pixel))
        self.dither()
        self.play(
            FadeOut(small_grid),            
            ShowCreation(freq_line)
        )
        self.remove(small_grid)
        self.play(
            Transform(pixel, dot),
        )
        self.dither()
        self.play(ShowCreation(arrow))
        self.play(loud_vibration)
        self.play(
            TransformAnimations(loud_vibration, quiet_vibration),            
            ApplyMethod(dot.fade, 0.9)
        )
        self.clear()
        self.add(freq_line, dot, arrow)
        self.play(quiet_vibration)
Beispiel #42
0
class NumberLine(VMobject):
    CONFIG = {
        "color" : BLUE,
        "x_min" : -SPACE_WIDTH,
        "x_max" : SPACE_WIDTH,
        "space_unit_to_num" : 1,
        "tick_size" : 0.1,
        "tick_frequency" : 0.5,
        "leftmost_tick" : None, #Defaults to ceil(x_min)
        "numbers_with_elongated_ticks" : [0],
        "longer_tick_multiple" : 2,
        "number_at_center" : 0,
        "propogate_style_to_family" : True
    }
    def __init__(self, **kwargs):
        digest_config(self, kwargs)
        if self.leftmost_tick is None:
            self.leftmost_tick = np.ceil(self.x_min)
        VMobject.__init__(self, **kwargs)

    def generate_points(self):
        self.main_line = Line(self.x_min*RIGHT, self.x_max*RIGHT)
        self.tick_marks = VMobject()
        self.add(self.main_line, self.tick_marks)
        for x in self.get_tick_numbers():
            self.add_tick(x, self.tick_size)
        for x in self.numbers_with_elongated_ticks:
            self.add_tick(x, self.longer_tick_multiple*self.tick_size)
        self.stretch(self.space_unit_to_num, 0)
        self.shift(-self.number_to_point(self.number_at_center))

    def add_tick(self, x, size):
        self.tick_marks.add(Line(
            x*RIGHT+size*DOWN,
            x*RIGHT+size*UP,
        ))
        return self

    def get_tick_marks(self):
        return self.tick_marks

    def get_tick_numbers(self):
        return np.arange(self.leftmost_tick, self.x_max, self.tick_frequency)

    def number_to_point(self, number):
        return interpolate(
            self.main_line.get_left(),
            self.main_line.get_right(),
            float(number-self.x_min)/(self.x_max - self.x_min)
        )

    def point_to_number(self, point):
        dist_from_left = (point[0]-self.main_line.get_left()[0])
        num_dist_from_left = num_dist_from_left/self.space_unit_to_num
        return self.x_min + dist_from_left

    def default_numbers_to_display(self):
        return self.get_tick_numbers()[::2]

    def get_vertical_number_offset(self, direction = DOWN):
        return 4*direction*self.tick_size

    def get_number_mobjects(self, *numbers, **kwargs):
        #TODO, handle decimals
        if len(numbers) == 0:
            numbers = self.default_numbers_to_display()
        result = []
        for number in numbers:
            mob = TexMobject(str(int(number)))
            mob.scale_to_fit_height(2*self.tick_size)
            mob.shift(
                self.number_to_point(number),
                self.get_vertical_number_offset(**kwargs)
            )
            result.append(mob)
        return result

    def add_numbers(self, *numbers, **kwargs):
        self.numbers = self.get_number_mobjects(
            *numbers, **kwargs
        )
        self.add(*self.numbers)
        return self
Beispiel #43
0
class NumberLine(VMobject):
    CONFIG = {
        "color" : BLUE,
        "x_min" : -SPACE_WIDTH,
        "x_max" : SPACE_WIDTH,
        "unit_size" : 1,
        "tick_size" : 0.1,
        "tick_frequency" : 1,
        "leftmost_tick" : None, #Defaults to ceil(x_min)
        "numbers_with_elongated_ticks" : [0],
        "numbers_to_show" : None,
        "longer_tick_multiple" : 2,
        "number_at_center" : 0,
        "propogate_style_to_family" : True
    }
    def __init__(self, **kwargs):
        digest_config(self, kwargs)
        if self.leftmost_tick is None:
            self.leftmost_tick = np.ceil(self.x_min)
        VMobject.__init__(self, **kwargs)

    def generate_points(self):
        self.main_line = Line(self.x_min*RIGHT, self.x_max*RIGHT)
        self.tick_marks = VMobject()
        self.add(self.main_line, self.tick_marks)
        for x in self.get_tick_numbers():
            self.add_tick(x, self.tick_size)
        for x in self.numbers_with_elongated_ticks:
            self.add_tick(x, self.longer_tick_multiple*self.tick_size)
        self.stretch(self.unit_size, 0)
        self.shift(-self.number_to_point(self.number_at_center))

    def add_tick(self, x, size):
        self.tick_marks.add(Line(
            x*RIGHT+size*DOWN,
            x*RIGHT+size*UP,
        ))
        return self

    def get_tick_marks(self):
        return self.tick_marks

    def get_tick_numbers(self):
        epsilon = 0.001
        return np.arange(
            self.leftmost_tick, self.x_max+epsilon,
            self.tick_frequency
        )

    def number_to_point(self, number):
        alpha = float(number-self.x_min)/(self.x_max - self.x_min)
        return interpolate(
            self.main_line.get_start(),
            self.main_line.get_end(),
            alpha
        )

    def point_to_number(self, point):
        left_point, right_point = self.main_line.get_start_and_end()
        full_vect = right_point-left_point
        def distance_from_left(p):
            return np.dot(p-left_point, full_vect)/np.linalg.norm(full_vect)

        return interpolate(
            self.x_min, self.x_max, 
            distance_from_left(point)/distance_from_left(right_point)
        )

    def default_numbers_to_display(self):
        if self.numbers_to_show is not None:
            return self.numbers_to_show
        return np.arange(self.leftmost_tick, self.x_max, 1)

    def get_vertical_number_offset(self, direction = DOWN):
        return 4*direction*self.tick_size

    def get_number_mobjects(self, *numbers, **kwargs):
        #TODO, handle decimals
        if len(numbers) == 0:
            numbers = self.default_numbers_to_display()
        result = VGroup()
        for number in numbers:
            mob = TexMobject(str(int(number)))
            mob.scale_to_fit_height(3*self.tick_size)
            mob.shift(
                self.number_to_point(number),
                self.get_vertical_number_offset(**kwargs)
            )
            result.add(mob)
        return result

    def add_numbers(self, *numbers, **kwargs):
        self.numbers = self.get_number_mobjects(
            *numbers, **kwargs
        )
        self.add(*self.numbers)
        return self