diff --git a/manimlib/mobject/coordinate_systems.py b/manimlib/mobject/coordinate_systems.py index 4b98c78b..60dc7f4c 100644 --- a/manimlib/mobject/coordinate_systems.py +++ b/manimlib/mobject/coordinate_systems.py @@ -31,7 +31,6 @@ class Axes(VGroup): "x_max": FRAME_X_RADIUS, "y_min": -FRAME_Y_RADIUS, "y_max": FRAME_Y_RADIUS, - "default_num_graph_points": 100, } def __init__(self, **kwargs): @@ -68,14 +67,12 @@ class Axes(VGroup): ]) def get_graph( - self, function, num_graph_points=None, + self, function, x_min=None, x_max=None, **kwargs ): kwargs["fill_opacity"] = kwargs.get("fill_opacity", 0) - kwargs["num_anchor_points"] = \ - num_graph_points or self.default_num_graph_points x_min = x_min or self.x_min x_max = x_max or self.x_max graph = ParametricFunction( diff --git a/manimlib/mobject/functions.py b/manimlib/mobject/functions.py index c5e78695..2be48c9b 100644 --- a/manimlib/mobject/functions.py +++ b/manimlib/mobject/functions.py @@ -7,22 +7,54 @@ class ParametricFunction(VMobject): CONFIG = { "t_min": 0, "t_max": 1, - "num_anchor_points": 100, + # TODO, be smarter about choosing this number + "step_size": 0.01, + "dt": 1e-8, + # TODO, be smar about figuring these out? + "discontinuities": [], } def __init__(self, function, **kwargs): self.function = function VMobject.__init__(self, **kwargs) + def get_function(self): + return self.function + + def get_point_from_function(self, t): + return self.function(t) + def generate_points(self): - n_points = 3 * self.num_anchor_points - 2 - self.points = np.zeros((n_points, self.dim)) - self.points[:, 0] = np.linspace( - self.t_min, self.t_max, n_points + t_min, t_max = self.t_min, self.t_max + dt = self.dt + step_size = self.step_size + + discontinuities = filter( + lambda t: t_min <= t <= t_max, + self.discontinuities ) - # VMobject.apply_function takes care of preserving - # desirable tangent line properties at anchor points - self.apply_function(lambda p: self.function(p[0])) + discontinuities = np.array(list(discontinuities)) + boundary_times = [ + self.t_min, self.t_max, + *(discontinuities - dt), + *(discontinuities + dt), + ] + boundary_times.sort() + print(boundary_times) + for t1, t2 in zip(boundary_times[0::2], boundary_times[1::2]): + t_range = list(np.arange(t1, t2, step_size)) + if t_range[-1] != t2: + t_range.append(t2) + points = np.array([self.function(t) for t in t_range]) + valid_indices = np.apply_along_axis( + np.all, 1, np.isfinite(points) + ) + points = points[valid_indices] + if len(points) > 0: + self.start_new_path(points[0]) + self.add_points_as_corners(points[1:]) + self.make_smooth() + return self class FunctionGraph(ParametricFunction): @@ -34,12 +66,11 @@ class FunctionGraph(ParametricFunction): def __init__(self, function, **kwargs): digest_config(self, kwargs) - - def parametric_function(t): - return t * RIGHT + function(t) * UP + self.parametric_function = \ + lambda t: np.array([t, function(t), 0]) ParametricFunction.__init__( self, - parametric_function, + self.parametric_function, t_min=self.x_min, t_max=self.x_max, **kwargs @@ -48,3 +79,6 @@ class FunctionGraph(ParametricFunction): def get_function(self): return self.function + + def get_point_from_function(self, x): + return self.parametric_function(x) diff --git a/manimlib/mobject/types/vectorized_mobject.py b/manimlib/mobject/types/vectorized_mobject.py index f2354dbb..e889dff6 100644 --- a/manimlib/mobject/types/vectorized_mobject.py +++ b/manimlib/mobject/types/vectorized_mobject.py @@ -485,11 +485,12 @@ class VMobject(Mobject): return points def set_points_as_corners(self, points): - if len(points) == 0: - return # TODO, raise warning? - self.clear_points() - self.start_new_path(points[0]) - self.add_points_as_corners(points[1:]) + nppcc = self.n_points_per_cubic_curve + points = np.array(points) + self.set_anchors_and_handles(*[ + interpolate(points[:-1], points[1:], a) + for a in np.linspace(0, 1, nppcc) + ]) return self def set_points_smoothly(self, points): diff --git a/manimlib/scene/graph_scene.py b/manimlib/scene/graph_scene.py index 868e2858..f260e635 100644 --- a/manimlib/scene/graph_scene.py +++ b/manimlib/scene/graph_scene.py @@ -44,7 +44,6 @@ class GraphScene(Scene): "axes_color": GREY, "graph_origin": 2.5 * DOWN + 4 * LEFT, "exclude_zero_label": True, - "num_graph_anchor_points": 25, "default_graph_colors": [BLUE, GREEN, YELLOW], "default_derivative_color": GREEN, "default_input_color": YELLOW, @@ -149,6 +148,7 @@ class GraphScene(Scene): color=None, x_min=None, x_max=None, + **kwargs ): if color is None: color = next(self.default_graph_colors_cycle) @@ -167,7 +167,7 @@ class GraphScene(Scene): graph = ParametricFunction( parameterized_function, color=color, - num_anchor_points=self.num_graph_anchor_points, + **kwargs ) graph.underlying_function = func return graph diff --git a/old_projects/eoc/chapter3.py b/old_projects/eoc/chapter3.py index ffc0ea85..194e1658 100644 --- a/old_projects/eoc/chapter3.py +++ b/old_projects/eoc/chapter3.py @@ -97,7 +97,6 @@ class ContrastAbstractAndConcrete(Scene): ParametricFunction( lambda t : (t/denom)*RIGHT+np.sin(t)*UP+np.cos(t)*OUT, t_max = 12*np.pi, - num_anchor_points = 100, ) for denom in (12.0, 4.0) ] diff --git a/old_projects/eoc/chapter4.py b/old_projects/eoc/chapter4.py index 49afe937..477b034c 100644 --- a/old_projects/eoc/chapter4.py +++ b/old_projects/eoc/chapter4.py @@ -208,7 +208,6 @@ class DampenedSpring(Scene): ParametricFunction( lambda t : (t/denom)*RIGHT+np.sin(t)*UP+np.cos(t)*OUT, t_max = 12*np.pi, - num_anchor_points = 100, color = GREY, ).shift(3*LEFT) for denom in (12.0, 2.0) diff --git a/old_projects/fourier.py b/old_projects/fourier.py index 8dbcf8d0..3979cd0d 100644 --- a/old_projects/fourier.py +++ b/old_projects/fourier.py @@ -3911,7 +3911,6 @@ class BoundsAtInfinity(SummarizeFormula): number_line_config = { "include_tip" : False, }, - default_num_graph_points = 1000, ) axes.x_axis.add_numbers(*list(filter( lambda x : x != 0, diff --git a/old_projects/uncertainty.py b/old_projects/uncertainty.py index 7e792571..2ad59384 100644 --- a/old_projects/uncertainty.py +++ b/old_projects/uncertainty.py @@ -525,7 +525,7 @@ class ShowPlan(PiCreatureScene): wave = FunctionGraph( lambda x : 0.3*np.sin(15*x)*np.sin(0.5*x), x_min = 0, x_max = 30, - num_anchor_points = 500, + step_size = 0.001, ) wave.next_to(word, RIGHT) rect = BackgroundRectangle(wave, fill_opacity = 1) @@ -965,7 +965,7 @@ class VariousMusicalNotes(Scene): a = graph_width_tracker.get_value() return FunctionGraph( lambda x : np.exp(-a*x**2)*np.sin(freq*x)-0.5, - num_anchor_points = 500, + step_size = 0.001, ) graph = get_graph() def graph_update(graph): @@ -1044,7 +1044,7 @@ class VariousMusicalNotes(Scene): lambda x : 0.5*np.sin(freq*x), x_min = -FRAME_WIDTH, x_max = FRAME_WIDTH, - num_anchor_points = 1000 + n_components = 0.001 ) long_graph.set_color(BLUE) long_graph.next_to(graph, UP, MED_LARGE_BUFF)