Merge pull request #3 from 3b1b/shaders

Update
This commit is contained in:
鹤翔万里 2021-01-16 08:46:48 +08:00 committed by GitHub
commit 83a9217063
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 273 additions and 231 deletions

View file

@ -27,6 +27,7 @@ tex:
# intermediate_filetype: "xdv" # intermediate_filetype: "xdv"
universal_import_line: "from manimlib.imports import *" universal_import_line: "from manimlib.imports import *"
style: style:
font: "Consolas"
background_color: "#333333" background_color: "#333333"
camera_qualities: camera_qualities:
low: low:

View file

@ -310,22 +310,33 @@ class SurfaceExample(Scene):
} }
def construct(self): def construct(self):
surface_text = Text("For 3d scenes, try using surfaces")
surface_text.fix_in_frame()
surface_text.to_edge(UP)
self.add(surface_text)
self.wait(0.1)
torus1 = Torus(r1=1, r2=1) torus1 = Torus(r1=1, r2=1)
torus2 = Torus(r1=3, r2=1) torus2 = Torus(r1=3, r2=1)
sphere = Sphere(radius=3, resolution=torus1.resolution) sphere = Sphere(radius=3, resolution=torus1.resolution)
# You can texture a surface with up to two images, which will # You can texture a surface with up to two images, which will
# be interpreted as the side towards the light, and away from # be interpreted as the side towards the light, and away from
# the light. These can be either urls, or paths to a local file # the light. These can be either urls, or paths to a local file
# in whatever you've set as the iamge directory in # in whatever you've set as the image directory in
# the custom_defaults.yml file # the custom_defaults.yml file
# day_texture = "EarthTextureMap"
# night_texture = "NightEarthTextureMap"
day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg" day_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4d/Whole_world_-_land_and_oceans.jpg/1280px-Whole_world_-_land_and_oceans.jpg"
night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg" night_texture = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/ba/The_earth_at_night.jpg/1280px-The_earth_at_night.jpg"
surfaces = [ surfaces = [
TexturedSurface(surface, day_texture, night_texture) TexturedSurface(surface, day_texture, night_texture)
for surface in [sphere, torus1, torus2] for surface in [sphere, torus1, torus2]
] ]
for mob in surfaces: for mob in surfaces:
mob.shift(IN)
mob.mesh = SurfaceMesh(mob) mob.mesh = SurfaceMesh(mob)
mob.mesh.set_stroke(BLUE, 1, opacity=0.5) mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
@ -365,12 +376,23 @@ class SurfaceExample(Scene):
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt)) frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
# Play around with where the light is # Play around with where the light is
light_text = Text("You can move around the light source")
light_text.move_to(surface_text)
light_text.fix_in_frame()
self.play(FadeTransform(surface_text, light_text))
light = self.camera.light_source light = self.camera.light_source
self.add(light) self.add(light)
light.save_state() light.save_state()
self.play(light.move_to, 3 * IN, run_time=5) self.play(light.move_to, 3 * IN, run_time=5)
self.play(light.shift, 10 * OUT, run_time=5) self.play(light.shift, 10 * OUT, run_time=5)
self.wait(4)
drag_text = Text("Try clicking and dragging while pressing d")
drag_text.move_to(light_text)
drag_text.fix_in_frame()
self.play(FadeTransform(light_text, drag_text))
self.wait()
# See https://github.com/3b1b/videos for many, many more # See https://github.com/3b1b/videos for many, many more

View file

@ -95,7 +95,7 @@ class CameraFrame(Mobject):
return self.set_rotation(phi=phi) return self.set_rotation(phi=phi)
def set_gamma(self, gamma): def set_gamma(self, gamma):
return self.set_rotation(phi=phi) return self.set_rotation(gamma=gamma)
def increment_theta(self, dtheta): def increment_theta(self, dtheta):
return self.set_rotation(theta=self.euler_angles[0] + dtheta) return self.set_rotation(theta=self.euler_angles[0] + dtheta)

View file

@ -77,6 +77,7 @@ from manimlib.scene.vector_space_scene import *
from manimlib.utils.bezier import * from manimlib.utils.bezier import *
from manimlib.utils.color import * from manimlib.utils.color import *
from manimlib.utils.config_ops import * from manimlib.utils.config_ops import *
from manimlib.utils.customization import *
from manimlib.utils.debug import * from manimlib.utils.debug import *
from manimlib.utils.directories import * from manimlib.utils.directories import *
from manimlib.utils.images import * from manimlib.utils.images import *

