This commit is contained in:
ddxtanx 2017-11-22 19:06:23 -06:00
commit 80dda4eb28
20 changed files with 7962 additions and 4428 deletions

View file

@ -82,12 +82,10 @@ class Write(ShowCreation):
def establish_run_time(self, mobject): def establish_run_time(self, mobject):
num_subs = len(mobject.family_members_with_points()) num_subs = len(mobject.family_members_with_points())
if num_subs < 5: if num_subs < 15:
self.run_time = 1 self.run_time = 1
elif num_subs < 15:
self.run_time = 2
else: else:
self.run_time = 3 self.run_time = 2
class DrawBorderThenFill(Animation): class DrawBorderThenFill(Animation):
CONFIG = { CONFIG = {
@ -330,9 +328,9 @@ class LaggedStart(Animation):
anim.update(alpha) anim.update(alpha)
return self return self
# def clean_up(self, *args, **kwargs): def clean_up(self, *args, **kwargs):
# for anim in self.subanimations: for anim in self.subanimations:
# anim.clean_up(*args, **kwargs) anim.clean_up(*args, **kwargs)
class DelayByOrder(Animation): class DelayByOrder(Animation):
""" """

View file

@ -59,7 +59,8 @@ class Transform(Animation):
Animation.clean_up(self, surrounding_scene) Animation.clean_up(self, surrounding_scene)
if self.replace_mobject_with_target_in_scene and surrounding_scene is not None: if self.replace_mobject_with_target_in_scene and surrounding_scene is not None:
surrounding_scene.remove(self.mobject) 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): class ReplacementTransform(Transform):
CONFIG = { CONFIG = {

View file

@ -141,15 +141,21 @@ class Camera(object):
def get_pen_and_fill(self, vmobject): def get_pen_and_fill(self, vmobject):
pen = aggdraw.Pen( 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) max(vmobject.stroke_width, 0)
) )
fill = aggdraw.Brush( 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()) opacity = int(255*vmobject.get_fill_opacity())
) )
return (pen, fill) 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): def get_stroke_color(self, vmobject):
return vmobject.get_stroke_color() return vmobject.get_stroke_color()

View file

