mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
Merge branch 'master' of https://github.com/3b1b/manim
This commit is contained in:
commit
80dda4eb28
20 changed files with 7962 additions and 4428 deletions
|
@ -82,12 +82,10 @@ class Write(ShowCreation):
|
|||
|
||||
def establish_run_time(self, mobject):
|
||||
num_subs = len(mobject.family_members_with_points())
|
||||
if num_subs < 5:
|
||||
if num_subs < 15:
|
||||
self.run_time = 1
|
||||
elif num_subs < 15:
|
||||
self.run_time = 2
|
||||
else:
|
||||
self.run_time = 3
|
||||
self.run_time = 2
|
||||
|
||||
class DrawBorderThenFill(Animation):
|
||||
CONFIG = {
|
||||
|
@ -330,9 +328,9 @@ class LaggedStart(Animation):
|
|||
anim.update(alpha)
|
||||
return self
|
||||
|
||||
# def clean_up(self, *args, **kwargs):
|
||||
# for anim in self.subanimations:
|
||||
# anim.clean_up(*args, **kwargs)
|
||||
def clean_up(self, *args, **kwargs):
|
||||
for anim in self.subanimations:
|
||||
anim.clean_up(*args, **kwargs)
|
||||
|
||||
class DelayByOrder(Animation):
|
||||
"""
|
||||
|
|
|
@ -59,7 +59,8 @@ class Transform(Animation):
|
|||
Animation.clean_up(self, surrounding_scene)
|
||||
if self.replace_mobject_with_target_in_scene and surrounding_scene is not None:
|
||||
surrounding_scene.remove(self.mobject)
|
||||
surrounding_scene.add(self.original_target_mobject)
|
||||
if not self.remover:
|
||||
surrounding_scene.add(self.original_target_mobject)
|
||||
|
||||
class ReplacementTransform(Transform):
|
||||
CONFIG = {
|
||||
|
|
10
camera.py
10
camera.py
|
@ -141,15 +141,21 @@ class Camera(object):
|
|||
|
||||
def get_pen_and_fill(self, vmobject):
|
||||
pen = aggdraw.Pen(
|
||||
self.get_stroke_color(vmobject).get_hex_l(),
|
||||
self.color_to_hex_l(self.get_stroke_color(vmobject)),
|
||||
max(vmobject.stroke_width, 0)
|
||||
)
|
||||
fill = aggdraw.Brush(
|
||||
self.get_fill_color(vmobject).get_hex_l(),
|
||||
self.color_to_hex_l(self.get_fill_color(vmobject)),
|
||||
opacity = int(255*vmobject.get_fill_opacity())
|
||||
)
|
||||
return (pen, fill)
|
||||
|
||||
def color_to_hex_l(self, color):
|
||||
try:
|
||||
return color.get_hex_l()
|
||||
except:
|
||||
return Color(BLACK).get_hex_l()
|
||||
|
||||
def get_stroke_color(self, vmobject):
|
||||
return vmobject.get_stroke_color()
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ DEFAULT_WIDTH = 1920
|
|||
|
||||
LOW_QUALITY_FRAME_DURATION = 1./15
|
||||
MEDIUM_QUALITY_FRAME_DURATION = 1./30
|
||||
# PRODUCTION_QUALITY_FRAME_DURATION = 1./60
|
||||
PRODUCTION_QUALITY_FRAME_DURATION = 1./30
|
||||
PRODUCTION_QUALITY_FRAME_DURATION = 1./60
|
||||
|
||||
#There might be other configuration than pixel_shape later...
|
||||
PRODUCTION_QUALITY_CAMERA_CONFIG = {
|
||||
|
|
|
@ -37,7 +37,6 @@ def play_chord(*nums):
|
|||
def play_error_sound():
|
||||
play_chord(11, 8, 6, 1)
|
||||
|
||||
|
||||
def play_finish_sound():
|
||||
play_chord(12, 9, 5, 2)
|
||||
|
||||
|
@ -422,7 +421,7 @@ def get_full_image_path(image_file_name):
|
|||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
raise IOError("File not Found")
|
||||
raise IOError("File %s not Found"%image_file_name)
|
||||
|
||||
def drag_pixels(frames):
|
||||
curr = frames[0]
|
||||
|
|
|
@ -399,11 +399,17 @@ class Mobject(object):
|
|||
|
||||
## Color functions
|
||||
|
||||
def highlight(self, color = YELLOW_C, family = True, condition = None):
|
||||
def highlight(self, color = YELLOW_C, family = True):
|
||||
"""
|
||||
Condition is function which takes in one arguments, (x, y, z).
|
||||
Here it just recurses to submobjects, but in subclasses this
|
||||
should be further implemented based on the the inner workings
|
||||
of color
|
||||
"""
|
||||
raise Exception("Not implemented")
|
||||
if family:
|
||||
for submob in self.submobjects:
|
||||
submob.highlight(color, family = family)
|
||||
return self
|
||||
|
||||
def gradient_highlight(self, *colors):
|
||||
self.submobject_gradient_highlight(*colors)
|
||||
|
@ -446,11 +452,14 @@ class Mobject(object):
|
|||
return self.color
|
||||
##
|
||||
|
||||
def save_state(self):
|
||||
def save_state(self, use_deepcopy = False):
|
||||
if hasattr(self, "saved_state"):
|
||||
#Prevent exponential growth of data
|
||||
self.saved_state = None
|
||||
self.saved_state = self.copy()
|
||||
if use_deepcopy:
|
||||
self.saved_state = self.deepcopy()
|
||||
else:
|
||||
self.saved_state = self.copy()
|
||||
return self
|
||||
|
||||
def restore(self):
|
||||
|
|
|
@ -30,15 +30,11 @@ class PMobject(Mobject):
|
|||
self.rgbas = np.append(self.rgbas, rgbas, axis = 0)
|
||||
return self
|
||||
|
||||
def highlight(self, color = YELLOW_C, family = True, condition = None):
|
||||
def highlight(self, color = YELLOW_C, family = True):
|
||||
rgba = color_to_rgba(color)
|
||||
mobs = self.family_members_with_points() if family else [self]
|
||||
for mob in mobs:
|
||||
if condition:
|
||||
to_change = np.apply_along_axis(condition, 1, mob.points)
|
||||
mob.rgbas[to_change, :] = rgba
|
||||
else:
|
||||
mob.rgbas[:,:] = rgba
|
||||
mob.rgbas[:,:] = rgba
|
||||
return self
|
||||
|
||||
def gradient_highlight(self, start_color, end_color):
|
||||
|
|
|
@ -26,7 +26,6 @@ class TexSymbol(VMobjectFromSVGPathstring):
|
|||
self.set_stroke(width = added_width + mobject.get_stroke_width())
|
||||
self.set_fill(opacity = opacity)
|
||||
|
||||
|
||||
class TexMobject(SVGMobject):
|
||||
CONFIG = {
|
||||
"template_tex_file" : TEMPLATE_TEX_FILE,
|
||||
|
@ -264,6 +263,43 @@ class Brace(TexMobject):
|
|||
vect = self.get_tip() - self.get_center()
|
||||
return vect/np.linalg.norm(vect)
|
||||
|
||||
class BulletedList(TextMobject):
|
||||
CONFIG = {
|
||||
"buff" : MED_LARGE_BUFF,
|
||||
"dot_scale_factor" : 2,
|
||||
#Have to include because of handle_multiple_args implementation
|
||||
"template_tex_file" : TEMPLATE_TEXT_FILE,
|
||||
"alignment" : "",
|
||||
}
|
||||
def __init__(self, *items, **kwargs):
|
||||
line_separated_items = [s + "\\\\" for s in items]
|
||||
TextMobject.__init__(self, *line_separated_items, **kwargs)
|
||||
for part in self:
|
||||
dot = TexMobject("\\cdot").scale(self.dot_scale_factor)
|
||||
dot.next_to(part[0], LEFT, SMALL_BUFF)
|
||||
part.add_to_back(dot)
|
||||
self.arrange_submobjects(
|
||||
DOWN,
|
||||
aligned_edge = LEFT,
|
||||
buff = self.buff
|
||||
)
|
||||
|
||||
def fade_all_but(self, index_or_string, opacity = 0.5):
|
||||
arg = index_or_string
|
||||
if isinstance(arg, str):
|
||||
part = self.get_part_by_tex(arg)
|
||||
elif isinstance(arg, int):
|
||||
part = self.submobjects[arg]
|
||||
else:
|
||||
raise Exception("Expected int or string, got {0}".format(arg))
|
||||
for other_part in self.submobjects:
|
||||
if other_part is part:
|
||||
other_part.set_fill(opacity = 1)
|
||||
else:
|
||||
other_part.set_fill(opacity = opacity)
|
||||
|
||||
##########
|
||||
|
||||
def tex_hash(expression, template_tex_file):
|
||||
return str(hash(expression + template_tex_file))
|
||||
|
||||
|
|
|
@ -97,13 +97,13 @@ class VMobject(Mobject):
|
|||
|
||||
def get_fill_color(self):
|
||||
try:
|
||||
self.fill_rgb = np.clip(self.fill_rgb, 0, 1)
|
||||
self.fill_rgb = np.clip(self.fill_rgb, 0.0, 1.0)
|
||||
return Color(rgb = self.fill_rgb)
|
||||
except:
|
||||
return Color(WHITE)
|
||||
|
||||
def get_fill_opacity(self):
|
||||
return self.fill_opacity
|
||||
return np.clip(self.fill_opacity, 0, 1)
|
||||
|
||||
def get_stroke_color(self):
|
||||
try:
|
||||
|
@ -113,7 +113,7 @@ class VMobject(Mobject):
|
|||
return Color(WHITE)
|
||||
|
||||
def get_stroke_width(self):
|
||||
return self.stroke_width
|
||||
return max(0, self.stroke_width)
|
||||
|
||||
def get_color(self):
|
||||
if self.fill_opacity == 0:
|
||||
|
|
|
@ -21,12 +21,12 @@ import cPickle
|
|||
from nn.mnist_loader import load_data_wrapper
|
||||
|
||||
NN_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
|
||||
# PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases_36")
|
||||
# PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases_80")
|
||||
# PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases_ReLU")
|
||||
PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases")
|
||||
IMAGE_MAP_DATA_FILE = os.path.join(NN_DIRECTORY, "image_map")
|
||||
# PRETRAINED_DATA_FILE = "/Users/grant/cs/manim/nn/pretrained_weights_and_biases_on_zero"
|
||||
# DEFAULT_LAYER_SIZES = [28**2, 36, 10]
|
||||
# DEFAULT_LAYER_SIZES = [28**2, 80, 10]
|
||||
DEFAULT_LAYER_SIZES = [28**2, 16, 16, 10]
|
||||
|
||||
class Network(object):
|
||||
|
|
31
nn/part1.py
31
nn/part1.py
|
@ -122,7 +122,7 @@ class NetworkMobject(VGroup):
|
|||
"neuron_fill_color" : GREEN,
|
||||
"edge_color" : LIGHT_GREY,
|
||||
"edge_stroke_width" : 2,
|
||||
"edge_propogation_color" : GREEN,
|
||||
"edge_propogation_color" : YELLOW,
|
||||
"edge_propogation_time" : 1,
|
||||
"max_shown_neurons" : 16,
|
||||
"brace_for_large_layers" : True,
|
||||
|
@ -196,21 +196,28 @@ class NetworkMobject(VGroup):
|
|||
for l1, l2 in zip(self.layers[:-1], self.layers[1:]):
|
||||
edge_group = VGroup()
|
||||
for n1, n2 in it.product(l1.neurons, l2.neurons):
|
||||
edge = Line(
|
||||
n1.get_center(),
|
||||
n2.get_center(),
|
||||
buff = self.neuron_radius,
|
||||
stroke_color = self.edge_color,
|
||||
stroke_width = self.edge_stroke_width,
|
||||
)
|
||||
edge = self.get_edge(n1, n2)
|
||||
edge_group.add(edge)
|
||||
n1.edges_out.add(edge)
|
||||
n2.edges_in.add(edge)
|
||||
self.edge_groups.add(edge_group)
|
||||
self.add_to_back(self.edge_groups)
|
||||
|
||||
def get_edge(self, neuron1, neuron2):
|
||||
return Line(
|
||||
neuron1.get_center(),
|
||||
neuron2.get_center(),
|
||||
buff = self.neuron_radius,
|
||||
stroke_color = self.edge_color,
|
||||
stroke_width = self.edge_stroke_width,
|
||||
)
|
||||
|
||||
def get_active_layer(self, layer_index, activation_vector):
|
||||
layer = self.layers[layer_index].deepcopy()
|
||||
self.activate_layer(layer, activation_vector)
|
||||
return layer
|
||||
|
||||
def activate_layer(self, layer, activation_vector):
|
||||
n_neurons = len(layer.neurons)
|
||||
av = activation_vector
|
||||
def arr_to_num(arr):
|
||||
|
@ -238,6 +245,11 @@ class NetworkMobject(VGroup):
|
|||
)
|
||||
return layer
|
||||
|
||||
def activate_layers(self, input_vector):
|
||||
activations = self.neural_network.get_activation_of_all_layers(input_vector)
|
||||
for activation, layer in zip(activations, self.layers):
|
||||
self.activate_layer(layer, activation)
|
||||
|
||||
def deactivate_layers(self):
|
||||
all_neurons = VGroup(*it.chain(*[
|
||||
layer.neurons
|
||||
|
@ -2980,6 +2992,7 @@ class ContinualEdgeUpdate(ContinualAnimation):
|
|||
"max_stroke_width" : 3,
|
||||
"stroke_width_exp" : 7,
|
||||
"n_cycles" : 5,
|
||||
"colors" : [GREEN, GREEN, GREEN, RED],
|
||||
}
|
||||
def __init__(self, network_mob, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
|
@ -2988,7 +3001,7 @@ class ContinualEdgeUpdate(ContinualAnimation):
|
|||
self.move_to_targets = []
|
||||
for edge in edges:
|
||||
edge.colors = [
|
||||
random.choice([GREEN, GREEN, GREEN, RED])
|
||||
random.choice(self.colors)
|
||||
for x in range(n_cycles)
|
||||
]
|
||||
msw = self.max_stroke_width
|
||||
|
|
3340
nn/part2.py
3340
nn/part2.py
File diff suppressed because it is too large
Load diff
4519
nn/part3.py
Normal file
4519
nn/part3.py
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
4248
nn/scenes.py
4248
nn/scenes.py
File diff suppressed because it is too large
Load diff
|
@ -61,6 +61,10 @@ class Scene(object):
|
|||
"""
|
||||
pass
|
||||
|
||||
def setup_bases(self):
|
||||
for base in self.__class__.__bases__:
|
||||
base.setup(self)
|
||||
|
||||
def construct(self):
|
||||
pass #To be implemented in subclasses
|
||||
|
||||
|
@ -71,20 +75,24 @@ class Scene(object):
|
|||
self.name = name
|
||||
return self
|
||||
|
||||
def update_shared_locals(self, *keys):
|
||||
def set_variables_as_attrs(self, *objects, **newly_named_objects):
|
||||
"""
|
||||
Often in constructing a scene, it's nice to refer to
|
||||
what was a local variable from a previous subroutine,
|
||||
so a dict of shared_locals is recorded, and it can be updated
|
||||
by passing in the objects directly.
|
||||
This method is slightly hacky, making it a little easier
|
||||
for certain methods (typically subroutines of construct)
|
||||
to share local variables.
|
||||
"""
|
||||
caller_locals = inspect.currentframe().f_back.f_locals
|
||||
self.shared_locals.update(dict([
|
||||
(key, caller_locals[key])
|
||||
for key in keys
|
||||
]))
|
||||
for key, value in caller_locals.items():
|
||||
for o in objects:
|
||||
if value is o:
|
||||
setattr(self, key, value)
|
||||
for key, value in newly_named_objects.items():
|
||||
setattr(self, key, value)
|
||||
return self
|
||||
|
||||
def get_attrs(self, *keys):
|
||||
return [getattr(self, key) for key in keys]
|
||||
|
||||
### Only these methods should touch the camera
|
||||
|
||||
def set_camera(self, camera):
|
||||
|
@ -341,7 +349,7 @@ class Scene(object):
|
|||
animations.pop()
|
||||
#method should already have target then.
|
||||
else:
|
||||
mobject.target = mobject.deepcopy()
|
||||
mobject.target = mobject.copy()
|
||||
state["curr_method"].im_func(
|
||||
mobject.target, *state["method_args"]
|
||||
)
|
||||
|
@ -479,14 +487,14 @@ class Scene(object):
|
|||
|
||||
command = [
|
||||
FFMPEG_BIN,
|
||||
'-y', # overwrite output file if it exists
|
||||
'-y', # overwrite output file if it exists
|
||||
'-f', 'rawvideo',
|
||||
'-vcodec','rawvideo',
|
||||
'-s', '%dx%d'%(width, height), # size of one frame
|
||||
'-pix_fmt', 'rgba',
|
||||
'-r', str(fps), # frames per second
|
||||
'-i', '-', # The imput comes from a pipe
|
||||
'-an', # Tells FFMPEG not to expect any audio
|
||||
'-i', '-', # The imput comes from a pipe
|
||||
'-an', # Tells FFMPEG not to expect any audio
|
||||
'-vcodec', 'mpeg',
|
||||
'-c:v', 'libx264',
|
||||
'-pix_fmt', 'yuv420p',
|
||||
|
|
|
@ -276,7 +276,7 @@ class Eyes(VMobject):
|
|||
|
||||
pi = Randolph(mode = mode)
|
||||
eyes = VGroup(pi.eyes, pi.pupils)
|
||||
eyes.scale_to_fit_height(self.height)
|
||||
pi.scale(self.height/eyes.get_height())
|
||||
if self.submobjects:
|
||||
eyes.move_to(self, DOWN)
|
||||
else:
|
||||
|
@ -526,18 +526,17 @@ class PiCreatureScene(Scene):
|
|||
lambda anim : pi_creature in anim.mobject.submobject_family(),
|
||||
animations
|
||||
)
|
||||
if anims_with_pi_creature:
|
||||
for anim in anims_with_pi_creature:
|
||||
if isinstance(anim, Transform):
|
||||
index = anim.mobject.submobject_family().index(pi_creature)
|
||||
target_family = anim.target_mobject.submobject_family()
|
||||
target = target_family[index]
|
||||
if isinstance(target, PiCreature):
|
||||
target.look_at(point_of_interest)
|
||||
continue
|
||||
animations.append(
|
||||
ApplyMethod(pi_creature.look_at, point_of_interest)
|
||||
)
|
||||
for anim in anims_with_pi_creature:
|
||||
if isinstance(anim, Transform):
|
||||
index = anim.mobject.submobject_family().index(pi_creature)
|
||||
target_family = anim.target_mobject.submobject_family()
|
||||
target = target_family[index]
|
||||
if isinstance(target, PiCreature):
|
||||
target.look_at(point_of_interest)
|
||||
if not anims_with_pi_creature:
|
||||
animations.append(
|
||||
ApplyMethod(pi_creature.look_at, point_of_interest)
|
||||
)
|
||||
return animations
|
||||
|
||||
def blink(self):
|
||||
|
|
|
@ -61,36 +61,7 @@ class OpeningQuote(Scene):
|
|||
|
||||
class PatreonThanks(Scene):
|
||||
CONFIG = {
|
||||
"specific_patrons" : [
|
||||
"Ali Yahya",
|
||||
"Meshal Alshammari",
|
||||
"CrypticSwarm ",
|
||||
"Justin Helps",
|
||||
"Ankit Agarwal",
|
||||
"Yu Jun",
|
||||
"Shelby Doolittle",
|
||||
"Dave Nicponski",
|
||||
"Damion Kistler",
|
||||
"Juan Benet",
|
||||
"Othman Alikhan",
|
||||
"Markus Persson",
|
||||
"Dan Buchoff",
|
||||
"Derek Dai",
|
||||
"Joseph John Cox",
|
||||
"Luc Ritchie",
|
||||
"Nils Schneider",
|
||||
"Mathew Bramson",
|
||||
"Guido Gambardella",
|
||||
"Jerry Ling",
|
||||
"Mark Govea",
|
||||
"Vecht",
|
||||
"Shimin Kuang",
|
||||
"Rish Kundalia",
|
||||
"Achille Brighton",
|
||||
"Kirk Werklund",
|
||||
"Ripta Pasay",
|
||||
"Felipe Diniz",
|
||||
],
|
||||
"specific_patrons" : [],
|
||||
"max_patron_group_size" : 20,
|
||||
"patron_scale_val" : 0.8,
|
||||
|
||||
|
|
|
@ -155,6 +155,7 @@ class Line(VMobject):
|
|||
def put_start_and_end_on(self, new_start, new_end):
|
||||
self.start = new_start
|
||||
self.end = new_end
|
||||
self.buff = 0
|
||||
self.generate_points()
|
||||
return
|
||||
|
||||
|
@ -242,9 +243,13 @@ class Arrow(Line):
|
|||
if len(args) == 1:
|
||||
args = (points[0]+UP+LEFT, points[0])
|
||||
Line.__init__(self, *args, **kwargs)
|
||||
self.add_tip()
|
||||
self.init_tip()
|
||||
if self.use_rectangular_stem and not hasattr(self, "rect"):
|
||||
self.add_rectangular_stem()
|
||||
self.init_colors()
|
||||
|
||||
def init_tip(self):
|
||||
self.tip = self.add_tip()
|
||||
|
||||
def add_tip(self, add_at_end = True):
|
||||
tip = VMobject(
|
||||
|
@ -253,11 +258,11 @@ class Arrow(Line):
|
|||
fill_color = self.color,
|
||||
fill_opacity = 1,
|
||||
stroke_color = self.color,
|
||||
stroke_width = 0,
|
||||
)
|
||||
self.set_tip_points(tip, add_at_end, preserve_normal = False)
|
||||
self.tip = tip
|
||||
self.add(self.tip)
|
||||
self.init_colors()
|
||||
self.add(tip)
|
||||
return tip
|
||||
|
||||
def add_rectangular_stem(self):
|
||||
self.rect = Rectangle(
|
||||
|
@ -283,6 +288,10 @@ class Arrow(Line):
|
|||
self.rectangular_stem_width,
|
||||
self.max_stem_width_to_tip_width_ratio*tip_base_width,
|
||||
)
|
||||
if hasattr(self, "second_tip"):
|
||||
start = center_of_mass(
|
||||
self.second_tip.get_anchors()[1:]
|
||||
)
|
||||
self.rect.set_points_as_corners([
|
||||
tip_base + perp_vect*width/2,
|
||||
start + perp_vect*width/2,
|
||||
|
@ -310,7 +319,7 @@ class Arrow(Line):
|
|||
|
||||
indices = (-2, -1) if add_at_end else (1, 0)
|
||||
pre_end_point, end_point = [
|
||||
self.points[index]
|
||||
self.get_anchors()[index]
|
||||
for index in indices
|
||||
]
|
||||
vect = end_point - pre_end_point
|
||||
|
@ -319,7 +328,6 @@ class Arrow(Line):
|
|||
if np.linalg.norm(v) == 0:
|
||||
v[0] = 1
|
||||
v *= tip_length/np.linalg.norm(v)
|
||||
|
||||
ratio = self.tip_width_to_length_ratio
|
||||
tip.set_points_as_corners([
|
||||
end_point,
|
||||
|
@ -360,7 +368,8 @@ class Arrow(Line):
|
|||
Line.scale(self, scale_factor, **kwargs)
|
||||
if self.preserve_tip_size_when_scaling:
|
||||
self.set_tip_points(self.tip)
|
||||
self.set_rectangular_stem_points()
|
||||
if self.use_rectangular_stem:
|
||||
self.set_rectangular_stem_points()
|
||||
return self
|
||||
|
||||
class Vector(Arrow):
|
||||
|
@ -374,9 +383,9 @@ class Vector(Arrow):
|
|||
Arrow.__init__(self, ORIGIN, direction, **kwargs)
|
||||
|
||||
class DoubleArrow(Arrow):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Arrow.__init__(self, *args, **kwargs)
|
||||
self.add_tip(add_at_end = False)
|
||||
def init_tip(self):
|
||||
self.tip = self.add_tip()
|
||||
self.second_tip = self.add_tip(add_at_end = False)
|
||||
|
||||
class CubicBezier(VMobject):
|
||||
def __init__(self, points, **kwargs):
|
||||
|
|
|
@ -11,9 +11,9 @@ class DecimalNumber(VMobject):
|
|||
"num_decimal_points" : 2,
|
||||
"digit_to_digit_buff" : 0.05
|
||||
}
|
||||
def __init__(self, float_num, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
num_string = '%.*f'%(self.num_decimal_points, float_num)
|
||||
def __init__(self, number, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
num_string = '%.*f'%(self.num_decimal_points, number)
|
||||
VMobject.__init__(self, *[
|
||||
TexMobject(char)
|
||||
for char in num_string
|
||||
|
@ -22,7 +22,7 @@ class DecimalNumber(VMobject):
|
|||
buff = self.digit_to_digit_buff,
|
||||
aligned_edge = DOWN
|
||||
)
|
||||
if float_num < 0:
|
||||
if number < 0:
|
||||
minus = self.submobjects[0]
|
||||
minus.next_to(
|
||||
self.submobjects[1], LEFT,
|
||||
|
@ -46,13 +46,15 @@ class Integer(VGroup):
|
|||
|
||||
class ChangingDecimal(Animation):
|
||||
CONFIG = {
|
||||
"num_decimal_points" : 2,
|
||||
"num_decimal_points" : None,
|
||||
"spare_parts" : 2,
|
||||
"position_update_func" : None,
|
||||
"tracked_mobject" : None,
|
||||
}
|
||||
def __init__(self, decimal_number, number_update_func, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
if self.num_decimal_points is None:
|
||||
self.num_decimal_points = decimal_number.num_decimal_points
|
||||
decimal_number.add(*[
|
||||
VectorizedPoint(decimal_number.get_corner(DOWN+LEFT))
|
||||
for x in range(self.spare_parts)]
|
||||
|
@ -65,9 +67,9 @@ class ChangingDecimal(Animation):
|
|||
|
||||
def update_number(self, alpha):
|
||||
decimal = self.decimal_number
|
||||
new_number = self.number_update_func(alpha)
|
||||
new_decimal = DecimalNumber(
|
||||
self.number_update_func(alpha),
|
||||
num_decimal_points = self.num_decimal_points
|
||||
new_number, num_decimal_points = self.num_decimal_points
|
||||
)
|
||||
new_decimal.replace(decimal, dim_to_match = 1)
|
||||
new_decimal.highlight(decimal.get_color())
|
||||
|
@ -78,6 +80,7 @@ class ChangingDecimal(Animation):
|
|||
]
|
||||
for sm1, sm2 in zip(*families):
|
||||
sm1.interpolate(sm1, sm2, 1)
|
||||
self.mobject.number = new_number
|
||||
|
||||
def update_position(self):
|
||||
if self.position_update_func is not None:
|
||||
|
|
Loading…
Add table
Reference in a new issue