TexMobjects are looking pretty good

This commit is contained in:
Grant Sanderson 2016-04-17 12:59:53 -07:00
parent 0d4e928b6e
commit f5e4c3b334
7 changed files with 119 additions and 62 deletions

View file

@ -15,14 +15,9 @@ class Transform(Animation):
"path_func" : straight_path
}
def __init__(self, mobject, ending_mobject, **kwargs):
mobject =mobject
#Copy ending_mobject so as to not mess with caller
ending_mobject = ending_mobject.copy()
digest_config(self, kwargs, locals())
count1, count2 = mobject.get_num_points(), ending_mobject.get_num_points()
if count2 == 0:
ending_mobject = mobject.get_point_mobject()
count2 = ending_mobject.get_num_points()
mobject.align_data(ending_mobject)
Animation.__init__(self, mobject, **kwargs)

View file

@ -1,11 +1,9 @@
import numpy as np
import itertools as it
import os
import sys
from PIL import Image
import cv2
from colour import Color
import progressbar
import aggdraw
from helpers import *
@ -71,15 +69,22 @@ class Camera(object):
mob.nonempty_family_members()
for mob in mobjects
])
vmobjects = []
for mobject in mobjects:
if isinstance(mobject, VMobject):
self.display_vectorized(mobject)
vmobjects.append(mobject)
elif isinstance(mobject, PMobject):
self.display_point_cloud(
mobject.points, mobject.rgbs,
self.adjusted_thickness(mobject.stroke_width)
)
#TODO, more? Call out if it's unknown?
image = Image.fromarray(self.pixel_array, mode = "RGB")
canvas = aggdraw.Draw(image)
for vmobject in vmobjects:
self.display_vectorized(vmobject, canvas)
canvas.flush()
self.pixel_array[:,:] = np.array(image)
def display_region(self, region):
@ -95,19 +100,16 @@ class Camera(object):
self.pixel_array[covered] = rgb
def display_vectorized(self, vmobject):
def display_vectorized(self, vmobject, canvas):
if vmobject.is_subpath:
#Subpath vectorized mobjects are taken care
#of by their parent
return
im = Image.fromarray(self.pixel_array, mode = "RGB")
canvas = aggdraw.Draw(im)
pen, fill = self.get_pen_and_fill(vmobject)
pathstring = self.get_pathstring(vmobject)
symbol = aggdraw.Symbol(pathstring)
canvas.symbol((0, 0), symbol, pen, fill)
canvas.flush()
self.pixel_array[:,:] = np.array(im)
def get_pen_and_fill(self, vmobject):
pen = aggdraw.Pen(
@ -122,7 +124,7 @@ class Camera(object):
def get_pathstring(self, vmobject):
result = ""
for mob in [vmobject]+vmobject.subpath_mobjects:
for mob in [vmobject]+vmobject.get_subpath_mobjects():
points = mob.points
if len(points) == 0:
continue
@ -166,12 +168,11 @@ class Camera(object):
indices = np.dot(pixel_coords, flattener)[:,0]
indices = indices.astype('int')
# new_array = np.zeros((pw*ph, 3), dtype = 'uint8')
# new_array[indices, :] = rgbs
new_pa = self.pixel_array.reshape((ph*pw, 3))
new_pa[indices] = rgbs
self.pixel_array = new_pa.reshape((ph, pw, 3))
def align_points_to_camera(self, points):
## This is where projection should live
return points - self.space_center

View file

@ -452,16 +452,24 @@ class Mobject(object):
def push_self_into_submobjects(self):
copy = self.copy()
copy.submobjects = []
self.points = np.zeros((0, self.dim))
self.init_points()
self.add(copy)
return self
def add_n_more_submobjects(self, n):
if n > 0 and len(self.submobjects) == 0:
curr = len(self.submobjects)
if n > 0 and curr == 0:
self.add(self.copy())
n = n-1
for i in range(n):
self.add(self.submobjects[i].copy())
n -= 1
curr += 1
indices = curr*np.arange(curr+n)/(curr+n)
new_submobjects = []
for index in indices:
submob = self.submobjects[index]
if submob in new_submobjects:
submob = submob.copy()
new_submobjects.append(submob)
self.submobjects = new_submobjects
return self
def interpolate(self, mobject1, mobject2, alpha, path_func):

View file

@ -5,7 +5,7 @@ from vectorized_mobject import VMobject
from topics.geometry import Rectangle, Circle
from helpers import *
SVG_SCALE_VALUE = 0.1
SVG_SCALE_VALUE = 0.05
class SVGMobject(VMobject):
CONFIG = {
@ -150,8 +150,7 @@ class VMobjectFromSVGPathstring(VMobject):
new_points = self.string_to_points(coord_string)
if command == "M": #moveto
if len(points) > 0:
self.add_subpath(new_points)
self.growing_path = self.subpath_mobjects[-1]
self.growing_path = self.add_subpath(new_points)
else:
self.growing_path.start_at(new_points[0])
return

View file

@ -1,3 +1,4 @@
from vectorized_mobject import VMobject
from svg_mobject import SVGMobject
from helpers import *
@ -5,16 +6,44 @@ class TexMobject(SVGMobject):
CONFIG = {
"template_tex_file" : TEMPLATE_TEX_FILE,
"color" : WHITE,
"stroke_width" : 1,
"stroke_width" : 0,
"should_center" : True,
"next_to_direction" : RIGHT,
"next_to_buff" : 0.2,
}
def __init__(self, expression, **kwargs):
digest_config(self, kwargs, locals())
image_file = tex_to_image_file(
self.expression,
self.template_tex_file
)
SVGMobject.__init__(self, image_file, **kwargs)
VMobject.__init__(self, **kwargs)
if self.should_center:
self.center()
def generate_points(self):
if isinstance(self.expression, list):
self.handle_list_expression()
else:
self.svg_file = tex_to_svg_file(
"".join(self.expression),
self.template_tex_file
)
SVGMobject.generate_points(self)
self.init_colors()
def handle_list_expression(self):
#TODO, next_to not sufficient?
subs = [
TexMobject(expr)
for expr in self.expression
]
for sm1, sm2 in zip(subs, subs[1:]):
sm2.next_to(
sm1,
self.next_to_direction,
self.next_to_buff
)
self.submobjects = subs
return self
class TextMobject(TexMobject):
@ -42,13 +71,13 @@ class Brace(TexMobject):
def tex_hash(expression):
return str(hash(expression))
def tex_to_image_file(expression, template_tex_file):
def tex_to_svg_file(expression, template_tex_file):
image_dir = os.path.join(TEX_IMAGE_DIR, tex_hash(expression))
if os.path.exists(image_dir):
return get_sorted_image_list(image_dir)
tex_file = generate_tex_file(expression, template_tex_file)
pdf_file = tex_to_pdf(tex_file)
return pdf_to_svg(pdf_file)
dvi_file = tex_to_dvi(tex_file)
return dvi_to_svg(dvi_file)
def generate_tex_file(expression, template_tex_file):
@ -64,11 +93,11 @@ def generate_tex_file(expression, template_tex_file):
outfile.write(body)
return result
def tex_to_pdf(tex_file):
result = tex_file.replace(".tex", ".pdf")
if not os.path.exists(result) or True:
def tex_to_dvi(tex_file):
result = tex_file.replace(".tex", ".dvi")
if not os.path.exists(result):
commands = [
"pdflatex",
"latex",
"-interaction=batchmode",
"-output-directory=" + TEX_DIR,
tex_file,
@ -78,26 +107,30 @@ def tex_to_pdf(tex_file):
os.system(" ".join(commands))
return result
def pdf_to_svg(pdf_file, regen_if_exists = False):
def dvi_to_svg(dvi_file, regen_if_exists = False):
"""
Converts a dvi, which potentially has multiple slides, into a
directory full of enumerated pngs corresponding with these slides.
Returns a list of PIL Image objects for these images sorted as they
where in the dvi
"""
result = pdf_file.replace(".pdf", ".svg")
if not os.path.exists(result) or True:
result = dvi_file.replace(".dvi", ".svg")
if not os.path.exists(result):
commands = [
"pdf2svg",
pdf_file,
"dvisvgm",
dvi_file,
"-n",
"-v",
"0",
"-o",
result,
# "> /dev/null"
"> /dev/null"
]
os.system(" ".join(commands))
return result
# directory, filename = os.path.split(pdf_file)
# directory, filename = os.path.split(dvi_file)
# name = filename.replace(".dvi", "")
# images_dir = os.path.join(TEX_IMAGE_DIR, name)
# if not os.path.exists(images_dir):
@ -107,7 +140,7 @@ def pdf_to_svg(pdf_file, regen_if_exists = False):
# "convert",
# "-density",
# str(PDF_DENSITY),
# pdf_file,
# dvi_file,
# "-size",
# str(DEFAULT_WIDTH) + "x" + str(DEFAULT_HEIGHT),
# os.path.join(images_dir, name + ".png")

View file

@ -15,7 +15,6 @@ class VMobject(Mobject):
"mark_paths_closed" : False,
}
def __init__(self, *args, **kwargs):
self.subpath_mobjects = []
Mobject.__init__(self, *args, **kwargs)
## Colors
@ -53,7 +52,10 @@ class VMobject(Mobject):
return self.fill_opacity
def get_stroke_color(self):
return Color(rgb = self.stroke_rgb)
try:
return Color(rgb = self.stroke_rgb)
except:
return Color(rgb = 0.99*self.stroke_rgb)
#TODO, get color? Specify if stroke or fill
#is the predominant color attribute?
@ -148,9 +150,14 @@ class VMobject(Mobject):
is_subpath = True
)
subpath_mobject.set_points(points)
self.subpath_mobjects.append(subpath_mobject)
self.add(subpath_mobject)
return self
return subpath_mobject
def get_subpath_mobjects(self):
return filter(
lambda m : m.is_subpath,
self.submobjects
)
## Information about line
@ -181,28 +188,40 @@ class VMobject(Mobject):
def align_points_with_larger(self, larger_mobject):
assert(isinstance(larger_mobject, VMobject))
num_anchors = self.get_num_anchor_points()
if num_anchors <= 1:
point = self.points[0] if len(self.points) else np.zeros(3)
self.points = np.zeros(larger_mobject.points.shape)
self.points[:,:] = point
self.insert_n_anchor_points(
larger_mobject.get_num_anchor_points()-\
self.get_num_anchor_points()
)
is_subpath = self.is_subpath or larger_mobject.is_subpath
self.is_subpath = larger_mobject.is_subpath = is_subpath
mark_closed = self.mark_paths_closed and larger_mobject.mark_paths_closed
self.mark_paths_closed = larger_mobject.mark_paths_closed = mark_closed
return self
def insert_n_anchor_points(self, n):
curr = self.get_num_anchor_points()
if curr == 0:
self.points = np.zeros((1, 3))
n = n-1
if curr == 1:
self.points = np.repeat(self.points, n+1)
return self
points = np.array([self.points[0]])
target_len = larger_mobject.get_num_anchor_points()-1
num_curves = self.get_num_anchor_points()-1
num_curves = curr-1
#Curves in self are buckets, and we need to know
#how many new anchor points to put into each one.
#Each element of index_allocation is like a bucket,
#and its value tells you the appropriate index of
#the smaller curve.
index_allocation = (np.arange(target_len) * num_curves)/target_len
index_allocation = (np.arange(curr+n-1) * num_curves)/(curr+n-1)
for index in range(num_curves):
curr_bezier_points = self.points[3*index:3*index+4]
num_inter_curves = sum(index_allocation == index)
step = 1./num_inter_curves
alphas = np.arange(0, 1+step, step)
alphas = np.arange(0, num_inter_curves+1)/float(num_inter_curves)
for a, b in zip(alphas, alphas[1:]):
new_points = partial_bezier_points(curr_bezier_points, a, b)
new_points = partial_bezier_points(
curr_bezier_points, a, b
)
points = np.append(
points, new_points[1:], axis = 0
)
@ -229,7 +248,7 @@ class VMobject(Mobject):
))
def become_partial(self, mobject, a, b):
assert(isinstance(mobject, VMobject))
assert(isinstance(mobject, VMobject))
#Partial curve includes three portions:
#-A middle section, which matches the curve exactly
#-A start, which is some ending portion of an inner cubic

View file

@ -9,6 +9,7 @@
\usepackage{tipa}
\usepackage{relsize}
\mode<presentation>
{
\usetheme{default} % or try Darmstadt, Madrid, Warsaw, ...
@ -17,6 +18,7 @@
\setbeamertemplate{navigation symbols}{}
\setbeamertemplate{caption}[numbered]
}
\begin{document}
\centering