mirror of
https://github.com/3b1b/manim.git
synced 2025-11-14 14:47:44 +00:00
commit
8762177df5
9 changed files with 119 additions and 52 deletions
|
|
@ -68,10 +68,10 @@ class TransformMatchingParts(AnimationGroup):
|
|||
anims.append(FadeTransformPieces(fade_source, fade_target, **kwargs))
|
||||
else:
|
||||
anims.append(FadeOutToPoint(
|
||||
fade_source, fade_target.get_center(), **kwargs
|
||||
fade_source, target_mobject.get_center(), **kwargs
|
||||
))
|
||||
anims.append(FadeInFromPoint(
|
||||
fade_target.copy(), fade_source.get_center(), **kwargs
|
||||
fade_target.copy(), mobject.get_center(), **kwargs
|
||||
))
|
||||
|
||||
super().__init__(*anims)
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ def insert_embed_line(file_name, lineno):
|
|||
lines = fp.readlines()
|
||||
line = lines[lineno - 1]
|
||||
n_spaces = len(line) - len(line.lstrip())
|
||||
lines.insert(lineno - 1, " " * n_spaces + "self.embed()\n")
|
||||
lines.insert(lineno, " " * n_spaces + "self.embed()\n")
|
||||
|
||||
alt_file = file_name.replace(".py", "_inserted_embed.py")
|
||||
with open(alt_file, 'w') as fp:
|
||||
|
|
@ -302,9 +302,10 @@ def get_configuration(args):
|
|||
"file_writer_config": file_writer_config,
|
||||
"quiet": args.quiet or args.write_all,
|
||||
"write_all": args.write_all,
|
||||
"skip_animations": args.skip_animations,
|
||||
"start_at_animation_number": args.start_at_animation_number,
|
||||
"preview": not write_file,
|
||||
"end_at_animation_number": None,
|
||||
"preview": not write_file,
|
||||
"leave_progress_bars": args.leave_progress_bars,
|
||||
}
|
||||
|
||||
|
|
@ -334,10 +335,6 @@ def get_configuration(args):
|
|||
else:
|
||||
config["start_at_animation_number"] = int(stan)
|
||||
|
||||
config["skip_animations"] = any([
|
||||
args.skip_animations,
|
||||
args.start_at_animation_number,
|
||||
])
|
||||
return config
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -80,10 +80,12 @@ def compute_total_frames(scene_class, scene_config):
|
|||
pre_config = copy.deepcopy(scene_config)
|
||||
pre_config["file_writer_config"]["write_to_movie"] = False
|
||||
pre_config["file_writer_config"]["save_last_frame"] = True
|
||||
pre_config["file_writer_config"]["quiet"] = True
|
||||
pre_config["skip_animations"] = True
|
||||
pre_scene = scene_class(**pre_config)
|
||||
pre_scene.run()
|
||||
return int(pre_scene.time * scene_config["camera_config"]["frame_rate"])
|
||||
total_time = pre_scene.time - pre_scene.skip_time
|
||||
return int(total_time * scene_config["camera_config"]["frame_rate"])
|
||||
|
||||
|
||||
def get_scenes_to_render(scene_classes, scene_config, config):
|
||||
|
|
|
|||
|
|
@ -305,6 +305,9 @@ class Circle(Arc):
|
|||
(angle - start_angle) / TAU
|
||||
)
|
||||
|
||||
def get_radius(self):
|
||||
return get_norm(self.get_start() - self.get_center())
|
||||
|
||||
|
||||
class Dot(Circle):
|
||||
CONFIG = {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from manimlib.mobject.types.surface import SGroup
|
|||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||
from manimlib.mobject.geometry import Square
|
||||
from manimlib.mobject.geometry import Polygon
|
||||
from manimlib.mobject.geometry import Line
|
||||
from manimlib.utils.bezier import interpolate
|
||||
from manimlib.utils.config_ops import digest_config
|
||||
from manimlib.utils.space_ops import get_norm
|
||||
|
|
@ -46,10 +48,6 @@ class SurfaceMesh(VGroup):
|
|||
|
||||
for ui in u_indices:
|
||||
path = VMobject()
|
||||
# full_ui = full_nv * ui
|
||||
# path.set_points_smoothly(
|
||||
# nudged_points[full_ui:full_ui + full_nv]
|
||||
# )
|
||||
low_ui = full_nv * int(math.floor(ui))
|
||||
high_ui = full_nv * int(math.ceil(ui))
|
||||
path.set_points_smoothly(interpolate(
|
||||
|
|
@ -60,9 +58,6 @@ class SurfaceMesh(VGroup):
|
|||
self.add(path)
|
||||
for vi in v_indices:
|
||||
path = VMobject()
|
||||
# path.set_points_smoothly(
|
||||
# nudged_points[vi::full_nv]
|
||||
# )
|
||||
path.set_points_smoothly(interpolate(
|
||||
nudged_points[int(math.floor(vi))::full_nv],
|
||||
nudged_points[int(math.ceil(vi))::full_nv],
|
||||
|
|
@ -228,6 +223,53 @@ class VCube(VGroup):
|
|||
self.refresh_unit_normal()
|
||||
|
||||
|
||||
class Dodecahedron(VGroup):
|
||||
CONFIG = {
|
||||
"fill_color": BLUE_E,
|
||||
"fill_opacity": 1,
|
||||
"stroke_width": 1,
|
||||
"reflectiveness": 0.2,
|
||||
"gloss": 0.3,
|
||||
"shadow": 0.2,
|
||||
"depth_test": True,
|
||||
}
|
||||
|
||||
def init_points(self):
|
||||
# Star by creating two of the pentagons, meeting
|
||||
# back to back on the positive x-axis
|
||||
phi = (1 + math.sqrt(5)) / 2
|
||||
x, y, z = np.identity(3)
|
||||
pentagon1 = Polygon(
|
||||
[phi, 1 / phi, 0],
|
||||
[1, 1, 1],
|
||||
[1 / phi, 0, phi],
|
||||
[1, -1, 1],
|
||||
[phi, -1 / phi, 0],
|
||||
)
|
||||
pentagon2 = pentagon1.copy().stretch(-1, 2, about_point=ORIGIN)
|
||||
pentagon2.reverse_points()
|
||||
x_pair = VGroup(pentagon1, pentagon2)
|
||||
z_pair = x_pair.copy().apply_matrix(np.array([z, -x, -y]).T)
|
||||
y_pair = x_pair.copy().apply_matrix(np.array([y, z, x]).T)
|
||||
|
||||
self.add(*x_pair, *y_pair, *z_pair)
|
||||
for pentagon in list(self):
|
||||
pc = pentagon.copy()
|
||||
pc.apply_function(lambda p: -p)
|
||||
pc.reverse_points()
|
||||
self.add(pc)
|
||||
|
||||
# # Rotate those two pentagons by all the axis permuations to fill
|
||||
# # out the dodecahedron
|
||||
# Id = np.identity(3)
|
||||
# for i in range(3):
|
||||
# perm = [j % 3 for j in range(i, i + 3)]
|
||||
# for b in [1, -1]:
|
||||
# matrix = b * np.array([Id[0][perm], Id[1][perm], Id[2][perm]])
|
||||
# self.add(pentagon1.copy().apply_matrix(matrix, about_point=ORIGIN))
|
||||
# self.add(pentagon2.copy().apply_matrix(matrix, about_point=ORIGIN))
|
||||
|
||||
|
||||
class Prism(Cube):
|
||||
CONFIG = {
|
||||
"dimensions": [3, 2, 1]
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@ import numpy as np
|
|||
import moderngl
|
||||
|
||||
from manimlib.constants import GREY_C
|
||||
from manimlib.constants import YELLOW
|
||||
from manimlib.constants import ORIGIN
|
||||
from manimlib.mobject.types.point_cloud_mobject import PMobject
|
||||
from manimlib.utils.iterables import resize_preserving_order
|
||||
|
||||
|
||||
DEFAULT_DOT_RADIUS = 0.05
|
||||
DEFAULT_GLOW_DOT_RADIUS = 0.2
|
||||
DEFAULT_GRID_HEIGHT = 6
|
||||
DEFAULT_BUFF_RATIO = 0.5
|
||||
|
||||
|
|
@ -123,5 +125,13 @@ class DotCloud(PMobject):
|
|||
|
||||
|
||||
class TrueDot(DotCloud):
|
||||
def __init__(self, center=ORIGIN, radius=DEFAULT_DOT_RADIUS, **kwargs):
|
||||
super().__init__(points=[center], radius=radius, **kwargs)
|
||||
def __init__(self, center=ORIGIN, **kwargs):
|
||||
super().__init__(points=[center], **kwargs)
|
||||
|
||||
|
||||
class GlowDot(TrueDot):
|
||||
CONFIG = {
|
||||
"glow_factor": 2,
|
||||
"radius": DEFAULT_GLOW_DOT_RADIUS,
|
||||
"color": YELLOW,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -887,7 +887,7 @@ class VMobject(Mobject):
|
|||
def triggers_refreshed_triangulation(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
old_points = self.get_points()
|
||||
old_points = self.get_points().copy()
|
||||
func(self, *args, **kwargs)
|
||||
if not np.all(self.get_points() == old_points):
|
||||
self.refresh_unit_normal()
|
||||
|
|
@ -1032,8 +1032,8 @@ class VGroup(VMobject):
|
|||
raise Exception("All submobjects must be of type VMobject")
|
||||
super().__init__(**kwargs)
|
||||
self.add(*vmobjects)
|
||||
|
||||
def __add__(self:'VGroup', other : 'VMobject' or 'VGroup'):
|
||||
|
||||
def __add__(self: 'VGroup', other: 'VMobject' or 'VGroup'):
|
||||
assert(isinstance(other, VMobject))
|
||||
return self.add(other)
|
||||
|
||||
|
|
@ -1048,7 +1048,8 @@ class VectorizedPoint(Point, VMobject):
|
|||
}
|
||||
|
||||
def __init__(self, location=ORIGIN, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
Point.__init__(self, **kwargs)
|
||||
VMobject.__init__(self, **kwargs)
|
||||
self.set_points(np.array([location]))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ class Scene(object):
|
|||
self.time = 0
|
||||
self.skip_time = 0
|
||||
self.original_skipping_status = self.skip_animations
|
||||
if self.start_at_animation_number is not None:
|
||||
self.skip_animations = True
|
||||
|
||||
# Items associated with interaction
|
||||
self.mouse_point = Point()
|
||||
|
|
@ -184,6 +186,13 @@ class Scene(object):
|
|||
for mob in self.mobjects
|
||||
])
|
||||
|
||||
def has_time_based_updaters(self):
|
||||
return any([
|
||||
sm.has_time_based_updater()
|
||||
for mob in self.mobjects()
|
||||
for sm in mob.get_family()
|
||||
])
|
||||
|
||||
# Related to time
|
||||
def get_time(self):
|
||||
return self.time
|
||||
|
|
@ -271,15 +280,16 @@ class Scene(object):
|
|||
def update_skipping_status(self):
|
||||
if self.start_at_animation_number is not None:
|
||||
if self.num_plays == self.start_at_animation_number:
|
||||
self.stop_skipping()
|
||||
self.skip_time = self.time
|
||||
if not self.original_skipping_status:
|
||||
self.stop_skipping()
|
||||
if self.end_at_animation_number is not None:
|
||||
if self.num_plays >= self.end_at_animation_number:
|
||||
raise EndSceneEarlyException()
|
||||
|
||||
def stop_skipping(self):
|
||||
if self.skip_animations:
|
||||
self.skip_animations = False
|
||||
self.skip_time += self.time
|
||||
self.virtual_animation_start_time = self.time
|
||||
self.skip_animations = False
|
||||
|
||||
# Methods associated with running animations
|
||||
def get_time_progression(self, run_time, n_iterations=None, desc="", override_skip_animations=False):
|
||||
|
|
@ -469,23 +479,18 @@ class Scene(object):
|
|||
@handle_play_like_call
|
||||
def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
|
||||
self.update_mobjects(dt=0) # Any problems with this?
|
||||
if self.should_update_mobjects():
|
||||
self.lock_static_mobject_data()
|
||||
time_progression = self.get_wait_time_progression(duration, stop_condition)
|
||||
last_t = 0
|
||||
for t in time_progression:
|
||||
dt = t - last_t
|
||||
last_t = t
|
||||
self.update_frame(dt)
|
||||
self.emit_frame()
|
||||
if stop_condition is not None and stop_condition():
|
||||
time_progression.close()
|
||||
break
|
||||
self.unlock_mobject_data()
|
||||
else:
|
||||
self.update_frame(duration)
|
||||
for n in self.get_wait_time_progression(duration):
|
||||
self.emit_frame()
|
||||
self.lock_static_mobject_data()
|
||||
time_progression = self.get_wait_time_progression(duration, stop_condition)
|
||||
last_t = 0
|
||||
for t in time_progression:
|
||||
dt = t - last_t
|
||||
last_t = t
|
||||
self.update_frame(dt)
|
||||
self.emit_frame()
|
||||
if stop_condition is not None and stop_condition():
|
||||
time_progression.close()
|
||||
break
|
||||
self.unlock_mobject_data()
|
||||
return self
|
||||
|
||||
def wait_until(self, stop_condition, max_time=60):
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class SceneFileWriter(object):
|
|||
"show_file_location_upon_completion": False,
|
||||
"quiet": False,
|
||||
"total_frames": 0,
|
||||
"progress_description_len": 35,
|
||||
"progress_description_len": 60,
|
||||
}
|
||||
|
||||
def __init__(self, scene, **kwargs):
|
||||
|
|
@ -76,10 +76,14 @@ class SceneFileWriter(object):
|
|||
return path
|
||||
|
||||
def get_default_scene_name(self):
|
||||
if self.file_name is None:
|
||||
return self.scene.__class__.__name__
|
||||
else:
|
||||
return self.file_name
|
||||
name = str(self.scene)
|
||||
saan = self.scene.start_at_animation_number
|
||||
eaan = self.scene.end_at_animation_number
|
||||
if saan is not None:
|
||||
name += f"_{saan}"
|
||||
if eaan is not None:
|
||||
name += f"_{eaan}"
|
||||
return name
|
||||
|
||||
def get_resolution_directory(self):
|
||||
pixel_height = self.scene.camera.pixel_height
|
||||
|
|
@ -212,15 +216,17 @@ class SceneFileWriter(object):
|
|||
if self.total_frames > 0:
|
||||
self.progress_display = ProgressDisplay(
|
||||
range(self.total_frames),
|
||||
# bar_format="{l_bar}{bar}|{n_fmt}/{total_fmt}",
|
||||
leave=False,
|
||||
ascii=True if platform.system() == 'Windows' else None,
|
||||
desc="Full render: "
|
||||
dynamic_ncols=True,
|
||||
)
|
||||
self.has_progress_display = True
|
||||
|
||||
def set_progress_display_subdescription(self, desc):
|
||||
def set_progress_display_subdescription(self, sub_desc):
|
||||
desc_len = self.progress_description_len
|
||||
full_desc = f"Full render ({desc})"
|
||||
file = os.path.split(self.get_movie_file_path())[1]
|
||||
full_desc = f"Rendering {file} ({sub_desc})"
|
||||
if len(full_desc) > desc_len:
|
||||
full_desc = full_desc[:desc_len - 4] + "...)"
|
||||
else:
|
||||
|
|
@ -327,7 +333,8 @@ class SceneFileWriter(object):
|
|||
self.print_file_ready_message(file_path)
|
||||
|
||||
def print_file_ready_message(self, file_path):
|
||||
log.info(f"File ready at {file_path}")
|
||||
if not self.quiet:
|
||||
log.info(f"File ready at {file_path}")
|
||||
|
||||
def should_open_file(self):
|
||||
return any([
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue