Updating coordinate system mobjects

This commit is contained in:
Grant Sanderson 2019-02-06 21:16:26 -08:00
parent 24d6113bba
commit d88c301622
25 changed files with 425 additions and 354 deletions

View file

@ -41,7 +41,7 @@ class AnalyzeZSquared(ComplexTransformationScene, ZoomedScene):
self.add_foreground_mobject(title) self.add_foreground_mobject(title)
def edit_background_plane(self): def edit_background_plane(self):
self.background.main_lines.set_stroke(GREY, 2) self.backgrounds.set_stroke(GREY, 2)
self.background.secondary_lines.set_stroke(DARK_GREY, 1) self.background.secondary_lines.set_stroke(DARK_GREY, 1)
self.add_foreground_mobject(self.background.coordinate_labels) self.add_foreground_mobject(self.background.coordinate_labels)

View file

@ -14,7 +14,7 @@ from manimlib.utils.config_ops import digest_config
class Container(object): class Container(object):
def __init__(self, *submobjects, **kwargs): def __init__(self, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
def add(self, *items): def add(self, *items):

View file

@ -1,4 +1,5 @@
import numpy as np import numpy as np
import itertools as it
from manimlib.constants import * from manimlib.constants import *
from manimlib.mobject.functions import ParametricFunction from manimlib.mobject.functions import ParametricFunction
@ -7,17 +8,90 @@ from manimlib.mobject.geometry import Line
from manimlib.mobject.number_line import NumberLine from manimlib.mobject.number_line import NumberLine
from manimlib.mobject.svg.tex_mobject import TexMobject from manimlib.mobject.svg.tex_mobject import TexMobject
from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import digest_config
from manimlib.utils.config_ops import merge_config from manimlib.utils.config_ops import merge_config
from manimlib.utils.simple_functions import binary_search
from manimlib.utils.space_ops import angle_of_vector from manimlib.utils.space_ops import angle_of_vector
# TODO: There should be much more code reuse between Axes, NumberPlane and GraphScene # TODO: There should be much more code reuse between Axes, NumberPlane and GraphScene
class Axes(VGroup): class CoordinateSystem():
"""
Abstract class for Axes and NumberPlane
"""
CONFIG = {
"dimension": 2,
"x_min": -FRAME_X_RADIUS,
"x_max": FRAME_X_RADIUS,
"y_min": -FRAME_Y_RADIUS,
"y_max": FRAME_Y_RADIUS,
}
def coords_to_point(self, *coords):
raise Exception("Not implemented")
def point_to_coords(self, point):
raise Exception("Not implemented")
def get_axes(self):
raise Exception("Not implemented")
def get_axis(self, index):
return self.get_axes()[index]
def get_x_axis(self):
return self.get_axis(0)
def get_y_axis(self):
return self.get_axis(1)
def get_z_axis(self):
return self.get_axis(2)
def get_graph(self, function, **kwargs):
x_min = kwargs.pop("x_min", self.x_min)
x_max = kwargs.pop("x_max", self.x_max)
graph = ParametricFunction(
lambda t: self.coords_to_point(t, function(t)),
t_min=x_min,
t_max=x_max,
**kwargs
)
graph.underlying_function = function
return graph
def get_parametric_curve(self, function, **kwargs):
dim = self.dimension
graph = ParametricFunction(
lambda t: self.coords_to_point(
*function(t)[:dim]
),
**kwargs
)
graph.underlying_function = function
return graph
def input_to_graph_point(self, x, graph):
if hasattr(graph, "underlying_function"):
return self.coords_to_point(x, graph.underlying_function(x))
else:
alpha = binary_search(
function=lambda a: self.point_to_coords(
graph.point_from_proportion(a)
)[0],
target=x,
lower_bound=self.x_min,
uplper_bound=self.x_max,
)
if alpha is not None:
return graph.point_from_proportion(alpha)
else:
return None
class Axes(VGroup, CoordinateSystem):
CONFIG = { CONFIG = {
"three_d": False,
"number_line_config": { "number_line_config": {
"color": LIGHT_GREY, "color": LIGHT_GREY,
"include_tip": True, "include_tip": True,
@ -26,24 +100,26 @@ class Axes(VGroup):
"y_axis_config": { "y_axis_config": {
"label_direction": LEFT, "label_direction": LEFT,
}, },
"x_min": -FRAME_X_RADIUS, "center_point": ORIGIN,
"x_max": FRAME_X_RADIUS,
"y_min": -FRAME_Y_RADIUS,
"y_max": FRAME_Y_RADIUS,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
VGroup.__init__(self, **kwargs) VGroup.__init__(self, **kwargs)
self.x_axis = self.get_axis( self.x_axis = self.create_axis(
self.x_min, self.x_max, self.x_axis_config self.x_min, self.x_max, self.x_axis_config
) )
self.y_axis = self.get_axis( self.y_axis = self.create_axis(
self.y_min, self.y_max, self.y_axis_config self.y_min, self.y_max, self.y_axis_config
) )
self.y_axis.rotate(90 * DEGREES, about_point=ORIGIN) self.y_axis.rotate(90 * DEGREES, about_point=ORIGIN)
self.add(self.x_axis, self.y_axis) # Add as a separate group incase various other
# mobjects are added to self, as for example in
# NumberPlane below
self.axes = VGroup(self.x_axis, self.y_axis)
self.add(*self.axes)
self.shift(self.center_point)
def get_axis(self, min_val, max_val, axis_config): def create_axis(self, min_val, max_val, axis_config):
new_config = merge_config([ new_config = merge_config([
axis_config, axis_config,
{"x_min": min_val, "x_max": max_val}, {"x_min": min_val, "x_max": max_val},
@ -54,63 +130,23 @@ class Axes(VGroup):
def coords_to_point(self, *coords): def coords_to_point(self, *coords):
origin = self.x_axis.number_to_point(0) origin = self.x_axis.number_to_point(0)
result = np.array(origin) result = np.array(origin)
for axis, coord in zip(self, coords): for axis, coord in zip(self.get_axes(), coords):
result += (axis.number_to_point(coord) - origin) result += (axis.number_to_point(coord) - origin)
return result return result
def point_to_coords(self, point): def point_to_coords(self, point):
return tuple([ return tuple([
axis.point_to_number(point) axis.point_to_number(point)
for axis in self for axis in self.get_axes()
if isinstance(axis, NumberLine)
]) ])
def get_graph( def get_axes(self):
self, function, return self.axes
x_min=None,
x_max=None,
**kwargs
):
kwargs["fill_opacity"] = kwargs.get("fill_opacity", 0)
x_min = x_min or self.x_min
x_max = x_max or self.x_max
graph = ParametricFunction(
lambda t: self.coords_to_point(t, function(t)),
t_min=x_min,
t_max=x_max,
**kwargs
)
graph.underlying_function = function
return graph
def input_to_graph_point(self, x, graph):
if hasattr(graph, "underlying_function"):
return self.coords_to_point(x, graph.underlying_function(x))
else:
# binary search
lh, rh = 0, 1
while abs(lh - rh) > 0.001:
mh = np.mean([lh, rh])
hands = [lh, mh, rh]
points = list(map(graph.point_from_proportion, hands))
lx, mx, rx = list(map(self.x_axis.point_to_number, points))
if lx <= x and rx >= x:
if mx > x:
rh = mh
else:
lh = mh
elif lx <= x and rx <= x:
return points[2]
elif lx >= x and rx >= x:
return points[0]
elif lx > x and rx < x:
lh, rh = rh, lh
return points[1]
return self.coords_to_point(x, graph.underlying_function(x))
class ThreeDAxes(Axes): class ThreeDAxes(Axes):
CONFIG = { CONFIG = {
"dimension": 3,
"x_min": -5.5, "x_min": -5.5,
"x_max": 5.5, "x_max": 5.5,
"y_min": -5.5, "y_min": -5.5,
@ -125,7 +161,7 @@ class ThreeDAxes(Axes):
def __init__(self, **kwargs): def __init__(self, **kwargs):
Axes.__init__(self, **kwargs) Axes.__init__(self, **kwargs)
z_axis = self.z_axis = self.get_axis( z_axis = self.z_axis = self.create_axis(
self.z_min, self.z_max, self.z_axis_config self.z_min, self.z_max, self.z_axis_config
) )
z_axis.rotate(-np.pi / 2, UP, about_point=ORIGIN) z_axis.rotate(-np.pi / 2, UP, about_point=ORIGIN)
@ -133,18 +169,19 @@ class ThreeDAxes(Axes):
angle_of_vector(self.z_normal), OUT, angle_of_vector(self.z_normal), OUT,
about_point=ORIGIN about_point=ORIGIN
) )
self.axes.append(z_axis)
self.add(z_axis) self.add(z_axis)
self.add_3d_pieces() self.add_3d_pieces()
self.set_axis_shading() self.set_axis_shading()
def add_3d_pieces(self): def add_3d_pieces(self):
for axis in self: for axis in self.axes:
axis.pieces = VGroup( axis.pieces = VGroup(
*axis.main_line.get_pieces(self.num_axis_pieces) *axis.get_pieces(self.num_axis_pieces)
) )
axis.add(axis.pieces) axis.add(axis.pieces)
axis.main_line.set_stroke(width=0, family=False) axis.set_stroke(width=0, family=False)
axis.set_shade_in_3d(True) axis.set_shade_in_3d(True)
def set_axis_shading(self): def set_axis_shading(self):
@ -161,195 +198,180 @@ class ThreeDAxes(Axes):
submob.set_sheen(0.2) submob.set_sheen(0.2)
class NumberPlane(VMobject): class NumberPlane(Axes):
CONFIG = { CONFIG = {
"color": BLUE_D, "axis_config": {
"secondary_color": BLUE_E, "stroke_color": WHITE,
"axes_color": WHITE, "stroke_width": 2,
"secondary_stroke_width": 1, "include_ticks": False,
# TODO: Allow coordinate center of NumberPlane to not be at (0, 0) "include_tip": False,
"x_radius": None, "line_to_number_buff": SMALL_BUFF,
"y_radius": None, "label_direction": DR,
"x_unit_size": 1, "number_scale_val": 0.5,
"y_unit_size": 1, },
"center_point": ORIGIN, "y_axis_config": {
"label_direction": DR,
},
"background_line_style": {
"stroke_color": BLUE_D,
"stroke_width": 2,
},
# Defaults to a faded version of line_config
"faded_line_style": None,
"x_line_frequency": 1, "x_line_frequency": 1,
"y_line_frequency": 1, "y_line_frequency": 1,
"secondary_line_ratio": 1, "faded_line_ratio": 1,
"written_coordinate_height": 0.2,
"propagate_style_to_family": False,
"make_smooth_after_applying_functions": True, "make_smooth_after_applying_functions": True,
} }
def generate_points(self): def __init__(self, **kwargs):
if self.x_radius is None: digest_config(self, kwargs)
center_to_edge = (FRAME_X_RADIUS + abs(self.center_point[0])) kwargs["number_line_config"] = self.axis_config
self.x_radius = center_to_edge / self.x_unit_size Axes.__init__(self, **kwargs)
if self.y_radius is None: self.init_background_lines()
center_to_edge = (FRAME_Y_RADIUS + 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)
def init_colors(self): def init_background_lines(self):
VMobject.init_colors(self) if self.faded_line_style is None:
self.axes.set_stroke(self.axes_color, self.stroke_width) background_line_style = self.background_line_style
self.main_lines.set_stroke(self.color, self.stroke_width) color = background_line_style.get(
self.secondary_lines.set_stroke( "stroke_color", WHITE
self.secondary_color, self.secondary_stroke_width )
stroke_width = background_line_style.get("stroke_width", 2) / 2
self.faded_line_style = {
"stroke_color": color,
"stroke_width": stroke_width,
"stroke_opacity": 0.5,
}
self.background_lines, self.faded_lines = self.get_lines()
self.background_lines.set_style(
**self.background_line_style,
) )
return self self.faded_lines.set_style(
**self.faded_line_style,
)
self.add_to_back(
self.faded_lines,
self.background_lines,
)
def get_lines(self):
x_axis = self.get_x_axis()
y_axis = self.get_y_axis()
x_freq = self.x_line_frequency
y_freq = self.y_line_frequency
x_lines1, x_lines2 = self.get_lines_parallel_to_axis(
x_axis, y_axis, x_freq,
self.faded_line_ratio,
)
y_lines1, y_lines2 = self.get_lines_parallel_to_axis(
y_axis, x_axis, y_freq,
self.faded_line_ratio,
)
lines1 = VGroup(*x_lines1, *y_lines1)
lines2 = VGroup(*x_lines2, *y_lines2)
return lines1, lines2
def get_lines_parallel_to_axis(self, axis1, axis2, freq, ratio):
line = Line(axis1.get_start(), axis1.get_end())
vect = line.get_vector()
dense_freq = (1 + ratio)
step = 1 / dense_freq
lines1 = VGroup()
lines2 = VGroup()
ranges = (
np.arange(0, axis2.x_max, step),
np.arange(0, axis2.x_min, -step),
)
for inputs in ranges:
for k, x in enumerate(inputs):
new_line = line.copy()
new_line.move_to(axis2.number_to_point(x))
new_line.align_to(line, vect)
if k % (1 + ratio) == 0:
lines1.add(new_line)
else:
lines2.add(new_line)
return lines1, lines2
def get_center_point(self): def get_center_point(self):
return self.coords_to_point(0, 0) return self.coords_to_point(0, 0)
def coords_to_point(self, x, y):
x, y = np.array([x, y])
result = self.axes.get_center()
result += x * self.get_x_unit_size() * RIGHT
result += y * self.get_y_unit_size() * UP
return result
def point_to_coords(self, point):
new_point = point - self.axes.get_center()
x = new_point[0] / self.get_x_unit_size()
y = new_point[1] / self.get_y_unit_size()
return x, y
# Does not recompute center, unit_sizes for each call; useful for
# iterating over large lists of points, but does assume these
# attributes are kept accurate. (Could alternatively have a method
# which returns a function dynamically created after a single
# call to each of get_center(), get_x_unit_size(), etc.)
def point_to_coords_cheap(self, point):
new_point = point - self.center_point
x = new_point[0] / self.x_unit_size
y = new_point[1] / self.y_unit_size
return x, y
def get_x_unit_size(self): def get_x_unit_size(self):
return self.axes.get_width() / (2.0 * self.x_radius) return self.get_x_axis().get_unit_size()
def get_y_unit_size(self): def get_y_unit_size(self):
return self.axes.get_height() / (2.0 * self.y_radius) return self.get_x_axis().get_unit_size()
def get_coordinate_labels(self, x_vals=None, y_vals=None): def get_coordinate_labels(self, x_vals=None, y_vals=None):
coordinate_labels = VGroup() x_vals = x_vals or []
if x_vals is None: y_vals = y_vals or []
x_vals = list(range(-int(self.x_radius), int(self.x_radius) + 1)) x_mobs = self.get_x_axis().get_number_mobjects(*x_vals)
if y_vals is None: y_mobs = self.get_y_axis().get_number_mobjects(*y_vals)
y_vals = list(range(-int(self.y_radius), int(self.y_radius) + 1))
for index, vals in enumerate([x_vals, y_vals]): self.coordinate_labels = VGroup(x_mobs, y_mobs)
num_pair = [0, 0] return self.coordinate_labels
for val in vals:
if val == 0:
continue
num_pair[index] = val
point = self.coords_to_point(*num_pair)
num = TexMobject(str(val))
num.add_background_rectangle()
num.set_height(
self.written_coordinate_height
)
num.next_to(point, DOWN + LEFT, buff=SMALL_BUFF)
coordinate_labels.add(num)
self.coordinate_labels = coordinate_labels
return coordinate_labels
def get_axes(self): def get_axes(self):
return self.axes return self.axes
def get_axis_labels(self, x_label="x", y_label="y"): def get_x_axis_label(self, label_tex, edge=RIGHT, direction=DL, **kwargs):
x_axis, y_axis = self.get_axes().split() return self.get_axis_label(
quads = [ label_tex, self.get_x_axis(),
(x_axis, x_label, UP, RIGHT), edge, direction, **kwargs
(y_axis, y_label, RIGHT, UP), )
]
labels = VGroup() def get_y_axis_label(self, label_tex, edge=UP, direction=DR, **kwargs):
for axis, tex, vect, edge in quads: return self.get_axis_label(
label = TexMobject(tex) label_tex, self.get_y_axis(),
label.add_background_rectangle() edge, direction, **kwargs
label.next_to(axis, vect) )
label.to_edge(edge)
labels.add(label) def get_axis_label(self, label_tex, axis, edge, direction, buff=MED_SMALL_BUFF):
self.axis_labels = labels label = TexMobject(label_tex)
return labels label.next_to(
axis.get_edge_center(edge), direction,
buff=buff
)
return label
def get_axis_labels(self, x_label_tex="x", y_label_tex="y"):
self.axis_labels = VGroup(
self.get_x_axis_label(x_label_tex),
self.get_y_axis_label(y_label_tex),
)
return self.axis_labels
def add_coordinates(self, x_vals=None, y_vals=None): def add_coordinates(self, x_vals=None, y_vals=None):
self.add(*self.get_coordinate_labels(x_vals, y_vals)) self.add(self.get_coordinate_labels(x_vals, y_vals))
return self return self
def get_vector(self, coords, **kwargs): def get_vector(self, coords, **kwargs):
point = coords[0] * RIGHT + coords[1] * UP kwargs["buff"] = 0
arrow = Arrow(ORIGIN, point, **kwargs) return Arrow(
return arrow self.coords_to_point(0, 0),
self.coords_to_point(*coords),
**kwargs
)
def prepare_for_nonlinear_transform(self, num_inserted_curves=50): def prepare_for_nonlinear_transform(self, num_inserted_curves=50):
for mob in self.family_members_with_points(): for mob in self.family_members_with_points():
num_curves = mob.get_num_curves() num_curves = mob.get_num_curves()
if num_inserted_curves > num_curves: if num_inserted_curves > num_curves:
mob.insert_n_curves( mob.insert_n_curves(
num_inserted_curves - num_curves) num_inserted_curves - num_curves
mob.make_smooth() )
return self return self
class ComplexPlane(NumberPlane): class ComplexPlane(NumberPlane):
CONFIG = { CONFIG = {
"color": BLUE, "color": BLUE,
"unit_size": 1,
"line_frequency": 1, "line_frequency": 1,
"faded_line_frequency": 0.5,
} }
def __init__(self, **kwargs):
digest_config(self, kwargs)
kwargs.update({
"x_unit_size": self.unit_size,
"y_unit_size": self.unit_size,
"x_line_frequency": self.line_frequency,
"x_faded_line_frequency": self.faded_line_frequency,
"y_line_frequency": self.line_frequency,
"y_faded_line_frequency": self.faded_line_frequency,
})
NumberPlane.__init__(self, **kwargs)
def number_to_point(self, number): def number_to_point(self, number):
number = complex(number) number = complex(number)
return self.coords_to_point(number.real, number.imag) return self.coords_to_point(number.real, number.imag)
@ -358,35 +380,35 @@ class ComplexPlane(NumberPlane):
x, y = self.point_to_coords(point) x, y = self.point_to_coords(point)
return complex(x, y) return complex(x, y)
def get_coordinate_labels(self, *numbers): def get_default_coordinate_values(self):
# TODO: Should merge this with the code from NumberPlane.get_coordinate_labels x_numbers = self.get_x_axis().default_numbers_to_display()
y_numbers = self.get_y_axis().default_numbers_to_display()
y_numbers = [
complex(0, y) for y in y_numbers if y != 0
]
return [*x_numbers, *y_numbers]
result = VGroup() def get_coordinate_labels(self, *numbers, **kwargs):
if len(numbers) == 0: if len(numbers) == 0:
numbers = list(range(-int(self.x_radius), int(self.x_radius) + 1)) numbers = self.get_default_coordinate_values()
numbers += [
complex(0, y) self.coordinate_labels = VGroup()
for y in range(-int(self.y_radius), int(self.y_radius) + 1)
if y != 0
]
for number in numbers: for number in numbers:
# if number == complex(0, 0): z = complex(number)
# continue if abs(z.imag) > abs(z.real):
point = self.number_to_point(number) axis = self.get_y_axis()
num_str = str(number).replace("j", "i") value = z.imag
if num_str.startswith("0"): kwargs = merge_config([
num_str = "0" {"number_config": {"unit": "i"}},
elif num_str in ["1i", "-1i"]: kwargs,
num_str = num_str.replace("1", "") ])
num_mob = TexMobject(num_str) else:
num_mob.add_background_rectangle() axis = self.get_x_axis()
num_mob.set_height(self.written_coordinate_height) value = z.real
num_mob.next_to(point, DOWN + LEFT, SMALL_BUFF) number_mob = axis.get_number_mobject(value, **kwargs)
result.add(num_mob) self.coordinate_labels.add(number_mob)
self.coordinate_labels = result return self.coordinate_labels
return result
def add_coordinates(self, *numbers): def add_coordinates(self, *numbers):
self.coordinate_labels = self.get_coordinate_labels(*numbers) self.add(self.get_coordinate_labels(*numbers))
self.add(self.coordinate_labels)
return self return self

View file

@ -36,11 +36,9 @@ class Mobject(Container):
"target": None, "target": None,
} }
def __init__(self, *submobjects, **kwargs): def __init__(self, **kwargs):
Container.__init__(self, *submobjects, **kwargs) Container.__init__(self, **kwargs)
if not all([isinstance(m, Mobject) for m in submobjects]): self.submobjects = []
raise Exception("All submobjects must be of type Mobject")
self.submobjects = list(submobjects)
self.color = Color(self.color) self.color = Color(self.color)
if self.name is None: if self.name is None:
self.name = self.__class__.__name__ self.name = self.__class__.__name__
@ -1031,7 +1029,8 @@ class Mobject(Container):
class Group(Mobject): class Group(Mobject):
# Alternate name to improve readibility in cases where def __init__(self, *mobjects, **kwargs):
# the mobject is used primarily for its submobject housing if not all([isinstance(m, Mobject) for m in mobjects]):
# functionality. raise Exception("All submobjects must be of type Mobject")
pass Mobject.__init__(self, **kwargs)
self.add(*mobjects)

View file

@ -1,25 +1,30 @@
import operator as op
from manimlib.constants import * from manimlib.constants import *
from manimlib.mobject.geometry import Arrow from manimlib.mobject.geometry import RegularPolygon
from manimlib.mobject.geometry import Line from manimlib.mobject.geometry import Line
from manimlib.mobject.numbers import DecimalNumber from manimlib.mobject.numbers import DecimalNumber
from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.utils.bezier import interpolate from manimlib.utils.bezier import interpolate
from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import digest_config
from manimlib.utils.config_ops import merge_config
from manimlib.utils.simple_functions import fdiv from manimlib.utils.simple_functions import fdiv
from manimlib.utils.space_ops import get_norm
from manimlib.utils.space_ops import normalize from manimlib.utils.space_ops import normalize
class NumberLine(VMobject): class NumberLine(Line):
CONFIG = { CONFIG = {
"color": LIGHT_GREY, "color": LIGHT_GREY,
"x_min": -FRAME_X_RADIUS, "x_min": -FRAME_X_RADIUS,
"x_max": FRAME_X_RADIUS, "x_max": FRAME_X_RADIUS,
"unit_size": 1, "unit_size": 1,
"include_ticks": True,
"tick_size": 0.1, "tick_size": 0.1,
"tick_frequency": 1, "tick_frequency": 1,
"leftmost_tick": None, # Defaults to value near x_min s.t. 0 is a tick # Defaults to value near x_min s.t. 0 is a tick
# TODO, rename this
"leftmost_tick": None,
# Change name
"numbers_with_elongated_ticks": [0], "numbers_with_elongated_ticks": [0],
"include_numbers": False, "include_numbers": False,
"numbers_to_show": None, "numbers_to_show": None,
@ -29,6 +34,8 @@ class NumberLine(VMobject):
"label_direction": DOWN, "label_direction": DOWN,
"line_to_number_buff": MED_SMALL_BUFF, "line_to_number_buff": MED_SMALL_BUFF,
"include_tip": False, "include_tip": False,
"tip_width": 0.25,
"tip_height": 0.25,
"decimal_number_config": { "decimal_number_config": {
"num_decimal_places": 0, "num_decimal_places": 0,
} }
@ -36,68 +43,72 @@ class NumberLine(VMobject):
def __init__(self, **kwargs): def __init__(self, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
if self.leftmost_tick is None: start = self.unit_size * self.x_min * RIGHT
tf = self.tick_frequency end = self.unit_size * self.x_max * RIGHT
self.leftmost_tick = tf * np.ceil(self.x_min / tf) Line.__init__(self, start, end, **kwargs)
VMobject.__init__(self, **kwargs) self.shift(-self.number_to_point(self.number_at_center))
self.init_leftmost_tick()
if self.include_ticks:
self.add_tick_marks()
if self.include_tip: if self.include_tip:
self.add_tip() self.add_tip()
if self.include_numbers: if self.include_numbers:
self.add_numbers() self.add_numbers()
def generate_points(self): def init_leftmost_tick(self):
self.main_line = Line(self.x_min * RIGHT, self.x_max * RIGHT) if self.leftmost_tick is None:
self.tick_marks = VGroup() self.leftmost_tick = op.mul(
self.add(self.main_line, self.tick_marks) self.tick_frequency,
rounding_value = int(-np.log10(0.1 * self.tick_frequency)) np.ceil(self.x_min / self.tick_frequency)
rounded_numbers_with_elongated_ticks = np.round( )
self.numbers_with_elongated_ticks,
rounding_value def add_tick_marks(self):
tick_size = self.tick_size
self.tick_marks = VGroup(*[
self.get_tick(x, tick_size)
for x in self.get_tick_numbers()
])
big_tick_size = tick_size * self.longer_tick_multiple
self.big_tick_marks = VGroup(*[
self.get_tick(x, big_tick_size)
for x in self.numbers_with_elongated_ticks
])
self.add(
self.tick_marks,
self.big_tick_marks,
) )
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))
def add_tick(self, x, size=None):
self.tick_marks.add(self.get_tick(x, size))
return self
def get_tick(self, x, size=None): def get_tick(self, x, size=None):
if size is None: if size is None:
size = self.tick_size size = self.tick_size
result = Line(size * DOWN, size * UP) result = Line(size * DOWN, size * UP)
result.rotate(self.main_line.get_angle()) result.rotate(self.get_angle())
result.move_to(self.number_to_point(x)) result.move_to(self.number_to_point(x))
result.match_style(self)
return result return result
def get_tick_marks(self): def get_tick_marks(self):
return self.tick_marks return VGroup(
*self.tick_marks,
*self.big_tick_marks,
)
def get_tick_numbers(self): def get_tick_numbers(self):
epsilon = 0.001
return np.arange( return np.arange(
self.leftmost_tick, self.x_max + epsilon, self.leftmost_tick,
self.x_max + self.tick_frequency / 2,
self.tick_frequency self.tick_frequency
) )
def number_to_point(self, number): def number_to_point(self, number):
alpha = float(number - self.x_min) / (self.x_max - self.x_min) alpha = float(number - self.x_min) / (self.x_max - self.x_min)
return interpolate( return interpolate(
self.main_line.get_start(), self.get_start(), self.get_end(), alpha
self.main_line.get_end(),
alpha
) )
def point_to_number(self, point): def point_to_number(self, point):
start_point, end_point = self.main_line.get_start_and_end() start_point, end_point = self.get_start_and_end()
full_vect = end_point - start_point full_vect = end_point - start_point
unit_vect = normalize(full_vect) unit_vect = normalize(full_vect)
@ -110,28 +121,43 @@ class NumberLine(VMobject):
) )
return interpolate(self.x_min, self.x_max, proportion) return interpolate(self.x_min, self.x_max, proportion)
def get_unit_size(self):
return (self.x_max - self.x_min) / self.get_length()
def default_numbers_to_display(self): def default_numbers_to_display(self):
if self.numbers_to_show is not None: if self.numbers_to_show is not None:
return self.numbers_to_show return self.numbers_to_show
return np.arange(int(self.leftmost_tick), int(self.x_max) + 1) return np.arange(int(self.leftmost_tick), int(self.x_max) + 1)
def get_number_mobject(self, number,
number_config=None,
scale_val=None,
direction=None,
buff=None):
number_config = merge_config([
number_config or {},
self.decimal_number_config
])
scale_val = scale_val or self.number_scale_val
direction = direction or self.label_direction
buff = buff or self.line_to_number_buff
num_mob = DecimalNumber(number, **number_config)
num_mob.scale(scale_val)
num_mob.next_to(
self.number_to_point(number),
direction=direction,
buff=buff
)
return num_mob
def get_number_mobjects(self, *numbers, **kwargs): def get_number_mobjects(self, *numbers, **kwargs):
# TODO, handle decimals
if len(numbers) == 0: if len(numbers) == 0:
numbers = self.default_numbers_to_display() numbers = self.default_numbers_to_display()
result = VGroup() return VGroup(*[
for number in numbers: self.get_number_mobject(number, **kwargs)
mob = DecimalNumber( for number in numbers
number, **self.decimal_number_config ])
)
mob.scale(self.number_scale_val)
mob.next_to(
self.number_to_point(number),
self.label_direction,
self.line_to_number_buff,
)
result.add(mob)
return result
def get_labels(self): def get_labels(self):
return self.get_number_mobjects() return self.get_number_mobjects()
@ -144,12 +170,13 @@ class NumberLine(VMobject):
return self return self
def add_tip(self): def add_tip(self):
start, end = self.main_line.get_start_and_end() tip = RegularPolygon(3)
vect = (end - start) / get_norm(end - start) color = self.color
arrow = Arrow(start, end + MED_SMALL_BUFF * vect, buff=0) tip.set_stroke(color, width=self.get_stroke_width())
tip = arrow.tip tip.set_fill(color, opacity=1)
tip.set_stroke(width=self.get_stroke_width()) tip.set_width(self.tip_width)
tip.set_color(self.color) tip.set_height(self.tip_height, stretch=True)
tip.move_to(self.get_end(), LEFT)
self.tip = tip self.tip = tip
self.add(tip) self.add(tip)

View file

@ -26,7 +26,7 @@ class DecimalNumber(VMobject):
formatter = self.get_formatter() formatter = self.get_formatter()
num_string = formatter.format(number) num_string = formatter.format(number)
rounded_num = np.round(float(number), self.num_decimal_places) rounded_num = np.round(number, self.num_decimal_places)
if num_string.startswith("-") and rounded_num == 0: if num_string.startswith("-") and rounded_num == 0:
if self.include_sign: if self.include_sign:
num_string = "+" + num_string[1:] num_string = "+" + num_string[1:]

View file

@ -11,7 +11,6 @@ from manimlib.mobject.geometry import Rectangle
from manimlib.mobject.geometry import RoundedRectangle from manimlib.mobject.geometry import RoundedRectangle
from manimlib.mobject.types.vectorized_mobject import VGroup from manimlib.mobject.types.vectorized_mobject import VGroup
from manimlib.mobject.types.vectorized_mobject import VMobject from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.utils.bezier import is_closed
from manimlib.utils.color import * from manimlib.utils.color import *
from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import digest_config
from manimlib.utils.config_ops import digest_locals from manimlib.utils.config_ops import digest_locals
@ -105,14 +104,14 @@ class SVGMobject(VMobject):
pass # TODO pass # TODO
# warnings.warn("Unknown element type: " + element.tagName) # warnings.warn("Unknown element type: " + element.tagName)
result = [m for m in result if m is not None] result = [m for m in result if m is not None]
self.handle_transforms(element, VMobject(*result)) self.handle_transforms(element, VGroup(*result))
if len(result) > 1 and not self.unpack_groups: if len(result) > 1 and not self.unpack_groups:
result = [VGroup(*result)] result = [VGroup(*result)]
return result return result
def g_to_mobjects(self, g_element): def g_to_mobjects(self, g_element):
mob = VMobject(*self.get_mobjects_from(g_element)) mob = VGroup(*self.get_mobjects_from(g_element))
self.handle_transforms(g_element, mob) self.handle_transforms(g_element, mob)
return mob.submobjects return mob.submobjects
@ -124,7 +123,7 @@ class SVGMobject(VMobject):
ref = use_element.getAttribute("xlink:href")[1:] ref = use_element.getAttribute("xlink:href")[1:]
if ref not in self.ref_to_element: if ref not in self.ref_to_element:
warnings.warn("%s not recognized" % ref) warnings.warn("%s not recognized" % ref)
return VMobject() return VGroup()
return self.get_mobjects_from( return self.get_mobjects_from(
self.ref_to_element[ref] self.ref_to_element[ref]
) )

View file

@ -796,18 +796,11 @@ class VMobject(Mobject):
class VGroup(VMobject): class VGroup(VMobject):
def __init__(self, *args, **kwargs): def __init__(self, *vmobjects, **kwargs):
if len(args) == 1 and isinstance(args[0], (tuple, list)): if not all([isinstance(m, VMobject) for m in vmobjects]):
args = args[0] raise Exception("All submobjects must be of type VMobject")
VMobject.__init__(self, **kwargs)
packed_args = [] self.add(*vmobjects)
for arg in args:
if isinstance(arg, (tuple, list)):
packed_args.append(VGroup(arg))
else:
packed_args.append(arg)
VMobject.__init__(self, *packed_args, **kwargs)
class VectorizedPoint(VMobject): class VectorizedPoint(VMobject):

View file

@ -96,7 +96,7 @@ class ComplexTransformationScene(Scene):
# TODO... # TODO...
def paint_plane(self, plane): def paint_plane(self, plane):
for lines in plane.main_lines, plane.secondary_lines: for lines in planes, plane.secondary_lines:
lines.set_color_by_gradient( lines.set_color_by_gradient(
self.vert_start_color, self.vert_start_color,
self.vert_end_color, self.vert_end_color,

View file

@ -156,10 +156,10 @@ class SpecialThreeDScene(ThreeDScene):
axes = ThreeDAxes(**self.three_d_axes_config) axes = ThreeDAxes(**self.three_d_axes_config)
for axis in axes: for axis in axes:
if self.cut_axes_at_radius: if self.cut_axes_at_radius:
p0 = axis.main_line.get_start() p0 = axis.get_start()
p1 = axis.number_to_point(-1) p1 = axis.number_to_point(-1)
p2 = axis.number_to_point(1) p2 = axis.number_to_point(1)
p3 = axis.main_line.get_end() p3 = axis.get_end()
new_pieces = VGroup( new_pieces = VGroup(
Line(p0, p1), Line(p1, p2), Line(p2, p3), Line(p0, p1), Line(p1, p2), Line(p2, p3),
) )

View file

@ -56,12 +56,16 @@ def digest_config(obj, kwargs, caller_locals={}):
obj.__dict__ = merge_config(all_dicts) obj.__dict__ = merge_config(all_dicts)
# TODO, priority here is backwards from dict.update.
# Should I change the convention?
def merge_config(all_dicts): def merge_config(all_dicts):
""" """
Creates a dict whose keyset is the union of all the Creates a dict whose keyset is the union of all the
input dictionaries. The value for each key is based input dictionaries. The value for each key is based
on the first dict in the list with that key. on the first dict in the list with that key.
First dicts have higher priority
When values are dictionaries, it is applied recursively When values are dictionaries, it is applied recursively
""" """
all_config = reduce(op.add, [list(d.items()) for d in all_dicts]) all_config = reduce(op.add, [list(d.items()) for d in all_dicts])

View file

@ -62,3 +62,30 @@ def fdiv(a, b, zero_over_zero_value=None):
where = True where = True
return np.true_divide(a, b, out=out, where=where) return np.true_divide(a, b, out=out, where=where)
def binary_search(function,
target,
lower_bound,
upper_bound,
tolerance=1e-4):
lh = lower_bound
rh = upper_bound
while abs(rh - lh) > tolerance:
mh = np.mean([lh, rh])
lx, mx, rx = [function(h) for h in (lh, mh, rh)]
if lx == target:
return lx
if rx == target:
return rx
if lx <= target and rx >= target:
if mx > target:
rh = mh
else:
lh = mh
elif lx > target and rx < target:
lh, rh = rh, lh
else:
return None
return mh

View file

@ -781,7 +781,7 @@ class InputOutputScene(Scene):
plane.add_coordinates(x_vals = list(range(-2, 3)), y_vals = list(range(-2, 3))) plane.add_coordinates(x_vals = list(range(-2, 3)), y_vals = list(range(-2, 3)))
plane.white_parts = VGroup(plane.axes, plane.coordinate_labels) plane.white_parts = VGroup(plane.axes, plane.coordinate_labels)
plane.coordinate_labels.set_background_stroke(width=0) plane.coordinate_labels.set_background_stroke(width=0)
plane.lines_to_fade = VGroup(plane.main_lines, plane.secondary_lines) plane.lines_to_fade = VGroup(planes, plane.secondary_lines)
plane.move_to(vect*FRAME_X_RADIUS/2 + self.y_shift*DOWN) plane.move_to(vect*FRAME_X_RADIUS/2 + self.y_shift*DOWN)
label = TextMobject(text) label = TextMobject(text)
label.scale(1.5) label.scale(1.5)

View file

@ -76,7 +76,7 @@ class NumberlineTransformationScene(ZoomedScene):
full_config = dict(self.number_line_config) full_config = dict(self.number_line_config)
full_config.update(added_config) full_config.update(added_config)
number_line = NumberLine(**full_config) number_line = NumberLine(**full_config)
number_line.main_line.insert_n_curves( number_line.insert_n_curves(
self.num_inserted_number_line_curves self.num_inserted_number_line_curves
) )
number_line.shift(zero_point - number_line.number_to_point(0)) number_line.shift(zero_point - number_line.number_to_point(0))
@ -179,12 +179,12 @@ class NumberlineTransformationScene(ZoomedScene):
self.moving_input_line = input_line_copy self.moving_input_line = input_line_copy
input_line_copy.remove(input_line_copy.numbers) input_line_copy.remove(input_line_copy.numbers)
# input_line_copy.set_stroke(width=2) # input_line_copy.set_stroke(width=2)
input_line_copy.main_line.insert_n_curves( input_line_copy.insert_n_curves(
self.num_inserted_number_line_curves self.num_inserted_number_line_curves
) )
return AnimationGroup( return AnimationGroup(
self.get_mapping_animation( self.get_mapping_animation(
func, input_line_copy.main_line, func, input_line_copy,
apply_function_to_points apply_function_to_points
), ),
self.get_mapping_animation( self.get_mapping_animation(
@ -242,7 +242,7 @@ class NumberlineTransformationScene(ZoomedScene):
zoom_anim.update(1) zoom_anim.update(1)
target_mini_line = Line(frame.get_left(), frame.get_right()) target_mini_line = Line(frame.get_left(), frame.get_right())
target_mini_line.scale(self.mini_line_scale_factor) target_mini_line.scale(self.mini_line_scale_factor)
target_mini_line.match_style(self.output_line.main_line) target_mini_line.match_style(self.output_line)
zoom_anim.update(0) zoom_anim.update(0)
zcbr_group.submobjects.insert(1, target_mini_line) zcbr_group.submobjects.insert(1, target_mini_line)
if target_coordinate_values: if target_coordinate_values:
@ -312,7 +312,7 @@ class NumberlineTransformationScene(ZoomedScene):
mini_line = self.mini_line = Line(frame.get_left(), frame.get_right()) mini_line = self.mini_line = Line(frame.get_left(), frame.get_right())
mini_line.scale(self.mini_line_scale_factor) mini_line.scale(self.mini_line_scale_factor)
mini_line.insert_n_curves(self.num_inserted_number_line_curves) mini_line.insert_n_curves(self.num_inserted_number_line_curves)
mini_line.match_style(self.input_line.main_line) mini_line.match_style(self.input_line)
mini_line_copy = mini_line.copy() mini_line_copy = mini_line.copy()
zcbr_group.add(mini_line_copy, mini_line) zcbr_group.add(mini_line_copy, mini_line)
anims += [FadeIn(mini_line), FadeIn(mini_line_copy)] anims += [FadeIn(mini_line), FadeIn(mini_line_copy)]
@ -2935,7 +2935,7 @@ class AnalyzeFunctionWithTransformations(NumberlineTransformationScene):
def setup_number_lines(self): def setup_number_lines(self):
NumberlineTransformationScene.setup_number_lines(self) NumberlineTransformationScene.setup_number_lines(self)
for line in self.input_line, self.output_line: for line in self.input_line, self.output_line:
VGroup(line.main_line, line.tick_marks).set_stroke(width=2) VGroup(line, line.tick_marks).set_stroke(width=2)
def add_function_title(self): def add_function_title(self):
title = TexMobject("f(x)", "=", "1 +", "\\frac{1}{x}") title = TexMobject("f(x)", "=", "1 +", "\\frac{1}{x}")

View file

@ -3883,7 +3883,7 @@ class ThinkBackToHowAmazingThisIs(ThreeDScene):
self.number_line = number_line self.number_line = number_line
def show_giant_circle(self): def show_giant_circle(self):
self.number_line.main_line.insert_n_curves(10000) self.number_line.insert_n_curves(10000)
everything = VGroup(*self.mobjects) everything = VGroup(*self.mobjects)
circle = everything.copy() circle = everything.copy()
circle.move_to(ORIGIN) circle.move_to(ORIGIN)

View file

@ -3223,7 +3223,7 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
]) ])
for axis, label, vect in zip(axes, axes_labels, [RIGHT, UP]): for axis, label, vect in zip(axes, axes_labels, [RIGHT, UP]):
label.next_to( label.next_to(
axis.main_line, vect, axis, vect,
submobject_to_align=label[0] submobject_to_align=label[0]
) )
@ -4297,7 +4297,7 @@ class ZToHalfFlowNearWall(ComplexTransformationScene, MovingCameraScene):
secondary_line_ratio=0, secondary_line_ratio=0,
) )
plane.next_to(ORIGIN, UP, buff=0.001) plane.next_to(ORIGIN, UP, buff=0.001)
horizontal_lines = VGroup(*[l for l in list(plane.main_lines) + [plane.axes[0]] if np.abs(l.get_center()[0]) < 0.1]) horizontal_lines = VGroup(*[l for l in list(planes) + [plane.axes[0]] if np.abs(l.get_center()[0]) < 0.1])
plane.set_stroke(MAROON_B, width=2) plane.set_stroke(MAROON_B, width=2)
horizontal_lines.set_stroke(BLUE, width=2) horizontal_lines.set_stroke(BLUE, width=2)

View file

@ -65,7 +65,7 @@ class SlopeOfCircleExample(ZoomedScene):
def setup_plane(self): def setup_plane(self):
self.plane = NumberPlane(**self.plane_kwargs) self.plane = NumberPlane(**self.plane_kwargs)
self.plane.main_lines.fade() self.planes.fade()
self.plane.add(self.plane.get_axis_labels()) self.plane.add(self.plane.get_axis_labels())
self.plane.add_coordinates() self.plane.add_coordinates()

View file

@ -652,7 +652,7 @@ class ConstantVelocityPlot(PlotVelocity):
def note_units(self): def note_units(self):
x_line, y_line = lines = VGroup(*[ x_line, y_line = lines = VGroup(*[
axis.main_line.copy() axis.copy()
for axis in (self.x_axis, self.y_axis) for axis in (self.x_axis, self.y_axis)
]) ])
lines.set_color(TIME_COLOR) lines.set_color(TIME_COLOR)

View file

@ -2221,7 +2221,7 @@ class ChangeToEigenBasis(ExampleTranformationScene):
self.play(FadeOut(self.plane)) self.play(FadeOut(self.plane))
cob_transform = self.get_matrix_transformation([[1, 0], [-1, 1]]) cob_transform = self.get_matrix_transformation([[1, 0], [-1, 1]])
ApplyMethod(self.plane.apply_function, cob_transform).update(1) ApplyMethod(self.plane.apply_function, cob_transform).update(1)
self.plane.main_lines.set_color(BLUE_D) self.planes.set_color(BLUE_D)
self.plane.axes.set_color(WHITE) self.plane.axes.set_color(WHITE)
self.play( self.play(
FadeIn(self.plane), FadeIn(self.plane),

View file

@ -2047,8 +2047,8 @@ class ShowCommutativeDiagram(ShowLinearity):
VGroup(ta_group[1], fa_group[1]).shift(MED_LARGE_BUFF*UP) VGroup(ta_group[1], fa_group[1]).shift(MED_LARGE_BUFF*UP)
for ta, fa in zip(ta_group, fa_group): for ta, fa in zip(ta_group, fa_group):
fa.next_to( fa.next_to(
ta.x_axis.main_line, RIGHT, ta.x_axis, RIGHT,
submobject_to_align = fa.x_axis.main_line submobject_to_align = fa.x_axis
) )
fa.to_edge(RIGHT) fa.to_edge(RIGHT)
ta.remove(ta.labels) ta.remove(ta.labels)
@ -2084,7 +2084,7 @@ class ShowCommutativeDiagram(ShowLinearity):
fourier_graph.set_color(self.center_of_mass_color) fourier_graph.set_color(self.center_of_mass_color)
arrow = Arrow( arrow = Arrow(
ta.x_axis.main_line, fa.x_axis.main_line, ta.x_axis, fa.x_axis,
color = WHITE, color = WHITE,
buff = MED_LARGE_BUFF, buff = MED_LARGE_BUFF,
) )
@ -3712,7 +3712,7 @@ class SummarizeTheFullTransform(DrawFrequencyPlot):
) )
imaginary_fourier_graph.set_color(BLUE) imaginary_fourier_graph.set_color(BLUE)
imaginary_fourier_graph.shift( imaginary_fourier_graph.shift(
frequency_axes.x_axis.main_line.get_right() - \ frequency_axes.x_axis.get_right() - \
imaginary_fourier_graph.points[-1], imaginary_fourier_graph.points[-1],
) )

View file

@ -46,7 +46,7 @@ class Slider(NumberLine):
def add_label(self, tex): def add_label(self, tex):
label = TexMobject(tex) label = TexMobject(tex)
label.scale(self.label_scale_val) label.scale(self.label_scale_val)
label.move_to(self.main_line.get_top()) label.move_to(self.get_top())
label.shift(MED_LARGE_BUFF*UP) label.shift(MED_LARGE_BUFF*UP)
self.add(label) self.add(label)
self.label = label self.label = label

View file

@ -674,7 +674,7 @@ class QuarterTurn(Scene):
class UsingTheta(Scene): class UsingTheta(Scene):
def construct(self): def construct(self):
plane = NumberPlane(x_unit_size = 3, y_unit_size = 3) plane = NumberPlane(x_unit_size = 3, y_unit_size = 3)
# plane.main_lines.fade(0.5) # planes.fade(0.5)
# plane.secondary_lines.fade(0.5) # plane.secondary_lines.fade(0.5)
plane.fade(0.5) plane.fade(0.5)
self.add(plane) self.add(plane)

View file

@ -1245,7 +1245,7 @@ class IntroduceLinusTheLinelander(Scene):
algebra.shift(3 * RIGHT) algebra.shift(3 * RIGHT)
self.play( self.play(
ShowCreation(number_line.main_line), ShowCreation(number_line),
linus.look_at, number_line linus.look_at, number_line
) )
self.play( self.play(
@ -3123,9 +3123,9 @@ class IntroduceThreeDNumbers(SpecialThreeDScene):
z_axis.set_color(WHITE) z_axis.set_color(WHITE)
z_axis_top = Line( z_axis_top = Line(
z_axis.number_to_point(0), z_axis.number_to_point(0),
z_axis.main_line.get_end(), z_axis.get_end(),
) )
z_axis_top.match_style(z_axis.main_line) z_axis_top.match_style(z_axis)
z_unit_line = Line( z_unit_line = Line(
z_axis.number_to_point(0), z_axis.number_to_point(0),
@ -3155,7 +3155,7 @@ class IntroduceThreeDNumbers(SpecialThreeDScene):
colored_coord_lines = VGroup(colored_, y_line, z_line) colored_coord_lines = VGroup(colored_, y_line, z_line)
coord_lines = VGroup( coord_lines = VGroup(
plane.axes[0], plane.axes[1], z_axis.main_line, plane.axes[0], plane.axes[1], z_axis,
) )
for i1, i2 in [(0, 2), (1, 0), (2, 1)]: for i1, i2 in [(0, 2), (1, 0), (2, 1)]:
coord_lines[i1].target = coord_lines[i2].copy() coord_lines[i1].target = coord_lines[i2].copy()

View file

@ -1700,7 +1700,7 @@ class VisualizeZSquared(Scene):
color_grid = self.get_color_grid() color_grid = self.get_color_grid()
self.play( self.play(
self.background_plane.main_lines.set_stroke, None, 1, self.background_planes.set_stroke, None, 1,
LaggedStart( LaggedStart(
FadeIn, color_grid, FadeIn, color_grid,
run_time = 2 run_time = 2
@ -1807,7 +1807,7 @@ class VisualizeZSquared(Scene):
secondary_line_ratio = 0, secondary_line_ratio = 0,
stroke_width = 2, stroke_width = 2,
) )
color_grid.main_lines.set_color_by_gradient( color_grids.set_color_by_gradient(
*[GREEN, RED, MAROON_B, TEAL]*2 *[GREEN, RED, MAROON_B, TEAL]*2
) )
color_grid.remove(color_grid.axes[0]) color_grid.remove(color_grid.axes[0])

View file

@ -877,7 +877,7 @@ class TwoCarsAtRedLight(Scene):
)) ))
self.play( self.play(
ApplyMethod( ApplyMethod(
self.time_axes.x_axis.main_line.stretch, 2.5, 0, self.time_axes.x_axis.stretch, 2.5, 0,
{"about_edge" : LEFT}, {"about_edge" : LEFT},
run_time = 4, run_time = 4,
rate_func = squish_rate_func(smooth, 0.3, 0.6), rate_func = squish_rate_func(smooth, 0.3, 0.6),
@ -885,7 +885,7 @@ class TwoCarsAtRedLight(Scene):
UpdateFromFunc( UpdateFromFunc(
self.time_axes.x_axis.tip, self.time_axes.x_axis.tip,
lambda m : m.move_to( lambda m : m.move_to(
self.time_axes.x_axis.main_line.get_right(), self.time_axes.x_axis.get_right(),
LEFT LEFT
) )
), ),
@ -1364,7 +1364,7 @@ class CenterOfMassDescription(FourierRecapScene):
circle_plane.target.set_height(FRAME_HEIGHT) circle_plane.target.set_height(FRAME_HEIGHT)
circle_plane.target.center() circle_plane.target.center()
circle_plane.target.axes.set_stroke(width = 2) circle_plane.target.axes.set_stroke(width = 2)
circle_plane.target.main_lines.set_stroke(width = 2) circle_plane.targets.set_stroke(width = 2)
circle_plane.target.secondary_lines.set_stroke(width = 1) circle_plane.target.secondary_lines.set_stroke(width = 1)
start_coords = (0.5, 0.5) start_coords = (0.5, 0.5)
@ -2940,7 +2940,7 @@ class IntroduceDeBroglie(Scene):
self.wait() self.wait()
#Transform time_line #Transform time_line
line = time_line.main_line line = time_line
self.play( self.play(
FadeOut(time_line.numbers), FadeOut(time_line.numbers),
VGroup(arrow, words, date).shift, MED_LARGE_BUFF*UP, VGroup(arrow, words, date).shift, MED_LARGE_BUFF*UP,