View file

@ -1,7 +1,5 @@
from functools import reduce
import copy import copy
import itertools as it import itertools as it
import operator as op
import random import random
import sys import sys
import moderngl import moderngl
@ -31,9 +29,6 @@ from manimlib.shader_wrapper import ShaderWrapper
from manimlib.shader_wrapper import get_colormap_code from manimlib.shader_wrapper import get_colormap_code
# TODO: Explain array_attrs
# TODO: Incorporate shader defaults
class Mobject(object): class Mobject(object):
""" """
Mathematical Object Mathematical Object
@ -146,9 +141,12 @@ class Mobject(object):
about_point = self.get_bounding_box_point(about_edge) about_point = self.get_bounding_box_point(about_edge)
for mob in self.get_family(): for mob in self.get_family():
arrs = [mob.get_points()] arrs = []
if mob.has_points():
arrs.append(mob.get_points())
if works_on_bounding_box: if works_on_bounding_box:
arrs.append(mob.get_bounding_box()) arrs.append(mob.get_bounding_box())
for arr in arrs: for arr in arrs:
if about_point is None: if about_point is None:
arr[:] = func(arr) arr[:] = func(arr)
@ -298,6 +296,84 @@ class Mobject(object):
self.set_submobjects(list_update(self.submobjects, mobject_attrs)) self.set_submobjects(list_update(self.submobjects, mobject_attrs))
return self return self
# Submobject organization
def arrange(self, direction=RIGHT, center=True, **kwargs):
for m1, m2 in zip(self.submobjects, self.submobjects[1:]):
m2.next_to(m1, direction, **kwargs)
if center:
self.center()
return self
def arrange_in_grid(self, n_rows=None, n_cols=None,
buff=None,
h_buff=None,
v_buff=None,
buff_ratio=None,
h_buff_ratio=0.5,
v_buff_ratio=0.5,
aligned_edge=ORIGIN,
fill_rows_first=True):
submobs = self.submobjects
if n_rows is None and n_cols is None:
n_rows = int(np.sqrt(len(submobs)))
if n_rows is None:
n_rows = len(submobs) // n_cols
if n_cols is None:
n_cols = len(submobs) // n_rows
if buff is not None:
h_buff = buff
v_buff = buff
else:
if buff_ratio is not None:
v_buff_ratio = buff_ratio
h_buff_ratio = buff_ratio
if h_buff is None:
h_buff = h_buff_ratio * self[0].get_width()
if v_buff is None:
v_buff = v_buff_ratio * self[0].get_height()
x_unit = h_buff + max([sm.get_width() for sm in submobs])
y_unit = v_buff + max([sm.get_height() for sm in submobs])
for index, sm in enumerate(submobs):
if fill_rows_first:
x, y = index % n_cols, index // n_cols
else:
x, y = index // n_rows, index % n_rows
sm.move_to(ORIGIN, aligned_edge)
sm.shift(x * x_unit * RIGHT + y * y_unit * DOWN)
self.center()
return self
def get_grid(self, n_rows, n_cols, height=None, **kwargs):
"""
Returns a new mobject containing multiple copies of this one
arranged in a grid
"""
grid = self.get_group_class()(
*(self.copy() for n in range(n_rows * n_cols))
)
grid.arrange_in_grid(n_rows, n_cols, **kwargs)
if height is not None:
grid.set_height(height)
return grid
def sort(self, point_to_num_func=lambda p: p[0], submob_func=None):
if submob_func is not None:
self.submobjects.sort(key=submob_func)
else:
self.submobjects.sort(key=lambda m: point_to_num_func(m.get_center()))
return self
def shuffle(self, recurse=False):
if recurse:
for submob in self.submobjects:
submob.shuffle(recurse=True)
random.shuffle(self.submobjects)
return self
# Copying # Copying
def copy(self): def copy(self):
@ -373,16 +449,16 @@ class Mobject(object):
self.has_updaters = False self.has_updaters = False
self.updating_suspended = False self.updating_suspended = False
def update(self, dt=0, recursive=True): def update(self, dt=0, recurse=True):
if not self.has_updaters or self.updating_suspended: if not self.has_updaters or self.updating_suspended:
return self return self
for updater in self.time_based_updaters: for updater in self.time_based_updaters:
updater(self, dt) updater(self, dt)
for updater in self.non_time_updaters: for updater in self.non_time_updaters:
updater(self) updater(self)
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.update(dt, recursive) submob.update(dt, recurse)
return self return self
def get_time_based_updaters(self): def get_time_based_updaters(self):
@ -419,13 +495,13 @@ class Mobject(object):
updater_list.remove(update_function) updater_list.remove(update_function)
return self return self
def clear_updaters(self, recursive=True): def clear_updaters(self, recurse=True):
self.time_based_updaters = [] self.time_based_updaters = []
self.non_time_updaters = [] self.non_time_updaters = []
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.clear_updaters() submob.clear_updaters()
self.suspend_updating(recursive) self.suspend_updating(recurse)
return self return self
def match_updaters(self, mobject): def match_updaters(self, mobject):
@ -434,22 +510,22 @@ class Mobject(object):
self.add_updater(updater) self.add_updater(updater)
return self return self
def suspend_updating(self, recursive=True): def suspend_updating(self, recurse=True):
self.updating_suspended = True self.updating_suspended = True
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.suspend_updating(recursive) submob.suspend_updating(recurse)
return self return self
def resume_updating(self, recursive=True, call_updater=True): def resume_updating(self, recurse=True, call_updater=True):
self.updating_suspended = False self.updating_suspended = False
if recursive: if recurse:
for submob in self.submobjects: for submob in self.submobjects:
submob.resume_updating(recursive) submob.resume_updating(recurse)
for parent in self.parents: for parent in self.parents:
parent.resume_updating(recursive=False, call_updater=False) parent.resume_updating(recurse=False, call_updater=False)
if call_updater: if call_updater:
self.update(dt=0, recursive=recursive) self.update(dt=0, recurse=recurse)
return self return self
def refresh_has_updater_status(self): def refresh_has_updater_status(self):
@ -744,30 +820,6 @@ class Mobject(object):
self.shift(start - curr_start) self.shift(start - curr_start)
return self return self
# Background rectangle
def add_background_rectangle(self, color=BLACK, opacity=0.75, **kwargs):
# TODO, this does not behave well when the mobject has points,
# since it gets displayed on top
from manimlib.mobject.shape_matchers import BackgroundRectangle
self.background_rectangle = BackgroundRectangle(
self, color=color,
fill_opacity=opacity,
**kwargs
)
self.add_to_back(self.background_rectangle)
return self
def add_background_rectangle_to_submobjects(self, **kwargs):
for submobject in self.submobjects:
submobject.add_background_rectangle(**kwargs)
return self
def add_background_rectangle_to_family_members_with_points(self, **kwargs):
for mob in self.family_members_with_points():
mob.add_background_rectangle(**kwargs)
return self
# Color functions # Color functions
def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True): def set_rgba_array(self, color=None, opacity=None, name="rgbas", recurse=True):
@ -857,6 +909,30 @@ class Mobject(object):
mob.uniforms["shadow"] = shadow mob.uniforms["shadow"] = shadow
return self return self
# Background rectangle
def add_background_rectangle(self, color=None, opacity=0.75, **kwargs):
# TODO, this does not behave well when the mobject has points,
# since it gets displayed on top
from manimlib.mobject.shape_matchers import BackgroundRectangle
self.background_rectangle = BackgroundRectangle(
self, color=color,
fill_opacity=opacity,
**kwargs
)
self.add_to_back(self.background_rectangle)
return self
def add_background_rectangle_to_submobjects(self, **kwargs):
for submobject in self.submobjects:
submobject.add_background_rectangle(**kwargs)
return self
def add_background_rectangle_to_family_members_with_points(self, **kwargs):
for mob in self.family_members_with_points():
mob.add_background_rectangle(**kwargs)
return self
# Getters # Getters
def get_bounding_box_point(self, direction): def get_bounding_box_point(self, direction):
@ -1037,84 +1113,6 @@ class Mobject(object):
def get_group_class(self): def get_group_class(self):
return Group return Group
# Submobject organization
def arrange(self, direction=RIGHT, center=True, **kwargs):
for m1, m2 in zip(self.submobjects, self.submobjects[1:]):
m2.next_to(m1, direction, **kwargs)
if center:
self.center()
return self
def arrange_in_grid(self, n_rows=None, n_cols=None,
buff=None,
h_buff=None,
v_buff=None,
buff_ratio=None,
h_buff_ratio=0.5,
v_buff_ratio=0.5,
aligned_edge=ORIGIN,
fill_rows_first=True):
submobs = self.submobjects
if n_rows is None and n_cols is None:
n_rows = int(np.sqrt(len(submobs)))
if n_rows is None:
n_rows = len(submobs) // n_cols
if n_cols is None:
n_cols = len(submobs) // n_rows
if buff is not None:
h_buff = buff
v_buff = buff
else:
if buff_ratio is not None:
v_buff_ratio = buff_ratio
h_buff_ratio = buff_ratio
if h_buff is None:
h_buff = h_buff_ratio * self[0].get_width()
if v_buff is None:
v_buff = v_buff_ratio * self[0].get_height()
x_unit = h_buff + max([sm.get_width() for sm in submobs])
y_unit = v_buff + max([sm.get_height() for sm in submobs])
for index, sm in enumerate(submobs):
if fill_rows_first:
x, y = index % n_cols, index // n_cols
else:
x, y = index // n_rows, index % n_rows
sm.move_to(ORIGIN, aligned_edge)
sm.shift(x * x_unit * RIGHT + y * y_unit * DOWN)
self.center()
return self
def get_grid(self, n_rows, n_cols, height=None, **kwargs):
"""
Returns a new mobject containing multiple copies of this one
arranged in a grid
"""
grid = self.get_group_class()(
*(self.copy() for n in range(n_rows * n_cols))
)
grid.arrange_in_grid(n_rows, n_cols, **kwargs)
if height is not None:
grid.set_height(height)
return grid
def sort(self, point_to_num_func=lambda p: p[0], submob_func=None):
if submob_func is not None:
self.submobjects.sort(key=submob_func)
else:
self.submobjects.sort(key=lambda m: point_to_num_func(m.get_center()))
return self
def shuffle(self, recurse=False):
if recurse:
for submob in self.submobjects:
submob.shuffle(recurse=True)
random.shuffle(self.submobjects)
return self
# Alignment # Alignment
def align_data_and_family(self, mobject): def align_data_and_family(self, mobject):