@ -6,8 +6,7 @@ DEFAULT_WIDTH = 1920
LOW_QUALITY_FRAME_DURATION = 1./15 LOW_QUALITY_FRAME_DURATION = 1./15
MEDIUM_QUALITY_FRAME_DURATION = 1./30 MEDIUM_QUALITY_FRAME_DURATION = 1./30
# PRODUCTION_QUALITY_FRAME_DURATION = 1./60 PRODUCTION_QUALITY_FRAME_DURATION = 1./60
PRODUCTION_QUALITY_FRAME_DURATION = 1./30
#There might be other configuration than pixel_shape later... #There might be other configuration than pixel_shape later...
PRODUCTION_QUALITY_CAMERA_CONFIG = { PRODUCTION_QUALITY_CAMERA_CONFIG = {

View file

@ -37,7 +37,6 @@ def play_chord(*nums):
def play_error_sound(): def play_error_sound():
play_chord(11, 8, 6, 1) play_chord(11, 8, 6, 1)
def play_finish_sound(): def play_finish_sound():
play_chord(12, 9, 5, 2) play_chord(12, 9, 5, 2)
@ -422,7 +421,7 @@ def get_full_image_path(image_file_name):
for path in possible_paths: for path in possible_paths:
if os.path.exists(path): if os.path.exists(path):
return path return path
raise IOError("File not Found") raise IOError("File %s not Found"%image_file_name)
def drag_pixels(frames): def drag_pixels(frames):
curr = frames[0] curr = frames[0]

View file

@ -399,11 +399,17 @@ class Mobject(object):
## Color functions ## 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). 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): def gradient_highlight(self, *colors):
self.submobject_gradient_highlight(*colors) self.submobject_gradient_highlight(*colors)
@ -446,11 +452,14 @@ class Mobject(object):
return self.color return self.color
## ##
def save_state(self): def save_state(self, use_deepcopy = False):
if hasattr(self, "saved_state"): if hasattr(self, "saved_state"):
#Prevent exponential growth of data #Prevent exponential growth of data
self.saved_state = None 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 return self
def restore(self): def restore(self):

View file

@ -30,15 +30,11 @@ class PMobject(Mobject):
self.rgbas = np.append(self.rgbas, rgbas, axis = 0) self.rgbas = np.append(self.rgbas, rgbas, axis = 0)
return self 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) rgba = color_to_rgba(color)
mobs = self.family_members_with_points() if family else [self] mobs = self.family_members_with_points() if family else [self]
for mob in mobs: for mob in mobs:
if condition: mob.rgbas[:,:] = rgba
to_change = np.apply_along_axis(condition, 1, mob.points)
mob.rgbas[to_change, :] = rgba
else:
mob.rgbas[:,:] = rgba
return self return self
def gradient_highlight(self, start_color, end_color): def gradient_highlight(self, start_color, end_color):

View file

@ -26,7 +26,6 @@ class TexSymbol(VMobjectFromSVGPathstring):
self.set_stroke(width = added_width + mobject.get_stroke_width()) self.set_stroke(width = added_width + mobject.get_stroke_width())
self.set_fill(opacity = opacity) self.set_fill(opacity = opacity)
class TexMobject(SVGMobject): class TexMobject(SVGMobject):
CONFIG = { CONFIG = {
"template_tex_file" : TEMPLATE_TEX_FILE, "template_tex_file" : TEMPLATE_TEX_FILE,
@ -264,6 +263,43 @@ class Brace(TexMobject):
vect = self.get_tip() - self.get_center() vect = self.get_tip() - self.get_center()
return vect/np.linalg.norm(vect) 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): def tex_hash(expression, template_tex_file):
return str(hash(expression + template_tex_file)) return str(hash(expression + template_tex_file))

View file

@ -97,13 +97,13 @@ class VMobject(Mobject):
def get_fill_color(self): def get_fill_color(self):
try: 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) return Color(rgb = self.fill_rgb)
except: except:
return Color(WHITE) return Color(WHITE)
def get_fill_opacity(self): def get_fill_opacity(self):
return self.fill_opacity return np.clip(self.fill_opacity, 0, 1)
def get_stroke_color(self): def get_stroke_color(self):
try: try:
@ -113,7 +113,7 @@ class VMobject(Mobject):
return Color(WHITE) return Color(WHITE)
def get_stroke_width(self): def get_stroke_width(self):
return self.stroke_width return max(0, self.stroke_width)
def get_color(self): def get_color(self):
if self.fill_opacity == 0: if self.fill_opacity == 0:

View file

@ -21,12 +21,12 @@ import cPickle
from nn.mnist_loader import load_data_wrapper from nn.mnist_loader import load_data_wrapper
NN_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) 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_ReLU")
PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases") PRETRAINED_DATA_FILE = os.path.join(NN_DIRECTORY, "pretrained_weights_and_biases")
IMAGE_MAP_DATA_FILE = os.path.join(NN_DIRECTORY, "image_map") 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" # 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] DEFAULT_LAYER_SIZES = [28**2, 16, 16, 10]
class Network(object): class Network(object):

View file

@ -122,7 +122,7 @@ class NetworkMobject(VGroup):
"neuron_fill_color" : GREEN, "neuron_fill_color" : GREEN,
"edge_color" : LIGHT_GREY, "edge_color" : LIGHT_GREY,
"edge_stroke_width" : 2, "edge_stroke_width" : 2,
"edge_propogation_color" : GREEN, "edge_propogation_color" : YELLOW,
"edge_propogation_time" : 1, "edge_propogation_time" : 1,
"max_shown_neurons" : 16, "max_shown_neurons" : 16,
"brace_for_large_layers" : True, "brace_for_large_layers" : True,
@ -196,21 +196,28 @@ class NetworkMobject(VGroup):
for l1, l2 in zip(self.layers[:-1], self.layers[1:]): for l1, l2 in zip(self.layers[:-1], self.layers[1:]):
edge_group = VGroup() edge_group = VGroup()
for n1, n2 in it.product(l1.neurons, l2.neurons): for n1, n2 in it.product(l1.neurons, l2.neurons):
edge = Line( edge = self.get_edge(n1, n2)
n1.get_center(),
n2.get_center(),
buff = self.neuron_radius,
stroke_color = self.edge_color,
stroke_width = self.edge_stroke_width,
)
edge_group.add(edge) edge_group.add(edge)
n1.edges_out.add(edge) n1.edges_out.add(edge)
n2.edges_in.add(edge) n2.edges_in.add(edge)
self.edge_groups.add(edge_group) self.edge_groups.add(edge_group)
self.add_to_back(self.edge_groups) 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): def get_active_layer(self, layer_index, activation_vector):
layer = self.layers[layer_index].deepcopy() 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) n_neurons = len(layer.neurons)
av = activation_vector av = activation_vector
def arr_to_num(arr): def arr_to_num(arr):
@ -238,6 +245,11 @@ class NetworkMobject(VGroup):
) )
return layer 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): def deactivate_layers(self):
all_neurons = VGroup(*it.chain(*[ all_neurons = VGroup(*it.chain(*[
layer.neurons layer.neurons
@ -2980,6 +2992,7 @@ class ContinualEdgeUpdate(ContinualAnimation):
"max_stroke_width" : 3, "max_stroke_width" : 3,
"stroke_width_exp" : 7, "stroke_width_exp" : 7,
"n_cycles" : 5, "n_cycles" : 5,
"colors" : [GREEN, GREEN, GREEN, RED],
} }
def __init__(self, network_mob, **kwargs): def __init__(self, network_mob, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs)
@ -2988,7 +3001,7 @@ class ContinualEdgeUpdate(ContinualAnimation):
self.move_to_targets = [] self.move_to_targets = []
for edge in edges: for edge in edges:
edge.colors = [ edge.colors = [
random.choice([GREEN, GREEN, GREEN, RED]) random.choice(self.colors)
for x in range(n_cycles) for x in range(n_cycles)
] ]
msw = self.max_stroke_width msw = self.max_stroke_width

File diff suppressed because it is too large Load diff

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

File diff suppressed because it is too large Load diff

View file

@ -61,6 +61,10 @@ class Scene(object):
""" """
pass pass
def setup_bases(self):
for base in self.__class__.__bases__:
base.setup(self)
def construct(self): def construct(self):
pass #To be implemented in subclasses pass #To be implemented in subclasses
@ -71,20 +75,24 @@ class Scene(object):
self.name = name self.name = name
return self 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 This method is slightly hacky, making it a little easier
what was a local variable from a previous subroutine, for certain methods (typically subroutines of construct)
so a dict of shared_locals is recorded, and it can be updated to share local variables.
by passing in the objects directly.
""" """
caller_locals = inspect.currentframe().f_back.f_locals caller_locals = inspect.currentframe().f_back.f_locals
self.shared_locals.update(dict([ for key, value in caller_locals.items():
(key, caller_locals[key]) for o in objects:
for key in keys if value is o:
])) setattr(self, key, value)
for key, value in newly_named_objects.items():
setattr(self, key, value)
return self return self
def get_attrs(self, *keys):
return [getattr(self, key) for key in keys]
### Only these methods should touch the camera ### Only these methods should touch the camera
def set_camera(self, camera): def set_camera(self, camera):
@ -341,7 +349,7 @@ class Scene(object):
animations.pop() animations.pop()
#method should already have target then. #method should already have target then.
else: else:
mobject.target = mobject.deepcopy() mobject.target = mobject.copy()
state["curr_method"].im_func( state["curr_method"].im_func(
mobject.target, *state["method_args"] mobject.target, *state["method_args"]
) )
@ -479,14 +487,14 @@ class Scene(object):
command = [ command = [
FFMPEG_BIN, FFMPEG_BIN,
'-y', # overwrite output file if it exists '-y', # overwrite output file if it exists
'-f', 'rawvideo', '-f', 'rawvideo',
'-vcodec','rawvideo', '-vcodec','rawvideo',
'-s', '%dx%d'%(width, height), # size of one frame '-s', '%dx%d'%(width, height), # size of one frame
'-pix_fmt', 'rgba', '-pix_fmt', 'rgba',
'-r', str(fps), # frames per second '-r', str(fps), # frames per second
'-i', '-', # The imput comes from a pipe '-i', '-', # The imput comes from a pipe
'-an', # Tells FFMPEG not to expect any audio '-an', # Tells FFMPEG not to expect any audio
'-vcodec', 'mpeg', '-vcodec', 'mpeg',
'-c:v', 'libx264', '-c:v', 'libx264',
'-pix_fmt', 'yuv420p', '-pix_fmt', 'yuv420p',

View file

@ -276,7 +276,7 @@ class Eyes(VMobject):
pi = Randolph(mode = mode) pi = Randolph(mode = mode)
eyes = VGroup(pi.eyes, pi.pupils) eyes = VGroup(pi.eyes, pi.pupils)
eyes.scale_to_fit_height(self.height) pi.scale(self.height/eyes.get_height())
if self.submobjects: if self.submobjects:
eyes.move_to(self, DOWN) eyes.move_to(self, DOWN)
else: else:
@ -526,18 +526,17 @@ class PiCreatureScene(Scene):
lambda anim : pi_creature in anim.mobject.submobject_family(), lambda anim : pi_creature in anim.mobject.submobject_family(),
animations animations
) )
if anims_with_pi_creature: for anim in anims_with_pi_creature:
for anim in anims_with_pi_creature: if isinstance(anim, Transform):
if isinstance(anim, Transform): index = anim.mobject.submobject_family().index(pi_creature)
index = anim.mobject.submobject_family().index(pi_creature) target_family = anim.target_mobject.submobject_family()
target_family = anim.target_mobject.submobject_family() target = target_family[index]
target = target_family[index] if isinstance(target, PiCreature):
if isinstance(target, PiCreature): target.look_at(point_of_interest)
target.look_at(point_of_interest) if not anims_with_pi_creature:
continue animations.append(
animations.append( ApplyMethod(pi_creature.look_at, point_of_interest)
ApplyMethod(pi_creature.look_at, point_of_interest) )
)
return animations return animations
def blink(self): def blink(self):

View file

@ -61,36 +61,7 @@ class OpeningQuote(Scene):
class PatreonThanks(Scene): class PatreonThanks(Scene):
CONFIG = { CONFIG = {
"specific_patrons" : [ "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",
],
"max_patron_group_size" : 20, "max_patron_group_size" : 20,
"patron_scale_val" : 0.8, "patron_scale_val" : 0.8,

View file

@ -155,6 +155,7 @@ class Line(VMobject):
def put_start_and_end_on(self, new_start, new_end): def put_start_and_end_on(self, new_start, new_end):
self.start = new_start self.start = new_start
self.end = new_end self.end = new_end
self.buff = 0
self.generate_points() self.generate_points()
return return
@ -242,9 +243,13 @@ class Arrow(Line):
if len(args) == 1: if len(args) == 1:
args = (points[0]+UP+LEFT, points[0]) args = (points[0]+UP+LEFT, points[0])
Line.__init__(self, *args, **kwargs) Line.__init__(self, *args, **kwargs)
self.add_tip() self.init_tip()
if self.use_rectangular_stem and not hasattr(self, "rect"): if self.use_rectangular_stem and not hasattr(self, "rect"):
self.add_rectangular_stem() self.add_rectangular_stem()
self.init_colors()
def init_tip(self):
self.tip = self.add_tip()
def add_tip(self, add_at_end = True): def add_tip(self, add_at_end = True):
tip = VMobject( tip = VMobject(
@ -253,11 +258,11 @@ class Arrow(Line):
fill_color = self.color, fill_color = self.color,
fill_opacity = 1, fill_opacity = 1,
stroke_color = self.color, stroke_color = self.color,
stroke_width = 0,
) )
self.set_tip_points(tip, add_at_end, preserve_normal = False) self.set_tip_points(tip, add_at_end, preserve_normal = False)
self.tip = tip self.add(tip)
self.add(self.tip) return tip
self.init_colors()
def add_rectangular_stem(self): def add_rectangular_stem(self):
self.rect = Rectangle( self.rect = Rectangle(
@ -283,6 +288,10 @@ class Arrow(Line):
self.rectangular_stem_width, self.rectangular_stem_width,
self.max_stem_width_to_tip_width_ratio*tip_base_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([ self.rect.set_points_as_corners([
tip_base + perp_vect*width/2, tip_base + perp_vect*width/2,
start + 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) indices = (-2, -1) if add_at_end else (1, 0)
pre_end_point, end_point = [ pre_end_point, end_point = [
self.points[index] self.get_anchors()[index]
for index in indices for index in indices
] ]
vect = end_point - pre_end_point vect = end_point - pre_end_point
@ -319,7 +328,6 @@ class Arrow(Line):
if np.linalg.norm(v) == 0: if np.linalg.norm(v) == 0:
v[0] = 1 v[0] = 1
v *= tip_length/np.linalg.norm(v) v *= tip_length/np.linalg.norm(v)
ratio = self.tip_width_to_length_ratio ratio = self.tip_width_to_length_ratio
tip.set_points_as_corners([ tip.set_points_as_corners([
end_point, end_point,
@ -360,7 +368,8 @@ class Arrow(Line):
Line.scale(self, scale_factor, **kwargs) Line.scale(self, scale_factor, **kwargs)
if self.preserve_tip_size_when_scaling: if self.preserve_tip_size_when_scaling:
self.set_tip_points(self.tip) self.set_tip_points(self.tip)
self.set_rectangular_stem_points() if self.use_rectangular_stem:
self.set_rectangular_stem_points()
return self return self
class Vector(Arrow): class Vector(Arrow):
@ -374,9 +383,9 @@ class Vector(Arrow):
Arrow.__init__(self, ORIGIN, direction, **kwargs) Arrow.__init__(self, ORIGIN, direction, **kwargs)
class DoubleArrow(Arrow): class DoubleArrow(Arrow):
def __init__(self, *args, **kwargs): def init_tip(self):
Arrow.__init__(self, *args, **kwargs) self.tip = self.add_tip()
self.add_tip(add_at_end = False) self.second_tip = self.add_tip(add_at_end = False)
class CubicBezier(VMobject): class CubicBezier(VMobject):
def __init__(self, points, **kwargs): def __init__(self, points, **kwargs):

View file

@ -11,9 +11,9 @@ class DecimalNumber(VMobject):
"num_decimal_points" : 2, "num_decimal_points" : 2,
"digit_to_digit_buff" : 0.05 "digit_to_digit_buff" : 0.05
} }
def __init__(self, float_num, **kwargs): def __init__(self, number, **kwargs):
digest_config(self, kwargs) digest_config(self, kwargs, locals())
num_string = '%.*f'%(self.num_decimal_points, float_num) num_string = '%.*f'%(self.num_decimal_points, number)
VMobject.__init__(self, *[ VMobject.__init__(self, *[
TexMobject(char) TexMobject(char)
for char in num_string for char in num_string
@ -22,7 +22,7 @@ class DecimalNumber(VMobject):
buff = self.digit_to_digit_buff, buff = self.digit_to_digit_buff,
aligned_edge = DOWN aligned_edge = DOWN
) )
if float_num < 0: if number < 0:
minus = self.submobjects[0] minus = self.submobjects[0]
minus.next_to( minus.next_to(
self.submobjects[1], LEFT, self.submobjects[1], LEFT,
@ -46,13 +46,15 @@ class Integer(VGroup):
class ChangingDecimal(Animation): class ChangingDecimal(Animation):
CONFIG = { CONFIG = {
"num_decimal_points" : 2, "num_decimal_points" : None,
"spare_parts" : 2, "spare_parts" : 2,
"position_update_func" : None, "position_update_func" : None,
"tracked_mobject" : None, "tracked_mobject" : None,
} }
def __init__(self, decimal_number, number_update_func, **kwargs): def __init__(self, decimal_number, number_update_func, **kwargs):
digest_config(self, kwargs, locals()) digest_config(self, kwargs, locals())
if self.num_decimal_points is None:
self.num_decimal_points = decimal_number.num_decimal_points
decimal_number.add(*[ decimal_number.add(*[
VectorizedPoint(decimal_number.get_corner(DOWN+LEFT)) VectorizedPoint(decimal_number.get_corner(DOWN+LEFT))
for x in range(self.spare_parts)] for x in range(self.spare_parts)]
@ -65,9 +67,9 @@ class ChangingDecimal(Animation):
def update_number(self, alpha): def update_number(self, alpha):
decimal = self.decimal_number decimal = self.decimal_number
new_number = self.number_update_func(alpha)
new_decimal = DecimalNumber( new_decimal = DecimalNumber(
self.number_update_func(alpha), new_number, num_decimal_points = self.num_decimal_points
num_decimal_points = self.num_decimal_points
) )
new_decimal.replace(decimal, dim_to_match = 1) new_decimal.replace(decimal, dim_to_match = 1)
new_decimal.highlight(decimal.get_color()) new_decimal.highlight(decimal.get_color())
@ -78,6 +80,7 @@ class ChangingDecimal(Animation):
] ]
for sm1, sm2 in zip(*families): for sm1, sm2 in zip(*families):
sm1.interpolate(sm1, sm2, 1) sm1.interpolate(sm1, sm2, 1)
self.mobject.number = new_number
def update_position(self): def update_position(self):
if self.position_update_func is not None: if self.position_update_func is not None: