From c3ccfbfa65b4f4a43b98e5703cadec41075f363a Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 6 Feb 2021 11:39:19 -0800 Subject: [PATCH 01/12] re-introduce make_smoothing option --- manimlib/mobject/functions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manimlib/mobject/functions.py b/manimlib/mobject/functions.py index 26a492a5..d0d64cee 100644 --- a/manimlib/mobject/functions.py +++ b/manimlib/mobject/functions.py @@ -9,6 +9,7 @@ class ParametricCurve(VMobject): "epsilon": 1e-8, # TODO, automatically figure out discontinuities "discontinuities": [], + "use_smoothing": True, } def __init__(self, t_func, t_range=None, **kwargs): @@ -39,7 +40,8 @@ class ParametricCurve(VMobject): points = np.array([self.t_func(t) for t in t_range]) self.start_new_path(points[0]) self.add_points_as_corners(points[1:]) - self.make_approximately_smooth() + if self.use_smoothing: + self.make_approximately_smooth() return self From e6fc323b6d1e0c665d18e8244f8b8e129e43b5e4 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sat, 6 Feb 2021 11:39:39 -0800 Subject: [PATCH 02/12] Some changes to get_graph_label defaults --- manimlib/mobject/coordinate_systems.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index e03ea6d0..fc980b87 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -147,15 +147,18 @@ class CoordinateSystem(): direction=RIGHT, buff=MED_SMALL_BUFF, color=None): - label = Tex(label) + if isinstance(label, str): + label = Tex(label) if color is None: label.match_color(graph) if x is None: # Searching from the right, find a point # whose y value is in bounds max_y = FRAME_Y_RADIUS - label.get_height() - for x0 in np.arange(*self.x_range)[-1::-1]: - if abs(self.itgp(x0, graph)[1]) < max_y: + max_x = FRAME_X_RADIUS - label.get_width() + for x0 in np.arange(*self.x_range)[::-1]: + pt = self.itgp(x0, graph) + if abs(pt[0]) < max_x and abs(pt[1]) < max_y: x = x0 break if x is None: From 700d7c6def02923974b9320b2878b18378d779f5 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 09:56:28 -0800 Subject: [PATCH 03/12] Axes.get_v_line_to_graph and Axes.get_h_line_to_graph --- manimlib/mobject/coordinate_systems.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index fc980b87..e83516ba 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -173,12 +173,19 @@ class CoordinateSystem(): label.shift_onto_screen() return label - def get_vertical_line_to_graph(self, x, graph, line_func=Line): + def get_v_line_to_graph(self, x, graph, line_func=Line): return line_func( self.coords_to_point(x, 0), self.input_to_graph_point(x, graph), ) + def get_h_line_to_graph(self, x, graph, line_func=Line): + y = self.get_y_axis().p2n(self.itgp(x, graph)) + return line_func( + self.coords_to_point(0, y), + self.input_to_graph_point(x, graph), + ) + # For calculus def angle_of_tangent(self, x, graph, dx=EPSILON): p0 = self.input_to_graph_point(x, graph) From 9a79043640e99db1fcf89ead72c8055b6bf5128c Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:29:45 -0800 Subject: [PATCH 04/12] Change threshold for treating quadratic bezier curves as lines --- .../shaders/inserts/quadratic_bezier_geometry_functions.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl b/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl index c0f77bb3..e27402d1 100644 --- a/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl +++ b/manimlib/shaders/inserts/quadratic_bezier_geometry_functions.glsl @@ -56,7 +56,7 @@ mat4 get_xyz_to_uv(vec3 b0, vec3 b1, vec3 unit_normal){ // float get_reduced_control_points(vec3 b0, vec3 b1, vec3 b2, out vec3 new_points[3]){ float get_reduced_control_points(in vec3 points[3], out vec3 new_points[3]){ float length_threshold = 1e-6; - float angle_threshold = 1e-3; + float angle_threshold = 5e-2; vec3 p0 = points[0]; vec3 p1 = points[1]; From 97e7b0568e7121368c171b9e25406a9fbd799fce Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:30:31 -0800 Subject: [PATCH 05/12] Add default font_size to NumberLine.add_numbers --- manimlib/mobject/number_line.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/manimlib/mobject/number_line.py b/manimlib/mobject/number_line.py index a8fefb01..e51e9f8f 100644 --- a/manimlib/mobject/number_line.py +++ b/manimlib/mobject/number_line.py @@ -149,17 +149,22 @@ class NumberLine(Line): num_mob.shift(num_mob[0].get_width() * LEFT / 2) return num_mob - def add_numbers(self, x_values=None, excluding=None, **kwargs): + def add_numbers(self, x_values=None, excluding=None, font_size=24, **kwargs): if x_values is None: x_values = self.get_tick_range() - if excluding is not None: - x_values = list_difference_update(x_values, excluding) - self.numbers = VGroup() + kwargs["font_size"] = font_size + + numbers = VGroup() for x in x_values: - self.numbers.add(self.get_number_mobject(x, **kwargs)) - self.add(self.numbers) - return self.numbers + if x in self.numbers_to_exclude: + continue + if excluding is not None and x in excluding: + continue + numbers.add(self.get_number_mobject(x, **kwargs)) + self.add(numbers) + self.numbers = numbers + return numbers class UnitInterval(NumberLine): From 479aac19880322d6bb866be4613a132b119be4c6 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:30:49 -0800 Subject: [PATCH 06/12] Add Line.get_projection method --- manimlib/mobject/geometry.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/manimlib/mobject/geometry.py b/manimlib/mobject/geometry.py index 6eebf6b4..9ed5cefb 100644 --- a/manimlib/mobject/geometry.py +++ b/manimlib/mobject/geometry.py @@ -472,6 +472,14 @@ class Line(TipableVMobject): def get_angle(self): return angle_of_vector(self.get_vector()) + def get_projection(self, point): + """ + Return projection of a point onto the line + """ + unit_vect = self.get_unit_vector() + start = self.get_start() + return start + np.dot(point - start, unit_vect) * unit_vect + def get_slope(self): return np.tan(self.get_angle()) From f984443ed597bda7579fc71dbccd6d1ce774b442 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:31:14 -0800 Subject: [PATCH 07/12] CoordinateSystem updates --- manimlib/mobject/coordinate_systems.py | 77 +++++++++++++++----------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index e83516ba..980b5106 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -5,6 +5,7 @@ from manimlib.constants import * from manimlib.mobject.functions import ParametricCurve from manimlib.mobject.geometry import Arrow from manimlib.mobject.geometry import Line +from manimlib.mobject.geometry import DashedLine from manimlib.mobject.geometry import Rectangle from manimlib.mobject.number_line import NumberLine from manimlib.mobject.svg.tex_mobject import Tex @@ -24,8 +25,8 @@ class CoordinateSystem(): """ CONFIG = { "dimension": 2, - "x_range": [-8, 8, 1], - "y_range": [-4, 4, 1], + "x_range": np.array([-8, 8, 1]), + "y_range": np.array([-4, 4, 1]), "width": None, "height": None, "num_sampled_graph_points_per_tick": 5, @@ -88,12 +89,26 @@ class CoordinateSystem(): ) return self.axis_labels + def get_line_from_axis_to_point(self, index, point, + line_func=DashedLine, + color=GREY_A, + stroke_width=2): + axis = self.get_axis(index) + line = line_func(axis.get_projection(point), point) + line.set_stroke(color, stroke_width) + return line + + def get_v_line(self, point, **kwargs): + return self.get_line_from_axis_to_point(0, point, **kwargs) + + def get_h_line(self, point, **kwargs): + return self.get_line_from_axis_to_point(1, point, **kwargs) + # Useful for graphing def get_graph(self, function, x_range=None, **kwargs): - t_range = list(self.x_range) + t_range = np.array(self.x_range, dtype=float) if x_range is not None: - for i in range(len(x_range)): - t_range[i] = x_range[i] + t_range[:len(x_range)] = x_range # For axes, the third coordinate of x_range indicates # tick frequency. But for functions, it indicates a # sample frequency @@ -134,7 +149,7 @@ class CoordinateSystem(): else: return None - def itgp(self, x, graph): + def i2gp(self, x, graph): """ Alias for input_to_graph_point """ @@ -157,7 +172,7 @@ class CoordinateSystem(): max_y = FRAME_Y_RADIUS - label.get_height() max_x = FRAME_X_RADIUS - label.get_width() for x0 in np.arange(*self.x_range)[::-1]: - pt = self.itgp(x0, graph) + pt = self.i2gp(x0, graph) if abs(pt[0]) < max_x and abs(pt[1]) < max_y: x = x0 break @@ -173,18 +188,11 @@ class CoordinateSystem(): label.shift_onto_screen() return label - def get_v_line_to_graph(self, x, graph, line_func=Line): - return line_func( - self.coords_to_point(x, 0), - self.input_to_graph_point(x, graph), - ) + def get_v_line_to_graph(self, x, graph, **kwargs): + return self.get_v_line(self.i2gp(x, graph), **kwargs) - def get_h_line_to_graph(self, x, graph, line_func=Line): - y = self.get_y_axis().p2n(self.itgp(x, graph)) - return line_func( - self.coords_to_point(0, y), - self.input_to_graph_point(x, graph), - ) + def get_h_line_to_graph(self, x, graph, **kwargs): + return self.get_h_line(self.i2gp(x, graph), **kwargs) # For calculus def angle_of_tangent(self, x, graph, dx=EPSILON): @@ -231,7 +239,7 @@ class CoordinateSystem(): else: raise Exception("Invalid input sample type") height = get_norm( - self.itgp(sample, graph) - self.c2p(sample, 0) + self.i2gp(sample, graph) - self.c2p(sample, 0) ) rect = Rectangle(width=x1 - x0, height=height) rect.move_to(self.c2p(x0, 0), DL) @@ -254,21 +262,25 @@ class Axes(VGroup, CoordinateSystem): CONFIG = { "axis_config": { "include_tip": True, + "numbers_to_exclude": [0], }, "x_axis_config": {}, "y_axis_config": { "line_to_number_direction": LEFT, }, + "height": FRAME_HEIGHT - 2, + "width": FRAME_WIDTH - 2, } - def __init__(self, x_range=None, y_range=None, **kwargs): - VGroup.__init__(self, **kwargs) + def __init__(self, + x_range=None, + y_range=None, + **kwargs): + super().__init__(**kwargs) if x_range is not None: - for i in range(len(x_range)): - self.x_range[i] = x_range[i] + self.x_range[:len(x_range)] = x_range if y_range is not None: - for i in range(len(y_range)): - self.y_range[i] = y_range[i] + self.y_range[:len(x_range)] = y_range self.x_axis = self.create_axis( self.x_range, self.x_axis_config, self.width, @@ -310,24 +322,21 @@ class Axes(VGroup, CoordinateSystem): def add_coordinate_labels(self, x_values=None, y_values=None, - excluding=[0], **kwargs): axes = self.get_axes() self.coordinate_labels = VGroup() for axis, values in zip(axes, [x_values, y_values]): - numbers = axis.add_numbers( - values, excluding=excluding, **kwargs - ) - self.coordinate_labels.add(numbers) + labels = axis.add_numbers(values, **kwargs) + self.coordinate_labels.add(labels) return self.coordinate_labels class ThreeDAxes(Axes): CONFIG = { "dimension": 3, - "x_range": (-6, 6, 1), - "y_range": (-5, 5, 1), - "z_range": (-4, 4, 1), + "x_range": np.array([-6, 6, 1]), + "y_range": np.array([-5, 5, 1]), + "z_range": np.array([-4, 4, 1]), "z_axis_config": {}, "z_normal": DOWN, "depth": None, @@ -375,6 +384,8 @@ class NumberPlane(Axes): "stroke_width": 2, "stroke_opacity": 1, }, + "height": None, + "width": None, # Defaults to a faded version of line_config "faded_line_style": None, "faded_line_ratio": 1, From 12e8506841ebd56fc692757be3672fbe42cdf68b Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:31:31 -0800 Subject: [PATCH 08/12] Added example scenes for graphing and using CoordinateSystems --- example_scenes.py | 251 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 209 insertions(+), 42 deletions(-) diff --git a/example_scenes.py b/example_scenes.py index 666faa42..e22e94c9 100644 --- a/example_scenes.py +++ b/example_scenes.py @@ -67,48 +67,6 @@ class OpeningManimExample(Scene): self.wait(2) -class InteractiveDevlopment(Scene): - def construct(self): - circle = Circle() - circle.set_fill(BLUE, opacity=0.5) - circle.set_stroke(BLUE_E, width=4) - square = Square() - - self.play(ShowCreation(square)) - self.wait() - - # This opens an iPython termnial where you can keep writing - # lines as if they were part of this construct method - self.embed() - # Try copying and pasting some of the lines below into - # the interactive shell - self.play(ReplacementTransform(square, circle)) - self.wait() - self.play(circle.stretch, 4, 0) - self.play(Rotate(circle, 90 * DEGREES)) - self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25) - - text = Text(""" - In general, using the interactive shell - is very helpful when developing new scenes - """) - self.play(Write(text)) - - # In the interactive shell, you can just type - # play, add, remove, clear, wait, save_state and restore, - # instead of self.play, self.add, self.remove, etc. - - # To interact with the window, type touch(). You can then - # scroll in the window, or zoom by holding down 'z' while scrolling, - # and change camera perspective by holding down 'd' while moving - # the mouse. Press 'r' to reset to the standard camera position. - # Press 'q' to stop interacting with the window and go back to - # typing new commands into the shell. - - # In principle you can customize a scene - always(circle.move_to, self.mouse_point) - - class AnimatingMethods(Scene): def construct(self): grid = Tex(r"\pi").get_grid(10, 10, height=4) @@ -121,7 +79,10 @@ class AnimatingMethods(Scene): # to the left, but the following line animates that motion. self.play(grid.shift, 2 * LEFT) # The same applies for any method, including those setting colors. + self.play(grid.set_color, YELLOW) + self.wait() self.play(grid.set_submobject_colors_by_gradient, BLUE, GREEN) + self.wait() self.play(grid.set_height, TAU - MED_SMALL_BUFF) self.wait() @@ -361,6 +322,166 @@ class UpdatersExample(Scene): self.wait(4 * PI) +class GraphExample(Scene): + def construct(self): + axes = Axes((-3, 10), (-1, 8)) + axes.add_coordinate_labels() + + self.play(Write(axes, lag_ratio=0.01, run_time=1)) + + # Axes.get_graph will return the graph of a function + sin_graph = axes.get_graph( + lambda x: 2 * math.sin(x), + color=BLUE, + ) + # By default, it draws it so as to somewhat smoothly interpolate + # between sampled points (x, f(x)). If the graph is meant to have + # a corner, though, you can set use_smoothing to False + relu_graph = axes.get_graph( + lambda x: max(x, 0), + use_smoothing=False, + color=YELLOW, + ) + # For discontinuous functions, you can specify the point of + # discontinuity so that it does not try to draw over the gap. + step_graph = axes.get_graph( + lambda x: 2.0 if x > 3 else 1.0, + discontinuities=[3], + color=GREEN, + ) + + # Axes.get_graph_label takes in either a string or a mobject. + # If it's a string, it treats it as a LaTeX expression. By default + # it places the label next to the graph near the right side, and + # has it match the color of the graph + sin_label = axes.get_graph_label(sin_graph, "\\sin(x)") + relu_label = axes.get_graph_label(relu_graph, Text("ReLU")) + step_label = axes.get_graph_label(step_graph, Text("Step"), x=4) + + self.play( + ShowCreation(sin_graph), + FadeIn(sin_label, RIGHT), + ) + self.wait(2) + self.play( + ReplacementTransform(sin_graph, relu_graph), + FadeTransform(sin_label, relu_label), + ) + self.wait() + self.play( + ReplacementTransform(relu_graph, step_graph), + FadeTransform(relu_label, step_label), + ) + self.wait() + + parabola = axes.get_graph(lambda x: 0.25 * x**2) + parabola.set_stroke(BLUE) + self.play( + FadeOut(step_graph), + FadeOut(step_label), + ShowCreation(parabola) + ) + self.wait() + + # You can use axes.input_to_graph_point, abbreviated + # to axes.i2gp, to find a particular point on a graph + dot = Dot(color=RED) + dot.move_to(axes.i2gp(2, parabola)) + self.play(FadeIn(dot, scale=0.5)) + + # A value tracker lets us animate a parameter, usually + # with the intent of having other mobjects update based + # on the parameter + x_tracker = ValueTracker(2) + f_always( + dot.move_to, + lambda: axes.i2gp(x_tracker.get_value(), parabola) + ) + + self.play(x_tracker.set_value, 4, run_time=3) + self.play(x_tracker.set_value, -2, run_time=3) + self.wait() + + +class CoordinateSystemExample(Scene): + def construct(self): + axes = Axes( + # x-axis ranges from -1 to 10, with a default step size of 1 + x_range=(-1, 10), + # y-axis ranges from -2 to 10 with a step size of 0.5 + y_range=(-2, 2, 0.5), + # The axes will be stretched so as to match the specified + # height and width + height=6, + width=10, + # Axes is made of two NumberLine mobjects. You can specify + # their configuration with axis_config + axis_config={ + "stroke_color": GREY_A, + "stroke_width": 2, + }, + # Alternatively, you can specify configuration for just one + # of them, like this. + y_axis_config={ + "include_tip": False, + } + ) + # Keyword arguments of add_coordinate_labels can be used to + # configure the DecimalNumber mobjects which it creates and + # adds to the axes + axes.add_coordinate_labels( + font_size=20, + num_decimal_places=1, + ) + self.add(axes) + + # Axes descends from the CoordinateSystem class, meaning + # you can call call axes.coords_to_point, abbreviated to + # axes.c2p, to associate a set of coordinates with a point, + # like so: + dot = Dot(color=RED) + dot.move_to(axes.c2p(0, 0)) + self.play(FadeIn(dot, scale=0.5)) + self.play(dot.move_to, axes.c2p(3, 2)) + self.wait() + self.play(dot.move_to, axes.c2p(5, 0.5)) + self.wait() + + # Similarly, you can call axes.point_to_coords, or axes.p2c + # print(axes.p2c(dot.get_center())) + + # We can draw lines from the axes to better mark the coordinates + # of a given point. + # Here, the always_redraw command means that on each new frame + # the lines will be redrawn + h_line = always_redraw(lambda: axes.get_h_line(dot.get_left())) + v_line = always_redraw(lambda: axes.get_v_line(dot.get_bottom())) + + self.play( + ShowCreation(h_line), + ShowCreation(v_line), + ) + self.play(dot.move_to, axes.c2p(3, -2)) + self.wait() + self.play(dot.move_to, axes.c2p(1, 1)) + self.wait() + + # If we tie the dot to a particular set of coordinates, notice + # that as we move the axes around it respects the coordinate + # system defined by them. + f_always(dot.move_to, lambda: axes.c2p(1, 1)) + self.play( + axes.scale, 0.75, + axes.to_corner, UL, + run_time=2, + ) + self.wait() + self.play(FadeOut(VGroup(axes, dot, h_line, v_line))) + + # Other coordinate systems you can play around with include + # ThreeDAxes, NumberPlane, and ComplexPlane. + + class SurfaceExample(Scene): CONFIG = { "camera_class": ThreeDCamera, @@ -452,6 +573,52 @@ class SurfaceExample(Scene): self.wait() +class InteractiveDevlopment(Scene): + def construct(self): + circle = Circle() + circle.set_fill(BLUE, opacity=0.5) + circle.set_stroke(BLUE_E, width=4) + square = Square() + + self.play(ShowCreation(square)) + self.wait() + + # This opens an iPython termnial where you can keep writing + # lines as if they were part of this construct method. + # In particular, 'square', 'circle' and 'self' will all be + # part of the local namespace in that terminal. + self.embed() + + # Try copying and pasting some of the lines below into + # the interactive shell + self.play(ReplacementTransform(square, circle)) + self.wait() + self.play(circle.stretch, 4, 0) + self.play(Rotate(circle, 90 * DEGREES)) + self.play(circle.shift, 2 * RIGHT, circle.scale, 0.25) + + text = Text(""" + In general, using the interactive shell + is very helpful when developing new scenes + """) + self.play(Write(text)) + + # In the interactive shell, you can just type + # play, add, remove, clear, wait, save_state and restore, + # instead of self.play, self.add, self.remove, etc. + + # To interact with the window, type touch(). You can then + # scroll in the window, or zoom by holding down 'z' while scrolling, + # and change camera perspective by holding down 'd' while moving + # the mouse. Press 'r' to reset to the standard camera position. + # Press 'q' to stop interacting with the window and go back to + # typing new commands into the shell. + + # In principle you can customize a scene to be responsive to + # mouse and keyboard interactions + always(circle.move_to, self.mouse_point) + + class ControlsExample(Scene): def setup(self): self.textbox = Textbox() From 1084dfb68136bfeb77cf08e787c12f58fd14028d Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:46:04 -0800 Subject: [PATCH 09/12] Partial fix for Surface.pointwise_become_partial, and hence for ShowCreation() on surfaces --- manimlib/mobject/types/surface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manimlib/mobject/types/surface.py b/manimlib/mobject/types/surface.py index 1008fe63..2bb9b569 100644 --- a/manimlib/mobject/types/surface.py +++ b/manimlib/mobject/types/surface.py @@ -101,16 +101,16 @@ class Surface(Mobject): return normalize_along_axis(normals, 1) def pointwise_become_partial(self, smobject, a, b, axis=None): + assert(isinstance(smobject, Surface)) if axis is None: axis = self.prefered_creation_axis - assert(isinstance(smobject, Surface)) if a <= 0 and b >= 1: self.match_points(smobject) return self nu, nv = smobject.resolution self.set_points(np.vstack([ - self.get_partial_points_array(arr, a, b, (nu, nv, 3), axis=axis) + self.get_partial_points_array(arr.copy(), a, b, (nu, nv, 3), axis=axis) for arr in smobject.get_surface_points_and_nudged_points() ])) return self From e8f288f7530e436eef5da7d6c21577320b76d7fc Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 17:52:56 -0800 Subject: [PATCH 10/12] Fix for ShowCreation on SGroups, like Cube --- manimlib/mobject/types/surface.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/manimlib/mobject/types/surface.py b/manimlib/mobject/types/surface.py index 2bb9b569..9ad72fd3 100644 --- a/manimlib/mobject/types/surface.py +++ b/manimlib/mobject/types/surface.py @@ -116,6 +116,8 @@ class Surface(Mobject): return self def get_partial_points_array(self, points, a, b, resolution, axis): + if len(points) == 0: + return points nu, nv = resolution[:2] points = points.reshape(resolution) max_index = resolution[axis] - 1 From 00e9c426ff7d904abd2fb145eb29648b8b8b4018 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 18:14:23 -0800 Subject: [PATCH 11/12] Fix Surface.pointwise_become_partial bug --- manimlib/mobject/types/surface.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/manimlib/mobject/types/surface.py b/manimlib/mobject/types/surface.py index 9ad72fd3..b5e1571e 100644 --- a/manimlib/mobject/types/surface.py +++ b/manimlib/mobject/types/surface.py @@ -124,16 +124,28 @@ class Surface(Mobject): lower_index, lower_residue = integer_interpolate(0, max_index, a) upper_index, upper_residue = integer_interpolate(0, max_index, b) if axis == 0: - points[:lower_index] = interpolate(points[lower_index], points[lower_index + 1], lower_residue) - points[upper_index:] = interpolate(points[upper_index], points[upper_index + 1], upper_residue) + points[:lower_index] = interpolate( + points[lower_index], + points[lower_index + 1], + lower_residue + ) + points[upper_index + 1:] = interpolate( + points[upper_index], + points[upper_index + 1], + upper_residue + ) else: - tuples = [ - (points[:, :lower_index], lower_index, lower_residue), - (points[:, upper_index:], upper_index, upper_residue), - ] - for to_change, index, residue in tuples: - col = interpolate(points[:, index], points[:, index + 1], residue) - to_change[:] = col.reshape((nu, 1, *resolution[2:])) + shape = (nu, 1, resolution[2]) + points[:, :lower_index] = interpolate( + points[:, lower_index], + points[:, lower_index + 1], + lower_residue + ).reshape(shape) + points[:, upper_index + 1:] = interpolate( + points[:, upper_index], + points[:, upper_index + 1], + upper_residue + ).reshape(shape) return points.reshape((nu * nv, *resolution[2:])) def sort_faces_back_to_front(self, vect=OUT): From 41a02285bde71483b8ae08ee1e05c284ef627496 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Sun, 7 Feb 2021 18:14:35 -0800 Subject: [PATCH 12/12] Small cleanup --- manimlib/mobject/three_dimensions.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/manimlib/mobject/three_dimensions.py b/manimlib/mobject/three_dimensions.py index 08024296..75740ae7 100644 --- a/manimlib/mobject/three_dimensions.py +++ b/manimlib/mobject/three_dimensions.py @@ -156,10 +156,6 @@ class Square3D(Surface): class Cube(SGroup): CONFIG = { - # "fill_color": BLUE, - # "fill_opacity": 1, - # "stroke_width": 1, - # "stroke_color": BLACK, "color": BLUE, "opacity": 1, "gloss": 0.5, @@ -174,7 +170,6 @@ class Cube(SGroup): face.apply_matrix(z_to_vector(vect)) self.add(face) self.set_height(self.side_length) - # self.set_color(self.color, self.opacity, self.gloss) class Prism(Cube):