View file

@ -4,6 +4,7 @@ from manimlib.mobject.geometry import Rectangle
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.color import Color from manimlib.utils.color import Color
from manimlib.utils.customization import get_customization
from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import digest_config
@ -23,15 +24,16 @@ class SurroundingRectangle(Rectangle):
class BackgroundRectangle(SurroundingRectangle): class BackgroundRectangle(SurroundingRectangle):
CONFIG = { CONFIG = {
"color": BLACK,
"stroke_width": 0, "stroke_width": 0,
"stroke_opacity": 0, "stroke_opacity": 0,
"fill_opacity": 0.75, "fill_opacity": 0.75,
"buff": 0 "buff": 0
} }
def __init__(self, mobject, **kwargs): def __init__(self, mobject, color=None, **kwargs):
SurroundingRectangle.__init__(self, mobject, **kwargs) if color is None:
color = get_customization()['style']['background_color']
SurroundingRectangle.__init__(self, mobject, color=color, **kwargs)
self.original_fill_opacity = self.fill_opacity self.original_fill_opacity = self.fill_opacity
def pointwise_become_partial(self, mobject, a, b): def pointwise_become_partial(self, mobject, a, b):

View file

@ -6,6 +6,7 @@ import cairo
from manimlib.constants import * from manimlib.constants import *
from manimlib.mobject.svg.svg_mobject import SVGMobject from manimlib.mobject.svg.svg_mobject import SVGMobject
from manimlib.utils.config_ops import digest_config from manimlib.utils.config_ops import digest_config
from manimlib.utils.customization import get_customization
from manimlib.utils.directories import get_text_dir from manimlib.utils.directories import get_text_dir
@ -135,7 +136,7 @@ class Text(SVGMobject):
settings = self.font + self.slant + self.weight settings = self.font + self.slant + self.weight
settings += str(self.t2f) + str(self.t2s) + str(self.t2w) settings += str(self.t2f) + str(self.t2s) + str(self.t2w)
settings += str(self.lsh) + str(self.size) settings += str(self.lsh) + str(self.size)
id_str = self.text+settings id_str = self.text + settings
hasher = hashlib.sha256() hasher = hashlib.sha256()
hasher.update(id_str.encode()) hasher.update(id_str.encode())
return hasher.hexdigest()[:16] return hasher.hexdigest()[:16]
@ -192,11 +193,11 @@ class Text(SVGMobject):
lsh = self.lsh * 10 lsh = self.lsh * 10
if self.font == '': if self.font == '':
print(NOT_SETTING_FONT_MSG) self.font = get_customization()['style']['font']
dir_name = get_text_dir() dir_name = get_text_dir()
hash_name = self.text2hash() hash_name = self.text2hash()
file_name = os.path.join(dir_name, hash_name) +'.svg' file_name = os.path.join(dir_name, hash_name) + '.svg'
if os.path.exists(file_name): if os.path.exists(file_name):
return file_name return file_name

View file

@ -90,13 +90,6 @@ class VMobject(Mobject):
"unit_normal": np.zeros((1, 3)) "unit_normal": np.zeros((1, 3))
}) })
def set_points(self, points):
old_points = self.get_points()
super().set_points(points)
if not np.all(points == old_points):
self.refresh_triangulation()
return self
# Colors # Colors
def init_colors(self): def init_colors(self):
self.set_fill( self.set_fill(
@ -458,19 +451,6 @@ class VMobject(Mobject):
self.resize_data(len(self.get_points() - 1)) self.resize_data(len(self.get_points() - 1))
self.append_points(new_points) self.append_points(new_points)
# TODO, how to be smart about tangents here?
def apply_function(self, function):
Mobject.apply_function(self, function)
if self.make_smooth_after_applying_functions:
self.make_smooth()
self.refresh_triangulation()
return self
def flip(self, *args, **kwargs):
super().flip(*args, **kwargs)
self.refresh_unit_normal()
self.refresh_triangulation()
# #
def consider_points_equals(self, p0, p1): def consider_points_equals(self, p0, p1):
return get_norm(p1 - p0) < self.tolerance_for_point_equality return get_norm(p1 - p0) < self.tolerance_for_point_equality
@ -761,6 +741,98 @@ class VMobject(Mobject):
vmob.pointwise_become_partial(self, a, b) vmob.pointwise_become_partial(self, a, b)
return vmob return vmob
# Related to triangulation
def refresh_triangulation(self):
for mob in self.get_family():
mob.needs_new_triangulation = True
return self
def get_triangulation(self, normal_vector=None):
# Figure out how to triangulate the interior to know
# how to send the points as to the vertex shader.
# First triangles come directly from the points
if normal_vector is None:
normal_vector = self.get_unit_normal()
if not self.needs_new_triangulation:
return self.triangulation
points = self.get_points()
if len(points) <= 1:
self.triangulation = np.zeros(0, dtype='i4')
self.needs_new_triangulation = False
return self.triangulation
# Rotate points such that unit normal vector is OUT
# TODO, 99% of the time this does nothing. Do a check for that?
points = np.dot(points, z_to_vector(normal_vector))
indices = np.arange(len(points), dtype=int)
b0s = points[0::3]
b1s = points[1::3]
b2s = points[2::3]
v01s = b1s - b0s
v12s = b2s - b1s
crosses = cross2d(v01s, v12s)
convexities = np.sign(crosses)
atol = self.tolerance_for_point_equality
end_of_loop = np.zeros(len(b0s), dtype=bool)
end_of_loop[:-1] = (np.abs(b2s[:-1] - b0s[1:]) > atol).any(1)
end_of_loop[-1] = True
concave_parts = convexities < 0
# These are the vertices to which we'll apply a polygon triangulation
inner_vert_indices = np.hstack([
indices[0::3],
indices[1::3][concave_parts],
indices[2::3][end_of_loop],
])
inner_vert_indices.sort()
rings = np.arange(1, len(inner_vert_indices) + 1)[inner_vert_indices % 3 == 2]
# Triangulate
inner_verts = points[inner_vert_indices]
inner_tri_indices = inner_vert_indices[earclip_triangulation(inner_verts, rings)]
tri_indices = np.hstack([indices, inner_tri_indices])
self.triangulation = tri_indices
self.needs_new_triangulation = False
return tri_indices
def triggers_refreshed_triangulation(func):
def wrapper(self, *args, **kwargs):
old_points = self.get_points()
func(self, *args, **kwargs)
if not np.all(self.get_points() == old_points):
self.refresh_triangulation()
self.refresh_unit_normal()
return wrapper
@triggers_refreshed_triangulation
def set_points(self, points):
super().set_points(points)
@triggers_refreshed_triangulation
def set_data(self, data):
super().set_data(data)
# TODO, how to be smart about tangents here?
@triggers_refreshed_triangulation
def apply_function(self, function):
super().apply_function(function)
if self.make_smooth_after_applying_functions:
self.make_smooth()
return self
@triggers_refreshed_triangulation
def flip(self, *args, **kwargs):
super().flip(*args, **kwargs)
# For shaders # For shaders
def init_shader_data(self): def init_shader_data(self):
self.fill_data = np.zeros(0, dtype=self.fill_dtype) self.fill_data = np.zeros(0, dtype=self.fill_dtype)
@ -849,67 +921,6 @@ class VMobject(Mobject):
return self.stroke_data return self.stroke_data
def refresh_triangulation(self):
for mob in self.get_family():
mob.needs_new_triangulation = True
return self
def get_triangulation(self, normal_vector=None):
# Figure out how to triangulate the interior to know
# how to send the points as to the vertex shader.
# First triangles come directly from the points
if normal_vector is None:
normal_vector = self.get_unit_normal()
if not self.needs_new_triangulation:
return self.triangulation
points = self.get_points()
if len(points) <= 1:
self.triangulation = np.zeros(0, dtype='i4')
self.needs_new_triangulation = False
return self.triangulation
# Rotate points such that unit normal vector is OUT
# TODO, 99% of the time this does nothing. Do a check for that?
points = np.dot(points, z_to_vector(normal_vector))
indices = np.arange(len(points), dtype=int)
b0s = points[0::3]
b1s = points[1::3]
b2s = points[2::3]
v01s = b1s - b0s
v12s = b2s - b1s
crosses = cross2d(v01s, v12s)
convexities = np.sign(crosses)
atol = self.tolerance_for_point_equality
end_of_loop = np.zeros(len(b0s), dtype=bool)
end_of_loop[:-1] = (np.abs(b2s[:-1] - b0s[1:]) > atol).any(1)
end_of_loop[-1] = True
concave_parts = convexities < 0
# These are the vertices to which we'll apply a polygon triangulation
inner_vert_indices = np.hstack([
indices[0::3],
indices[1::3][concave_parts],
indices[2::3][end_of_loop],
])
inner_vert_indices.sort()
rings = np.arange(1, len(inner_vert_indices) + 1)[inner_vert_indices % 3 == 2]
# Triangulate
inner_verts = points[inner_vert_indices]
inner_tri_indices = inner_vert_indices[earclip_triangulation(inner_verts, rings)]
tri_indices = np.hstack([indices, inner_tri_indices])
self.triangulation = tri_indices
self.needs_new_triangulation = False
return tri_indices
def get_fill_shader_data(self): def get_fill_shader_data(self):
points = self.get_points() points = self.get_points()
if len(self.fill_data) != len(points): if len(self.fill_data) != len(points):

View file

@ -0,0 +1,23 @@
import os
import tempfile
from manimlib.config import get_custom_defaults
from manimlib.config import get_manim_dir
CUSTOMIZATION = {}
def get_customization():
if not CUSTOMIZATION:
CUSTOMIZATION.update(get_custom_defaults())
directories = CUSTOMIZATION["directories"]
# Unless user has specified otherwise, use the system default temp
# directory for storing tex files, mobject_data, etc.
if not directories["temporary_storage"]:
directories["temporary_storage"] = tempfile.gettempdir()
# Assumes all shaders are written into manimlib/shaders
directories["shaders"] = os.path.join(
get_manim_dir(), "manimlib", "shaders"
)
return CUSTOMIZATION

View file

@ -1,28 +1,11 @@
import os import os
import tempfile
from manimlib.utils.file_ops import guarantee_existence from manimlib.utils.file_ops import guarantee_existence
from manimlib.config import get_custom_defaults from manimlib.utils.customization import get_customization
from manimlib.config import get_manim_dir
PRE_COMPUTED_DIRS = {}
def get_directories(): def get_directories():
if not PRE_COMPUTED_DIRS: return get_customization()["directories"]
custom_defaults = get_custom_defaults()
PRE_COMPUTED_DIRS.update(custom_defaults["directories"])
# Unless user has specified otherwise, use the system default temp
# directory for storing tex files, mobject_data, etc.
if not PRE_COMPUTED_DIRS["temporary_storage"]:
PRE_COMPUTED_DIRS["temporary_storage"] = tempfile.gettempdir()
# Assumes all shaders are written into manimlib/shaders
PRE_COMPUTED_DIRS["shaders"] = os.path.join(
get_manim_dir(), "manimlib", "shaders"
)
return PRE_COMPUTED_DIRS
def get_temp_dir(): def get_temp_dir():