Rewrote ParametricFunction to have less buggy interpolation

This commit is contained in:
Grant Sanderson
2019-02-06 15:18:11 -08:00
parent 16e8a76c6a
commit 47f6d6ba38
8 changed files with 58 additions and 29 deletions

View File

@ -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(

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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)
]

View File

@ -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)

View File

@ -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,

View File

@ -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)