From e89f193c562c07f3af13493edb05e539dbb2262b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 3 Nov 2022 11:29:43 -0700 Subject: [PATCH 1/6] Add CoordinateSystem.bind_graph_to_func --- manimlib/mobject/coordinate_systems.py | 41 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index 7f42ec80..3ebc4377 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -5,9 +5,9 @@ import numbers import numpy as np -from manimlib.constants import BLACK, BLUE, BLUE_D, GREEN, GREY_A, WHITE +from manimlib.constants import BLACK, BLUE, BLUE_D, GREEN, GREY_A, WHITE, RED from manimlib.constants import DEGREES, PI -from manimlib.constants import DL, DOWN, DR, LEFT, ORIGIN, OUT, RIGHT, UP +from manimlib.constants import DL, UL, DOWN, DR, LEFT, ORIGIN, OUT, RIGHT, UP from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH from manimlib.constants import FRAME_X_RADIUS, FRAME_Y_RADIUS from manimlib.constants import MED_SMALL_BUFF, SMALL_BUFF @@ -19,6 +19,7 @@ from manimlib.mobject.geometry import Rectangle from manimlib.mobject.number_line import NumberLine from manimlib.mobject.svg.tex_mobject import Tex from manimlib.mobject.types.vectorized_mobject import VGroup +from manimlib.mobject.types.dot_cloud import DotCloud from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import merge_dicts_recursively from manimlib.utils.simple_functions import binary_search @@ -233,6 +234,19 @@ class CoordinateSystem(ABC): """ return self.input_to_graph_point(x, graph) + def bind_graph_to_func(self, graph, func, jagged=False): + """ + Use for graphing functions which might change over time, or change with + conditions + """ + graph.x_values = [self.x_axis.p2n(p) for p in graph.get_points()] + graph.add_updater(lambda g: g.set_points([self.c2p(x, func(x)) for x in g.x_values])) + if jagged: + graph.add_updater(lambda g: g.make_jagged()) + else: + graph.add_updater(lambda g: g.make_approximately_smooth()) + return graph + def get_graph_label( self, graph: ParametricCurve, @@ -274,6 +288,12 @@ class CoordinateSystem(ABC): def get_h_line_to_graph(self, x: float, graph: ParametricCurve, **kwargs): return self.get_h_line(self.i2gp(x, graph), **kwargs) + def get_scatterplot(self, + x_values: np.ndarray, + y_values: np.ndarray, + **dot_config): + return DotCloud(self.c2p(x_values, y_values), **dot_config) + # For calculus def angle_of_tangent( self, @@ -316,6 +336,7 @@ class CoordinateSystem(ABC): stroke_color: ManimColor = BLACK, fill_opacity: float = 1, colors: Iterable[ManimColor] = (BLUE, GREEN), + negative_color: ManimColor = RED, stroke_background: bool = True, show_signed_area: bool = True ) -> VGroup: @@ -338,12 +359,13 @@ class CoordinateSystem(ABC): sample = 0.5 * x0 + 0.5 * x1 else: raise Exception("Invalid input sample type") - height = get_norm( - self.i2gp(sample, graph) - self.c2p(sample, 0) + height_vect = self.i2gp(sample, graph) - self.c2p(sample, 0) + rect = Rectangle( + width=self.x_axis.n2p(x1)[0] - self.x_axis.n2p(x0)[0], + height=get_norm(height_vect), ) - rect = Rectangle(width=self.x_axis.n2p(x1)[0] - self.x_axis.n2p(x0)[0], - height=height) - rect.move_to(self.c2p(x0, 0), DL) + rect.positive = height_vect[1] > 0 + rect.move_to(self.c2p(x0, 0), DL if rect.positive else UL) rects.append(rect) result = VGroup(*rects) result.set_submobject_colors_by_gradient(*colors) @@ -353,6 +375,9 @@ class CoordinateSystem(ABC): fill_opacity=fill_opacity, stroke_background=stroke_background ) + for rect in result: + if not rect.positive: + rect.set_fill(negative_color) return result def get_area_under_graph(self, graph, x_range, fill_color=BLUE, fill_opacity=1): @@ -647,7 +672,7 @@ class ComplexPlane(NumberPlane): if abs(z.imag) > abs(z.real): axis = self.get_y_axis() value = z.imag - kwargs["unit"] = "i" + kwargs["unit_tex"] = "i" else: axis = self.get_x_axis() value = z.real From c3c1d3ec35ac6c4f02defb329aa3d2007e10452a Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 3 Nov 2022 11:29:49 -0700 Subject: [PATCH 2/6] Typo fix --- manimlib/mobject/mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index d5d37452..bab9323f 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -1449,7 +1449,7 @@ class Mobject(object): return interpolate(points[i], points[i + 1], subalpha) def pfp(self, alpha): - """Abbreviation fo point_from_proportion""" + """Abbreviation for point_from_proportion""" return self.point_from_proportion(alpha) def get_pieces(self, n_pieces: int) -> Group: From 1bd3d61b08b7f683d63fbcf4cc18bea8cd526342 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 3 Nov 2022 11:30:20 -0700 Subject: [PATCH 3/6] Add unit_tex option for NumberLine.add_numbers --- manimlib/mobject/number_line.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/number_line.py b/manimlib/mobject/number_line.py index e16382e6..a55f7e1e 100644 --- a/manimlib/mobject/number_line.py +++ b/manimlib/mobject/number_line.py @@ -143,17 +143,21 @@ class NumberLine(Line): x: float, direction: np.ndarray | None = None, buff: float | None = None, + unit: float = 1.0, + unit_tex: str = "", **number_config ) -> DecimalNumber: number_config = merge_dicts_recursively( - self.decimal_number_config, number_config + self.decimal_number_config, number_config, ) if direction is None: direction = self.line_to_number_direction if buff is None: buff = self.line_to_number_buff + if unit_tex: + number_config["unit"] = unit_tex - num_mob = DecimalNumber(x, **number_config) + num_mob = DecimalNumber(x / unit, **number_config) num_mob.next_to( self.number_to_point(x), direction=direction, @@ -162,6 +166,10 @@ class NumberLine(Line): if x < 0 and direction[0] == 0: # Align without the minus sign num_mob.shift(num_mob[0].get_width() * LEFT / 2) + if x == unit and unit_tex: + center = num_mob.get_center() + num_mob.remove(num_mob[0]) + num_mob.move_to(center) return num_mob def add_numbers( From ce77f38bf1941aaf668a8d0cfa0bec135d1def15 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 3 Nov 2022 11:29:43 -0700 Subject: [PATCH 4/6] Add CoordinateSystem.bind_graph_to_func --- manimlib/mobject/coordinate_systems.py | 41 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index 7f42ec80..3ebc4377 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -5,9 +5,9 @@ import numbers import numpy as np -from manimlib.constants import BLACK, BLUE, BLUE_D, GREEN, GREY_A, WHITE +from manimlib.constants import BLACK, BLUE, BLUE_D, GREEN, GREY_A, WHITE, RED from manimlib.constants import DEGREES, PI -from manimlib.constants import DL, DOWN, DR, LEFT, ORIGIN, OUT, RIGHT, UP +from manimlib.constants import DL, UL, DOWN, DR, LEFT, ORIGIN, OUT, RIGHT, UP from manimlib.constants import FRAME_HEIGHT, FRAME_WIDTH from manimlib.constants import FRAME_X_RADIUS, FRAME_Y_RADIUS from manimlib.constants import MED_SMALL_BUFF, SMALL_BUFF @@ -19,6 +19,7 @@ from manimlib.mobject.geometry import Rectangle from manimlib.mobject.number_line import NumberLine from manimlib.mobject.svg.tex_mobject import Tex from manimlib.mobject.types.vectorized_mobject import VGroup +from manimlib.mobject.types.dot_cloud import DotCloud from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import merge_dicts_recursively from manimlib.utils.simple_functions import binary_search @@ -233,6 +234,19 @@ class CoordinateSystem(ABC): """ return self.input_to_graph_point(x, graph) + def bind_graph_to_func(self, graph, func, jagged=False): + """ + Use for graphing functions which might change over time, or change with + conditions + """ + graph.x_values = [self.x_axis.p2n(p) for p in graph.get_points()] + graph.add_updater(lambda g: g.set_points([self.c2p(x, func(x)) for x in g.x_values])) + if jagged: + graph.add_updater(lambda g: g.make_jagged()) + else: + graph.add_updater(lambda g: g.make_approximately_smooth()) + return graph + def get_graph_label( self, graph: ParametricCurve, @@ -274,6 +288,12 @@ class CoordinateSystem(ABC): def get_h_line_to_graph(self, x: float, graph: ParametricCurve, **kwargs): return self.get_h_line(self.i2gp(x, graph), **kwargs) + def get_scatterplot(self, + x_values: np.ndarray, + y_values: np.ndarray, + **dot_config): + return DotCloud(self.c2p(x_values, y_values), **dot_config) + # For calculus def angle_of_tangent( self, @@ -316,6 +336,7 @@ class CoordinateSystem(ABC): stroke_color: ManimColor = BLACK, fill_opacity: float = 1, colors: Iterable[ManimColor] = (BLUE, GREEN), + negative_color: ManimColor = RED, stroke_background: bool = True, show_signed_area: bool = True ) -> VGroup: @@ -338,12 +359,13 @@ class CoordinateSystem(ABC): sample = 0.5 * x0 + 0.5 * x1 else: raise Exception("Invalid input sample type") - height = get_norm( - self.i2gp(sample, graph) - self.c2p(sample, 0) + height_vect = self.i2gp(sample, graph) - self.c2p(sample, 0) + rect = Rectangle( + width=self.x_axis.n2p(x1)[0] - self.x_axis.n2p(x0)[0], + height=get_norm(height_vect), ) - rect = Rectangle(width=self.x_axis.n2p(x1)[0] - self.x_axis.n2p(x0)[0], - height=height) - rect.move_to(self.c2p(x0, 0), DL) + rect.positive = height_vect[1] > 0 + rect.move_to(self.c2p(x0, 0), DL if rect.positive else UL) rects.append(rect) result = VGroup(*rects) result.set_submobject_colors_by_gradient(*colors) @@ -353,6 +375,9 @@ class CoordinateSystem(ABC): fill_opacity=fill_opacity, stroke_background=stroke_background ) + for rect in result: + if not rect.positive: + rect.set_fill(negative_color) return result def get_area_under_graph(self, graph, x_range, fill_color=BLUE, fill_opacity=1): @@ -647,7 +672,7 @@ class ComplexPlane(NumberPlane): if abs(z.imag) > abs(z.real): axis = self.get_y_axis() value = z.imag - kwargs["unit"] = "i" + kwargs["unit_tex"] = "i" else: axis = self.get_x_axis() value = z.real From 80d34547db816c4b71421f8aff425c35d0d4734a Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 3 Nov 2022 11:29:49 -0700 Subject: [PATCH 5/6] Typo fix --- manimlib/mobject/mobject.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/mobject/mobject.py b/manimlib/mobject/mobject.py index d5d37452..bab9323f 100644 --- a/manimlib/mobject/mobject.py +++ b/manimlib/mobject/mobject.py @@ -1449,7 +1449,7 @@ class Mobject(object): return interpolate(points[i], points[i + 1], subalpha) def pfp(self, alpha): - """Abbreviation fo point_from_proportion""" + """Abbreviation for point_from_proportion""" return self.point_from_proportion(alpha) def get_pieces(self, n_pieces: int) -> Group: From faa37844e70d6acd3e89b10f8b2566269dcad058 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 3 Nov 2022 11:30:20 -0700 Subject: [PATCH 6/6] Add unit_tex option for NumberLine.add_numbers --- manimlib/mobject/number_line.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/number_line.py b/manimlib/mobject/number_line.py index e16382e6..a55f7e1e 100644 --- a/manimlib/mobject/number_line.py +++ b/manimlib/mobject/number_line.py @@ -143,17 +143,21 @@ class NumberLine(Line): x: float, direction: np.ndarray | None = None, buff: float | None = None, + unit: float = 1.0, + unit_tex: str = "", **number_config ) -> DecimalNumber: number_config = merge_dicts_recursively( - self.decimal_number_config, number_config + self.decimal_number_config, number_config, ) if direction is None: direction = self.line_to_number_direction if buff is None: buff = self.line_to_number_buff + if unit_tex: + number_config["unit"] = unit_tex - num_mob = DecimalNumber(x, **number_config) + num_mob = DecimalNumber(x / unit, **number_config) num_mob.next_to( self.number_to_point(x), direction=direction, @@ -162,6 +166,10 @@ class NumberLine(Line): if x < 0 and direction[0] == 0: # Align without the minus sign num_mob.shift(num_mob[0].get_width() * LEFT / 2) + if x == unit and unit_tex: + center = num_mob.get_center() + num_mob.remove(num_mob[0]) + num_mob.move_to(center) return num_mob def add_numbers(