mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
redimentary ability to preview scenes
This commit is contained in:
parent
b5fbf7edea
commit
b527568f5c
6 changed files with 98 additions and 36 deletions
|
@ -12,7 +12,7 @@ PRODUCTION_QUALITY_DISPLAY_CONFIG = {
|
||||||
LOW_QUALITY_DISPLAY_CONFIG = {
|
LOW_QUALITY_DISPLAY_CONFIG = {
|
||||||
"height" : 480,
|
"height" : 480,
|
||||||
"width" : 840,
|
"width" : 840,
|
||||||
"frame_duration" : 0.15,
|
"frame_duration" : 0.04,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ LOGO_RADIUS = 1.5
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
circle = Circle(density = 100, color = 'skyblue').repeat(5).scale(LOGO_RADIUS)
|
||||||
sphere = Sphere(density = 100, color = DARK_BLUE).scale(LOGO_RADIUS)
|
sphere = Sphere(density = 50, color = DARK_BLUE).scale(LOGO_RADIUS)
|
||||||
sphere.rotate(-np.pi / 7, [1, 0, 0])
|
sphere.rotate(-np.pi / 7, [1, 0, 0])
|
||||||
sphere.rotate(-np.pi / 7)
|
sphere.rotate(-np.pi / 7)
|
||||||
alpha = 0.3
|
alpha = 0.3
|
||||||
|
|
|
@ -1582,9 +1582,6 @@ class IntersectionChoppingExamples(Scene):
|
||||||
self.remove(*self.mobjects)
|
self.remove(*self.mobjects)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
17
scene.py
17
scene.py
|
@ -15,6 +15,7 @@ from mobject import *
|
||||||
from image_mobject import *
|
from image_mobject import *
|
||||||
from animation import *
|
from animation import *
|
||||||
import displayer as disp
|
import displayer as disp
|
||||||
|
from tk_scene import TkSceneRoot
|
||||||
|
|
||||||
DEFAULT_COUNT_NUM_OFFSET = (SPACE_WIDTH - 1, SPACE_HEIGHT - 1, 0)
|
DEFAULT_COUNT_NUM_OFFSET = (SPACE_WIDTH - 1, SPACE_HEIGHT - 1, 0)
|
||||||
DEFAULT_COUNT_RUN_TIME = 5.0
|
DEFAULT_COUNT_RUN_TIME = 5.0
|
||||||
|
@ -45,6 +46,7 @@ class Scene(object):
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
return self
|
||||||
|
|
||||||
def add(self, *mobjects):
|
def add(self, *mobjects):
|
||||||
"""
|
"""
|
||||||
|
@ -56,6 +58,7 @@ class Scene(object):
|
||||||
#now be closer to the foreground.
|
#now be closer to the foreground.
|
||||||
self.remove(mobject)
|
self.remove(mobject)
|
||||||
self.mobjects.append(mobject)
|
self.mobjects.append(mobject)
|
||||||
|
return self
|
||||||
|
|
||||||
def remove(self, *mobjects):
|
def remove(self, *mobjects):
|
||||||
for mobject in mobjects:
|
for mobject in mobjects:
|
||||||
|
@ -63,6 +66,8 @@ class Scene(object):
|
||||||
raise Exception("Removing something which is not a mobject")
|
raise Exception("Removing something which is not a mobject")
|
||||||
while mobject in self.mobjects:
|
while mobject in self.mobjects:
|
||||||
self.mobjects.remove(mobject)
|
self.mobjects.remove(mobject)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
def highlight_region(self, region, color = None):
|
def highlight_region(self, region, color = None):
|
||||||
self.background = disp.paint_region(
|
self.background = disp.paint_region(
|
||||||
|
@ -70,6 +75,7 @@ class Scene(object):
|
||||||
image_array = self.background,
|
image_array = self.background,
|
||||||
color = color,
|
color = color,
|
||||||
)
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
def reset_background(self):
|
def reset_background(self):
|
||||||
self.background = self.original_background
|
self.background = self.original_background
|
||||||
|
@ -100,6 +106,7 @@ class Scene(object):
|
||||||
animation.clean_up()
|
animation.clean_up()
|
||||||
self.add(*moving_mobjects)
|
self.add(*moving_mobjects)
|
||||||
progress_bar.finish()
|
progress_bar.finish()
|
||||||
|
return self
|
||||||
|
|
||||||
def count(self, items, item_type = "mobject", *args, **kwargs):
|
def count(self, items, item_type = "mobject", *args, **kwargs):
|
||||||
if item_type == "mobject":
|
if item_type == "mobject":
|
||||||
|
@ -108,6 +115,7 @@ class Scene(object):
|
||||||
self.count_regions(items, *args, **kwargs)
|
self.count_regions(items, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
raise Exception("Unknown item_type, should be mobject or region")
|
raise Exception("Unknown item_type, should be mobject or region")
|
||||||
|
return self
|
||||||
|
|
||||||
def count_mobjects(
|
def count_mobjects(
|
||||||
self, mobjects, mode = "highlight",
|
self, mobjects, mode = "highlight",
|
||||||
|
@ -147,6 +155,7 @@ class Scene(object):
|
||||||
self.remove(num_mob)
|
self.remove(num_mob)
|
||||||
self.add(num_mob)
|
self.add(num_mob)
|
||||||
self.number = num_mob
|
self.number = num_mob
|
||||||
|
return self
|
||||||
|
|
||||||
def count_regions(self, regions,
|
def count_regions(self, regions,
|
||||||
mode = "one_at_a_time",
|
mode = "one_at_a_time",
|
||||||
|
@ -167,6 +176,7 @@ class Scene(object):
|
||||||
self.remove(num_mob)
|
self.remove(num_mob)
|
||||||
self.add(num_mob)
|
self.add(num_mob)
|
||||||
self.number = num_mob
|
self.number = num_mob
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
|
@ -177,6 +187,7 @@ class Scene(object):
|
||||||
|
|
||||||
def dither(self, duration = DEFAULT_DITHER_TIME):
|
def dither(self, duration = DEFAULT_DITHER_TIME):
|
||||||
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
||||||
|
return self
|
||||||
|
|
||||||
def write_to_gif(self, name = None,
|
def write_to_gif(self, name = None,
|
||||||
end_dither_time = DEFAULT_DITHER_TIME):
|
end_dither_time = DEFAULT_DITHER_TIME):
|
||||||
|
@ -188,9 +199,13 @@ class Scene(object):
|
||||||
self.dither(end_dither_time)
|
self.dither(end_dither_time)
|
||||||
disp.write_to_movie(self, name or str(self))
|
disp.write_to_movie(self, name or str(self))
|
||||||
|
|
||||||
def show(self):
|
def show_frame(self):
|
||||||
Image.fromarray(self.get_frame()).show()
|
Image.fromarray(self.get_frame()).show()
|
||||||
|
|
||||||
|
def preview(self):
|
||||||
|
self.dither()
|
||||||
|
TkSceneRoot(self)
|
||||||
|
|
||||||
def save_image(self, path):
|
def save_image(self, path):
|
||||||
path = os.path.join(MOVIE_DIR, path) + ".png"
|
path = os.path.join(MOVIE_DIR, path) + ".png"
|
||||||
Image.fromarray(self.get_frame()).save(path)
|
Image.fromarray(self.get_frame()).save(path)
|
||||||
|
|
64
tex_utils.py
64
tex_utils.py
|
@ -3,51 +3,55 @@ import itertools as it
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from constants import *
|
from constants import *
|
||||||
|
|
||||||
|
#TODO, Cleanup and refactor this file.
|
||||||
|
|
||||||
def tex_to_image(expression,
|
def tex_to_image(expression,
|
||||||
size = "\HUGE",
|
size = "\HUGE",
|
||||||
template_tex_file = TEMPLATE_TEX_FILE):
|
template_tex_file = TEMPLATE_TEX_FILE):
|
||||||
"""
|
"""
|
||||||
Returns list of images for correpsonding with a list of expressions
|
Returns list of images for correpsonding with a list of expressions
|
||||||
"""
|
"""
|
||||||
return_list = False
|
return_list = isinstance(expression, list)
|
||||||
arg = expression
|
simple_tex = "".join(expression)
|
||||||
if not isinstance(expression, str):
|
if return_list:
|
||||||
#TODO, verify that expression is iterable of strings
|
expression = tex_expression_list_as_string(expression)
|
||||||
expression = "\n".join([
|
exp_hash = str(hash(expression + size))
|
||||||
"\onslide<%d>{"%count + exp + "}"
|
image_dir = os.path.join(TEX_IMAGE_DIR, exp_hash)
|
||||||
for count, exp in zip(it.count(1), expression)
|
if os.path.exists(image_dir):
|
||||||
])
|
result = [
|
||||||
return_list = True
|
Image.open(os.path.join(image_dir, png_file)).convert('RGB')
|
||||||
filestem = os.path.join(
|
for png_file in os.listdir(image_dir)
|
||||||
TEX_DIR, str(hash(expression + size))
|
]
|
||||||
)
|
else:
|
||||||
if not os.path.exists(filestem + ".dvi"):
|
filestem = os.path.join(TEX_DIR, exp_hash)
|
||||||
if not os.path.exists(filestem + ".tex"):
|
if not os.path.exists(filestem + ".tex"):
|
||||||
print " ".join([
|
print "Writing %s at size %s to %s.tex"%(
|
||||||
"Writing ",
|
simple_tex, size, filestem
|
||||||
"".join(arg),
|
)
|
||||||
"at size %s to "%(size),
|
|
||||||
filestem,
|
|
||||||
])
|
|
||||||
with open(template_tex_file, "r") as infile:
|
with open(template_tex_file, "r") as infile:
|
||||||
body = infile.read()
|
body = infile.read()
|
||||||
body = body.replace(SIZE_TO_REPLACE, size)
|
body = body.replace(SIZE_TO_REPLACE, size)
|
||||||
body = body.replace(TEX_TEXT_TO_REPLACE, expression)
|
body = body.replace(TEX_TEXT_TO_REPLACE, expression)
|
||||||
with open(filestem + ".tex", "w") as outfile:
|
with open(filestem + ".tex", "w") as outfile:
|
||||||
outfile.write(body)
|
outfile.write(body)
|
||||||
commands = [
|
if not os.path.exists(filestem + ".dvi"):
|
||||||
"latex",
|
commands = [
|
||||||
"-interaction=batchmode",
|
"latex",
|
||||||
"-output-directory=" + TEX_DIR,
|
"-interaction=batchmode",
|
||||||
filestem + ".tex"
|
"-output-directory=" + TEX_DIR,
|
||||||
]
|
filestem + ".tex",
|
||||||
#TODO, Error check
|
"> /dev/null"
|
||||||
os.system(" ".join(commands))
|
]
|
||||||
assert os.path.exists(filestem + ".dvi")
|
#TODO, Error check
|
||||||
result = dvi_to_png(filestem + ".dvi")
|
os.system(" ".join(commands))
|
||||||
|
result = dvi_to_png(filestem + ".dvi")
|
||||||
return result if return_list else result[0]
|
return result if return_list else result[0]
|
||||||
|
|
||||||
|
def tex_expression_list_as_string(expression):
|
||||||
|
return "\n".join([
|
||||||
|
"\onslide<%d>{"%count + exp + "}"
|
||||||
|
for count, exp in zip(it.count(1), expression)
|
||||||
|
])
|
||||||
|
|
||||||
def dvi_to_png(filename, regen_if_exists = False):
|
def dvi_to_png(filename, regen_if_exists = False):
|
||||||
"""
|
"""
|
||||||
|
|
46
tk_scene.py
Normal file
46
tk_scene.py
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
from scene import *
|
||||||
|
import Tkinter
|
||||||
|
from PIL import ImageTk, Image
|
||||||
|
|
||||||
|
class TkSceneRoot(Tkinter.Tk):
|
||||||
|
def __init__(self, scene):
|
||||||
|
if scene.frames == []:
|
||||||
|
raise str(scene) + " has no frames!"
|
||||||
|
self.scene = scene
|
||||||
|
Tkinter.Tk.__init__(self)
|
||||||
|
|
||||||
|
self.height, self.width = scene.shape
|
||||||
|
kwargs = {"height" : self.height, "width" : self.width}
|
||||||
|
self.frame = Tkinter.Frame(self, **kwargs)
|
||||||
|
self.frame.pack()
|
||||||
|
self.canvas = Tkinter.Canvas(self.frame, **kwargs)
|
||||||
|
self.canvas.configure(background='black')
|
||||||
|
self.canvas.place(x=-2,y=-2)
|
||||||
|
|
||||||
|
self.frame_index = 0
|
||||||
|
self.num_frames = len(self.scene.frames)
|
||||||
|
self.frame_duration_in_ms = int(1000*scene.frame_duration)
|
||||||
|
# self.after(0,self.show_new_image)
|
||||||
|
while(1):
|
||||||
|
self.frame_index = 0
|
||||||
|
self.show_new_image()
|
||||||
|
self.mainloop()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def show_new_image(self):
|
||||||
|
self.frame_index += 1
|
||||||
|
if self.frame_index >= self.num_frames:
|
||||||
|
return
|
||||||
|
frame = self.scene.frames[self.frame_index]
|
||||||
|
|
||||||
|
image = Image.fromarray(frame).convert('RGB')
|
||||||
|
image.resize(self.frame.size())
|
||||||
|
photo = ImageTk.PhotoImage(image)
|
||||||
|
self.canvas.delete(Tkinter.ALL)
|
||||||
|
self.canvas.create_image(
|
||||||
|
0, 0,
|
||||||
|
image = photo, anchor = Tkinter.NW
|
||||||
|
)
|
||||||
|
self.after(self.frame_duration_in_ms, self.show_new_image)
|
||||||
|
self.update()
|
Loading…
Add table
Reference in a new issue