Add interface to JS renderer (#465)

* Add interface to JS renderer

* Update manim_directive.py

* Add simple flag to get_style()

* Change add_frames to add_frame

* Update changelog.rst
This commit is contained in:
Devin Neal 2020-10-04 18:49:01 -07:00 committed by GitHub
commit 41f6f054e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 3273 additions and 330 deletions

View file

@ -32,6 +32,7 @@ Command line
#. Re-implement GIF export with the :code:`-i` flag (using this flag outputs ONLY a .gif file, and no .mp4 file)
#. Added a :code:`--verbose` flag
#. You can save the logs to a file by using :code:`--log_to_file`
#. Add experimental javascript rendering with :code:`--use_js_renderer`
Config system
@ -62,6 +63,7 @@ Mobjects, Scenes, and Animations
#. Add a :code:`Variable` class for displaying text that continuously updates to reflect the value of a python variable.
#. The ``Tex`` and ``MathTex`` objects allow you to specify a custom TexTemplate using the ``template`` keyword argument.
#. :code:`VGroup` now supports printing the class names of contained mobjects and :code:`VDict` supports printing the internal dict of mobjects
#. :code:`Scene` now renders when :code:`Scene.render()` is called rather than upon instantiation.
#. :code:`ValueTracker` now supports increment using the `+=` operator (in addition to the already existing `increment_value` method)

View file

@ -192,7 +192,7 @@ class ManimDirective(Directive):
"from manim import *",
*file_writer_config_code,
*user_code,
f"{clsname}()",
f"{clsname}().render()",
]
exec("\n".join(code), globals())

View file

@ -56,6 +56,7 @@ from .mobject.vector_field import *
from .scene.graph_scene import *
from .scene.moving_camera_scene import *
from .scene.reconfigurable_scene import *
from .scene.js_scene import *
from .scene.scene import *
from .scene.sample_space_scene import *
from .scene.three_d_scene import *

View file

@ -9,12 +9,17 @@ import importlib.util
import types
from . import constants, logger, console, file_writer_config
from .config.config import args
from .config.config import camera_config, args
from .config import cfg_subcmds
from .utils.module_ops import (
get_module,
get_scene_classes_from_module,
get_scenes_to_render,
)
from .scene.scene import Scene
from .utils.sounds import play_error_sound, play_finish_sound
from .utils.file_ops import open_file as open_media_file
from . import constants
from .grpc.impl import frame_server_impl
def open_file_if_needed(file_writer):
@ -50,104 +55,6 @@ def open_file_if_needed(file_writer):
sys.stdout = curr_stdout
def is_child_scene(obj, module):
return (
inspect.isclass(obj)
and issubclass(obj, Scene)
and obj != Scene
and obj.__module__.startswith(module.__name__)
)
def prompt_user_for_choice(scene_classes):
num_to_class = {}
for count, scene_class in enumerate(scene_classes):
count += 1 # start with 1 instead of 0
name = scene_class.__name__
console.print(f"{count}: {name}", style="logging.level.info")
num_to_class[count] = scene_class
try:
user_input = console.input(
f"[log.message] {constants.CHOOSE_NUMBER_MESSAGE} [/log.message]"
)
return [
num_to_class[int(num_str)]
for num_str in re.split(r"\s*,\s*", user_input.strip())
]
except KeyError:
logger.error(constants.INVALID_NUMBER_MESSAGE)
sys.exit(2)
except EOFError:
sys.exit(1)
def get_scenes_to_render(scene_classes):
if not scene_classes:
logger.error(constants.NO_SCENE_MESSAGE)
return []
if file_writer_config["write_all"]:
return scene_classes
result = []
for scene_name in file_writer_config["scene_names"]:
found = False
for scene_class in scene_classes:
if scene_class.__name__ == scene_name:
result.append(scene_class)
found = True
break
if not found and (scene_name != ""):
logger.error(constants.SCENE_NOT_FOUND_MESSAGE.format(scene_name))
if result:
return result
return (
[scene_classes[0]]
if len(scene_classes) == 1
else prompt_user_for_choice(scene_classes)
)
def get_scene_classes_from_module(module):
return [
member[1]
for member in inspect.getmembers(module, lambda x: is_child_scene(x, module))
]
def get_module(file_name):
if file_name == "-":
# Since this feature is used for rapid testing, using Scene Caching would be a
# hindrance in this case.
file_writer_config["disable_caching"] = True
module = types.ModuleType("input_scenes")
logger.info(
"Enter the animation's code & end with an EOF (CTRL+D on Linux/Unix, CTRL+Z on Windows):"
)
code = sys.stdin.read()
if not code.startswith("from manim import"):
logger.warning(
"Didn't find an import statement for Manim. Importing automatically..."
)
code = "from manim import *\n" + code
logger.info("Rendering animation from typed code...")
try:
exec(code, module.__dict__)
return module
except Exception as e:
logger.error(f"Failed to render scene: {str(e)}")
sys.exit(2)
else:
if os.path.exists(file_name):
if file_name[-3:] != ".py":
raise Exception(f"{file_name} is not a valid Manim python script.")
module_name = file_name[:-3].replace(os.sep, ".").split(".")[-1]
spec = importlib.util.spec_from_file_location(module_name, file_name)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
else:
raise FileNotFoundError(f"{file_name} not found")
def main():
if hasattr(args, "subcommands"):
if "cfg" in args.subcommands:
@ -169,15 +76,19 @@ def main():
sound_on = file_writer_config["sound"]
for SceneClass in scene_classes_to_render:
try:
# By invoking, this renders the full scene
scene = SceneClass()
open_file_if_needed(scene.file_writer)
if sound_on:
play_finish_sound()
if camera_config["use_js_renderer"]:
frame_server_impl.get(SceneClass).start()
else:
scene = SceneClass()
scene.render()
open_file_if_needed(scene.file_writer)
if sound_on:
play_finish_sound()
except Exception:
print("\n\n")
traceback.print_exc()
print("\n\n")
if not camera_config["use_js_renderer"]:
if sound_on:
play_error_sound()

View file

@ -104,12 +104,6 @@ class Camera(object):
# corresponding class. If a Mobject is not an instance of a class in
# this dict (or an instance of a class that inherits from a class in
# this dict), then it cannot be rendered.
self.display_funcs = {
VMobject: self.display_multiple_vectorized_mobjects,
PMobject: self.display_multiple_point_cloud_mobjects,
AbstractImageMobject: self.display_multiple_image_mobjects,
Mobject: lambda batch, pa: batch, # Do nothing
}
self.init_background()
self.resize_frame_shape()
@ -149,6 +143,12 @@ class Camera(object):
:exc:`TypeError`
When mobject is not an instance of a class that can be rendered.
"""
self.display_funcs = {
VMobject: self.display_multiple_vectorized_mobjects,
PMobject: self.display_multiple_point_cloud_mobjects,
AbstractImageMobject: self.display_multiple_image_mobjects,
Mobject: lambda batch, pa: batch, # Do nothing
}
# We have to check each type in turn because we are dealing with
# super classes. For example, if square = Square(), then
# type(square) != VMobject, but isinstance(square, VMobject) == True.
@ -358,6 +358,9 @@ class Camera(object):
self.set_pixel_array(self.background)
return self
def set_frame_to_background(self, background):
self.set_pixel_array(background)
####
# TODO, it's weird that this is part of camera.

32
manim/camera/js_camera.py Normal file
View file

@ -0,0 +1,32 @@
from .camera import Camera
import copy
class JsCamera(Camera):
def __init__(self, **kwargs):
super().__init__(self, **kwargs)
self.serialized_frame = []
self.pixel_array = None
def display_multiple_non_background_colored_vmobjects(self, vmobjects, _):
for vmobject in vmobjects:
# TODO: Store a proto instead of JSON.
needs_redraw = False
point_hash = hash(tuple(vmobject.points.flatten()))
if vmobject.point_hash != point_hash:
vmobject.point_hash = point_hash
needs_redraw = True
self.serialized_frame.append(
{
"points": vmobject.points.tolist(),
"style": vmobject.get_style(simple=True),
"id": id(vmobject),
"needs_redraw": needs_redraw,
}
)
def reset(self):
self.serialized_frame = []
def set_frame_to_background(self, background):
self.serialized_frame = copy.deepcopy(background)

View file

@ -112,6 +112,14 @@ def _parse_config(config_parser, args):
background_color = colour.Color(default["background_color"])
config["background_color"] = background_color
config["use_js_renderer"] = args.use_js_renderer or default.getboolean(
"use_js_renderer"
)
config["js_renderer_path"] = args.js_renderer_path or default.get(
"js_renderer_path"
)
# Set the rest of the frame properties
config["frame_height"] = 8.0
config["frame_width"] = (
@ -151,6 +159,8 @@ if _from_command_line():
if not (hasattr(args, "subcommands")):
_init_dirs(file_writer_config)
config = _parse_config(config_parser, args)
if config["use_js_renderer"]:
file_writer_config["disable_caching"] = True
camera_config = config
# Set the different loggers

View file

@ -391,6 +391,18 @@ def _parse_cli(arg_list, input=True):
"the rendering at the second value",
)
parser.add_argument(
"--use_js_renderer",
help="Render animations using the javascript frontend",
action="store_const",
const=True,
)
parser.add_argument(
"--js_renderer_path",
help="Path to the javascript frontend",
)
# Specify the manim.cfg file
parser.add_argument(
"--config_file",

View file

@ -78,6 +78,21 @@ media_dir = ./media
# --log_dir (by default "/logs", that will be put inside the media dir)
log_dir = logs
# # --video_dir
# video_dir = %(MEDIA_DIR)s/videos
# # --tex_dir
# tex_dir = %(MEDIA_DIR)s/Tex
# # --text_dir
# text_dir = %(MEDIA_DIR)s/texts
# --use_js_renderer
use_js_renderer = False
# --js_renderer_path
js_renderer_path =
# If the -t (--transparent) flag is used, these will be replaced with the
# values specified in the [TRANSPARENT] section later in this file.
png_mode = RGB

View file

@ -120,3 +120,9 @@ FFMPEG_VERBOSITY_MAP = {
"CRITICAL": "fatal",
}
VERBOSITY_CHOICES = FFMPEG_VERBOSITY_MAP.keys()
JS_RENDERER_INFO = (
"The Electron frontend to Manim is hosted at "
"https://github.com/ManimCommunity/manim-renderer. After cloning and building it, "
"you can either start it prior to running Manim or specify the path to the "
"executable with the --js_renderer_path flag."
)

0
manim/grpc/__init__.py Normal file
View file

View file

@ -0,0 +1,6 @@
# https://github.com/protocolbuffers/protobuf/issues/1491#issuecomment-547504972
import sys
from pathlib import Path
sys.path.append(str(Path(__file__).parent))

View file

@ -0,0 +1,743 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: frameserver.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name="frameserver.proto",
package="frameserver",
syntax="proto3",
serialized_options=None,
serialized_pb=b'\n\x11\x66rameserver.proto\x12\x0b\x66rameserver"A\n\x0c\x46rameRequest\x12\x17\n\x0f\x61nimation_index\x18\x01 \x01(\x03\x12\x18\n\x10\x61nimation_offset\x18\x02 \x01(\x02"u\n\x05Style\x12\x12\n\nfill_color\x18\x01 \x01(\t\x12\x14\n\x0c\x66ill_opacity\x18\x02 \x01(\x02\x12\x14\n\x0cstroke_color\x18\x03 \x01(\t\x12\x16\n\x0estroke_opacity\x18\x04 \x01(\x02\x12\x14\n\x0cstroke_width\x18\x05 \x01(\x02"(\n\x05Point\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02"v\n\x0bMobjectData\x12\n\n\x02id\x18\x01 \x01(\x03\x12"\n\x06points\x18\x02 \x03(\x0b\x32\x12.frameserver.Point\x12!\n\x05style\x18\x03 \x01(\x0b\x32\x12.frameserver.Style\x12\x14\n\x0cneeds_redraw\x18\x04 \x01(\x08"\xb0\x01\n\rFrameResponse\x12*\n\x08mobjects\x18\x01 \x03(\x0b\x32\x18.frameserver.MobjectData\x12\x15\n\rframe_pending\x18\x02 \x01(\x08\x12\x1a\n\x12\x61nimation_finished\x18\x03 \x01(\x08\x12\x16\n\x0escene_finished\x18\x04 \x01(\x08\x12\x10\n\x08\x64uration\x18\x05 \x01(\x02\x12\x16\n\x0e\x61nimation_name\x18\x06 \x01(\t"\x17\n\x15RendererStatusRequest",\n\x16RendererStatusResponse\x12\x12\n\nscene_name\x18\x01 \x01(\t"\x16\n\x14SceneLocationRequest"\x17\n\x15SceneLocationResponse2\x8f\x02\n\x0b\x46rameServer\x12G\n\x0eGetFrameAtTime\x12\x19.frameserver.FrameRequest\x1a\x1a.frameserver.FrameResponse\x12Y\n\x0eRendererStatus\x12".frameserver.RendererStatusRequest\x1a#.frameserver.RendererStatusResponse\x12\\\n\x13UpdateSceneLocation\x12!.frameserver.SceneLocationRequest\x1a".frameserver.SceneLocationResponseb\x06proto3',
)
_FRAMEREQUEST = _descriptor.Descriptor(
name="FrameRequest",
full_name="frameserver.FrameRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="animation_index",
full_name="frameserver.FrameRequest.animation_index",
index=0,
number=1,
type=3,
cpp_type=2,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="animation_offset",
full_name="frameserver.FrameRequest.animation_offset",
index=1,
number=2,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=34,
serialized_end=99,
)
_STYLE = _descriptor.Descriptor(
name="Style",
full_name="frameserver.Style",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="fill_color",
full_name="frameserver.Style.fill_color",
index=0,
number=1,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="fill_opacity",
full_name="frameserver.Style.fill_opacity",
index=1,
number=2,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="stroke_color",
full_name="frameserver.Style.stroke_color",
index=2,
number=3,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="stroke_opacity",
full_name="frameserver.Style.stroke_opacity",
index=3,
number=4,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="stroke_width",
full_name="frameserver.Style.stroke_width",
index=4,
number=5,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=101,
serialized_end=218,
)
_POINT = _descriptor.Descriptor(
name="Point",
full_name="frameserver.Point",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="x",
full_name="frameserver.Point.x",
index=0,
number=1,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="y",
full_name="frameserver.Point.y",
index=1,
number=2,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="z",
full_name="frameserver.Point.z",
index=2,
number=3,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=220,
serialized_end=260,
)
_MOBJECTDATA = _descriptor.Descriptor(
name="MobjectData",
full_name="frameserver.MobjectData",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="id",
full_name="frameserver.MobjectData.id",
index=0,
number=1,
type=3,
cpp_type=2,
label=1,
has_default_value=False,
default_value=0,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="points",
full_name="frameserver.MobjectData.points",
index=1,
number=2,
type=11,
cpp_type=10,
label=3,
has_default_value=False,
default_value=[],
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="style",
full_name="frameserver.MobjectData.style",
index=2,
number=3,
type=11,
cpp_type=10,
label=1,
has_default_value=False,
default_value=None,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="needs_redraw",
full_name="frameserver.MobjectData.needs_redraw",
index=3,
number=4,
type=8,
cpp_type=7,
label=1,
has_default_value=False,
default_value=False,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=262,
serialized_end=380,
)
_FRAMERESPONSE = _descriptor.Descriptor(
name="FrameResponse",
full_name="frameserver.FrameResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="mobjects",
full_name="frameserver.FrameResponse.mobjects",
index=0,
number=1,
type=11,
cpp_type=10,
label=3,
has_default_value=False,
default_value=[],
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="frame_pending",
full_name="frameserver.FrameResponse.frame_pending",
index=1,
number=2,
type=8,
cpp_type=7,
label=1,
has_default_value=False,
default_value=False,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="animation_finished",
full_name="frameserver.FrameResponse.animation_finished",
index=2,
number=3,
type=8,
cpp_type=7,
label=1,
has_default_value=False,
default_value=False,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="scene_finished",
full_name="frameserver.FrameResponse.scene_finished",
index=3,
number=4,
type=8,
cpp_type=7,
label=1,
has_default_value=False,
default_value=False,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="duration",
full_name="frameserver.FrameResponse.duration",
index=4,
number=5,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="animation_name",
full_name="frameserver.FrameResponse.animation_name",
index=5,
number=6,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=383,
serialized_end=559,
)
_RENDERERSTATUSREQUEST = _descriptor.Descriptor(
name="RendererStatusRequest",
full_name="frameserver.RendererStatusRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=561,
serialized_end=584,
)
_RENDERERSTATUSRESPONSE = _descriptor.Descriptor(
name="RendererStatusResponse",
full_name="frameserver.RendererStatusResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="scene_name",
full_name="frameserver.RendererStatusResponse.scene_name",
index=0,
number=1,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=586,
serialized_end=630,
)
_SCENELOCATIONREQUEST = _descriptor.Descriptor(
name="SceneLocationRequest",
full_name="frameserver.SceneLocationRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=632,
serialized_end=654,
)
_SCENELOCATIONRESPONSE = _descriptor.Descriptor(
name="SceneLocationResponse",
full_name="frameserver.SceneLocationResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=656,
serialized_end=679,
)
_MOBJECTDATA.fields_by_name["points"].message_type = _POINT
_MOBJECTDATA.fields_by_name["style"].message_type = _STYLE
_FRAMERESPONSE.fields_by_name["mobjects"].message_type = _MOBJECTDATA
DESCRIPTOR.message_types_by_name["FrameRequest"] = _FRAMEREQUEST
DESCRIPTOR.message_types_by_name["Style"] = _STYLE
DESCRIPTOR.message_types_by_name["Point"] = _POINT
DESCRIPTOR.message_types_by_name["MobjectData"] = _MOBJECTDATA
DESCRIPTOR.message_types_by_name["FrameResponse"] = _FRAMERESPONSE
DESCRIPTOR.message_types_by_name["RendererStatusRequest"] = _RENDERERSTATUSREQUEST
DESCRIPTOR.message_types_by_name["RendererStatusResponse"] = _RENDERERSTATUSRESPONSE
DESCRIPTOR.message_types_by_name["SceneLocationRequest"] = _SCENELOCATIONREQUEST
DESCRIPTOR.message_types_by_name["SceneLocationResponse"] = _SCENELOCATIONRESPONSE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
FrameRequest = _reflection.GeneratedProtocolMessageType(
"FrameRequest",
(_message.Message,),
{
"DESCRIPTOR": _FRAMEREQUEST,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.FrameRequest)
},
)
_sym_db.RegisterMessage(FrameRequest)
Style = _reflection.GeneratedProtocolMessageType(
"Style",
(_message.Message,),
{
"DESCRIPTOR": _STYLE,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.Style)
},
)
_sym_db.RegisterMessage(Style)
Point = _reflection.GeneratedProtocolMessageType(
"Point",
(_message.Message,),
{
"DESCRIPTOR": _POINT,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.Point)
},
)
_sym_db.RegisterMessage(Point)
MobjectData = _reflection.GeneratedProtocolMessageType(
"MobjectData",
(_message.Message,),
{
"DESCRIPTOR": _MOBJECTDATA,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.MobjectData)
},
)
_sym_db.RegisterMessage(MobjectData)
FrameResponse = _reflection.GeneratedProtocolMessageType(
"FrameResponse",
(_message.Message,),
{
"DESCRIPTOR": _FRAMERESPONSE,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.FrameResponse)
},
)
_sym_db.RegisterMessage(FrameResponse)
RendererStatusRequest = _reflection.GeneratedProtocolMessageType(
"RendererStatusRequest",
(_message.Message,),
{
"DESCRIPTOR": _RENDERERSTATUSREQUEST,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.RendererStatusRequest)
},
)
_sym_db.RegisterMessage(RendererStatusRequest)
RendererStatusResponse = _reflection.GeneratedProtocolMessageType(
"RendererStatusResponse",
(_message.Message,),
{
"DESCRIPTOR": _RENDERERSTATUSRESPONSE,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.RendererStatusResponse)
},
)
_sym_db.RegisterMessage(RendererStatusResponse)
SceneLocationRequest = _reflection.GeneratedProtocolMessageType(
"SceneLocationRequest",
(_message.Message,),
{
"DESCRIPTOR": _SCENELOCATIONREQUEST,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.SceneLocationRequest)
},
)
_sym_db.RegisterMessage(SceneLocationRequest)
SceneLocationResponse = _reflection.GeneratedProtocolMessageType(
"SceneLocationResponse",
(_message.Message,),
{
"DESCRIPTOR": _SCENELOCATIONRESPONSE,
"__module__": "frameserver_pb2"
# @@protoc_insertion_point(class_scope:frameserver.SceneLocationResponse)
},
)
_sym_db.RegisterMessage(SceneLocationResponse)
_FRAMESERVER = _descriptor.ServiceDescriptor(
name="FrameServer",
full_name="frameserver.FrameServer",
file=DESCRIPTOR,
index=0,
serialized_options=None,
serialized_start=682,
serialized_end=953,
methods=[
_descriptor.MethodDescriptor(
name="GetFrameAtTime",
full_name="frameserver.FrameServer.GetFrameAtTime",
index=0,
containing_service=None,
input_type=_FRAMEREQUEST,
output_type=_FRAMERESPONSE,
serialized_options=None,
),
_descriptor.MethodDescriptor(
name="RendererStatus",
full_name="frameserver.FrameServer.RendererStatus",
index=1,
containing_service=None,
input_type=_RENDERERSTATUSREQUEST,
output_type=_RENDERERSTATUSRESPONSE,
serialized_options=None,
),
_descriptor.MethodDescriptor(
name="UpdateSceneLocation",
full_name="frameserver.FrameServer.UpdateSceneLocation",
index=2,
containing_service=None,
input_type=_SCENELOCATIONREQUEST,
output_type=_SCENELOCATIONRESPONSE,
serialized_options=None,
),
],
)
_sym_db.RegisterServiceDescriptor(_FRAMESERVER)
DESCRIPTOR.services_by_name["FrameServer"] = _FRAMESERVER
# @@protoc_insertion_point(module_scope)

View file

@ -0,0 +1,164 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
import frameserver_pb2 as frameserver__pb2
class FrameServerStub(object):
"""Missing associated documentation comment in .proto file"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.GetFrameAtTime = channel.unary_unary(
"/frameserver.FrameServer/GetFrameAtTime",
request_serializer=frameserver__pb2.FrameRequest.SerializeToString,
response_deserializer=frameserver__pb2.FrameResponse.FromString,
)
self.RendererStatus = channel.unary_unary(
"/frameserver.FrameServer/RendererStatus",
request_serializer=frameserver__pb2.RendererStatusRequest.SerializeToString,
response_deserializer=frameserver__pb2.RendererStatusResponse.FromString,
)
self.UpdateSceneLocation = channel.unary_unary(
"/frameserver.FrameServer/UpdateSceneLocation",
request_serializer=frameserver__pb2.SceneLocationRequest.SerializeToString,
response_deserializer=frameserver__pb2.SceneLocationResponse.FromString,
)
class FrameServerServicer(object):
"""Missing associated documentation comment in .proto file"""
def GetFrameAtTime(self, request, context):
"""Updates the scene to the specified animation offset and returns a
serialization of the frame at that time.
"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def RendererStatus(self, request, context):
"""Used to signal to the renderer that manim is running."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def UpdateSceneLocation(self, request, context):
"""Missing associated documentation comment in .proto file"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def add_FrameServerServicer_to_server(servicer, server):
rpc_method_handlers = {
"GetFrameAtTime": grpc.unary_unary_rpc_method_handler(
servicer.GetFrameAtTime,
request_deserializer=frameserver__pb2.FrameRequest.FromString,
response_serializer=frameserver__pb2.FrameResponse.SerializeToString,
),
"RendererStatus": grpc.unary_unary_rpc_method_handler(
servicer.RendererStatus,
request_deserializer=frameserver__pb2.RendererStatusRequest.FromString,
response_serializer=frameserver__pb2.RendererStatusResponse.SerializeToString,
),
"UpdateSceneLocation": grpc.unary_unary_rpc_method_handler(
servicer.UpdateSceneLocation,
request_deserializer=frameserver__pb2.SceneLocationRequest.FromString,
response_serializer=frameserver__pb2.SceneLocationResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
"frameserver.FrameServer", rpc_method_handlers
)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class FrameServer(object):
"""Missing associated documentation comment in .proto file"""
@staticmethod
def GetFrameAtTime(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/frameserver.FrameServer/GetFrameAtTime",
frameserver__pb2.FrameRequest.SerializeToString,
frameserver__pb2.FrameResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
@staticmethod
def RendererStatus(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/frameserver.FrameServer/RendererStatus",
frameserver__pb2.RendererStatusRequest.SerializeToString,
frameserver__pb2.RendererStatusResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
@staticmethod
def UpdateSceneLocation(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/frameserver.FrameServer/UpdateSceneLocation",
frameserver__pb2.SceneLocationRequest.SerializeToString,
frameserver__pb2.SceneLocationResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)

View file

@ -0,0 +1,550 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: renderserver.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name="renderserver.proto",
package="renderserver",
syntax="proto3",
serialized_options=None,
serialized_pb=b'\n\x12renderserver.proto\x12\x0crenderserver"\x0e\n\x0c\x45mptyRequest"\x0f\n\rEmptyResponse"\x1f\n\x0fNewSceneRequest\x12\x0c\n\x04name\x18\x01 \x01(\t"\x12\n\x10NewSceneResponse"+\n\tAnimation\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x10\n\x08\x64uration\x18\x02 \x01(\x02"\x18\n\x16\x41nimationStatusRequest"\x19\n\x17\x41nimationStatusResponse"m\n\x12ManimStatusRequest\x12\x12\n\nscene_name\x18\x01 \x01(\t\x12\x16\n\x0escene_finished\x18\x02 \x01(\x08\x12+\n\nanimations\x18\x03 \x03(\x0b\x32\x17.renderserver.Animation"\x15\n\x13ManimStatusResponse"\x14\n\x12UpdateSceneRequest"\x15\n\x13UpdateSceneResponse2\x94\x03\n\x0cRenderServer\x12J\n\x0f\x41nimationStatus\x12\x1a.renderserver.EmptyRequest\x1a\x1b.renderserver.EmptyResponse\x12R\n\x0bManimStatus\x12 .renderserver.ManimStatusRequest\x1a!.renderserver.ManimStatusResponse\x12R\n\x0bUpdateScene\x12 .renderserver.UpdateSceneRequest\x1a!.renderserver.UpdateSceneResponse\x12\x46\n\x08NewScene\x12\x1d.renderserver.NewSceneRequest\x1a\x1b.renderserver.EmptyResponse\x12H\n\rSceneFinished\x12\x1a.renderserver.EmptyRequest\x1a\x1b.renderserver.EmptyResponseb\x06proto3',
)
_EMPTYREQUEST = _descriptor.Descriptor(
name="EmptyRequest",
full_name="renderserver.EmptyRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=36,
serialized_end=50,
)
_EMPTYRESPONSE = _descriptor.Descriptor(
name="EmptyResponse",
full_name="renderserver.EmptyResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=52,
serialized_end=67,
)
_NEWSCENEREQUEST = _descriptor.Descriptor(
name="NewSceneRequest",
full_name="renderserver.NewSceneRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="name",
full_name="renderserver.NewSceneRequest.name",
index=0,
number=1,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=69,
serialized_end=100,
)
_NEWSCENERESPONSE = _descriptor.Descriptor(
name="NewSceneResponse",
full_name="renderserver.NewSceneResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=102,
serialized_end=120,
)
_ANIMATION = _descriptor.Descriptor(
name="Animation",
full_name="renderserver.Animation",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="name",
full_name="renderserver.Animation.name",
index=0,
number=1,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="duration",
full_name="renderserver.Animation.duration",
index=1,
number=2,
type=2,
cpp_type=6,
label=1,
has_default_value=False,
default_value=float(0),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=122,
serialized_end=165,
)
_ANIMATIONSTATUSREQUEST = _descriptor.Descriptor(
name="AnimationStatusRequest",
full_name="renderserver.AnimationStatusRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=167,
serialized_end=191,
)
_ANIMATIONSTATUSRESPONSE = _descriptor.Descriptor(
name="AnimationStatusResponse",
full_name="renderserver.AnimationStatusResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=193,
serialized_end=218,
)
_MANIMSTATUSREQUEST = _descriptor.Descriptor(
name="ManimStatusRequest",
full_name="renderserver.ManimStatusRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name="scene_name",
full_name="renderserver.ManimStatusRequest.scene_name",
index=0,
number=1,
type=9,
cpp_type=9,
label=1,
has_default_value=False,
default_value=b"".decode("utf-8"),
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="scene_finished",
full_name="renderserver.ManimStatusRequest.scene_finished",
index=1,
number=2,
type=8,
cpp_type=7,
label=1,
has_default_value=False,
default_value=False,
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
_descriptor.FieldDescriptor(
name="animations",
full_name="renderserver.ManimStatusRequest.animations",
index=2,
number=3,
type=11,
cpp_type=10,
label=3,
has_default_value=False,
default_value=[],
message_type=None,
enum_type=None,
containing_type=None,
is_extension=False,
extension_scope=None,
serialized_options=None,
file=DESCRIPTOR,
),
],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=220,
serialized_end=329,
)
_MANIMSTATUSRESPONSE = _descriptor.Descriptor(
name="ManimStatusResponse",
full_name="renderserver.ManimStatusResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=331,
serialized_end=352,
)
_UPDATESCENEREQUEST = _descriptor.Descriptor(
name="UpdateSceneRequest",
full_name="renderserver.UpdateSceneRequest",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=354,
serialized_end=374,
)
_UPDATESCENERESPONSE = _descriptor.Descriptor(
name="UpdateSceneResponse",
full_name="renderserver.UpdateSceneResponse",
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[],
extensions=[],
nested_types=[],
enum_types=[],
serialized_options=None,
is_extendable=False,
syntax="proto3",
extension_ranges=[],
oneofs=[],
serialized_start=376,
serialized_end=397,
)
_MANIMSTATUSREQUEST.fields_by_name["animations"].message_type = _ANIMATION
DESCRIPTOR.message_types_by_name["EmptyRequest"] = _EMPTYREQUEST
DESCRIPTOR.message_types_by_name["EmptyResponse"] = _EMPTYRESPONSE
DESCRIPTOR.message_types_by_name["NewSceneRequest"] = _NEWSCENEREQUEST
DESCRIPTOR.message_types_by_name["NewSceneResponse"] = _NEWSCENERESPONSE
DESCRIPTOR.message_types_by_name["Animation"] = _ANIMATION
DESCRIPTOR.message_types_by_name["AnimationStatusRequest"] = _ANIMATIONSTATUSREQUEST
DESCRIPTOR.message_types_by_name["AnimationStatusResponse"] = _ANIMATIONSTATUSRESPONSE
DESCRIPTOR.message_types_by_name["ManimStatusRequest"] = _MANIMSTATUSREQUEST
DESCRIPTOR.message_types_by_name["ManimStatusResponse"] = _MANIMSTATUSRESPONSE
DESCRIPTOR.message_types_by_name["UpdateSceneRequest"] = _UPDATESCENEREQUEST
DESCRIPTOR.message_types_by_name["UpdateSceneResponse"] = _UPDATESCENERESPONSE
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
EmptyRequest = _reflection.GeneratedProtocolMessageType(
"EmptyRequest",
(_message.Message,),
{
"DESCRIPTOR": _EMPTYREQUEST,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.EmptyRequest)
},
)
_sym_db.RegisterMessage(EmptyRequest)
EmptyResponse = _reflection.GeneratedProtocolMessageType(
"EmptyResponse",
(_message.Message,),
{
"DESCRIPTOR": _EMPTYRESPONSE,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.EmptyResponse)
},
)
_sym_db.RegisterMessage(EmptyResponse)
NewSceneRequest = _reflection.GeneratedProtocolMessageType(
"NewSceneRequest",
(_message.Message,),
{
"DESCRIPTOR": _NEWSCENEREQUEST,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.NewSceneRequest)
},
)
_sym_db.RegisterMessage(NewSceneRequest)
NewSceneResponse = _reflection.GeneratedProtocolMessageType(
"NewSceneResponse",
(_message.Message,),
{
"DESCRIPTOR": _NEWSCENERESPONSE,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.NewSceneResponse)
},
)
_sym_db.RegisterMessage(NewSceneResponse)
Animation = _reflection.GeneratedProtocolMessageType(
"Animation",
(_message.Message,),
{
"DESCRIPTOR": _ANIMATION,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.Animation)
},
)
_sym_db.RegisterMessage(Animation)
AnimationStatusRequest = _reflection.GeneratedProtocolMessageType(
"AnimationStatusRequest",
(_message.Message,),
{
"DESCRIPTOR": _ANIMATIONSTATUSREQUEST,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.AnimationStatusRequest)
},
)
_sym_db.RegisterMessage(AnimationStatusRequest)
AnimationStatusResponse = _reflection.GeneratedProtocolMessageType(
"AnimationStatusResponse",
(_message.Message,),
{
"DESCRIPTOR": _ANIMATIONSTATUSRESPONSE,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.AnimationStatusResponse)
},
)
_sym_db.RegisterMessage(AnimationStatusResponse)
ManimStatusRequest = _reflection.GeneratedProtocolMessageType(
"ManimStatusRequest",
(_message.Message,),
{
"DESCRIPTOR": _MANIMSTATUSREQUEST,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.ManimStatusRequest)
},
)
_sym_db.RegisterMessage(ManimStatusRequest)
ManimStatusResponse = _reflection.GeneratedProtocolMessageType(
"ManimStatusResponse",
(_message.Message,),
{
"DESCRIPTOR": _MANIMSTATUSRESPONSE,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.ManimStatusResponse)
},
)
_sym_db.RegisterMessage(ManimStatusResponse)
UpdateSceneRequest = _reflection.GeneratedProtocolMessageType(
"UpdateSceneRequest",
(_message.Message,),
{
"DESCRIPTOR": _UPDATESCENEREQUEST,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.UpdateSceneRequest)
},
)
_sym_db.RegisterMessage(UpdateSceneRequest)
UpdateSceneResponse = _reflection.GeneratedProtocolMessageType(
"UpdateSceneResponse",
(_message.Message,),
{
"DESCRIPTOR": _UPDATESCENERESPONSE,
"__module__": "renderserver_pb2"
# @@protoc_insertion_point(class_scope:renderserver.UpdateSceneResponse)
},
)
_sym_db.RegisterMessage(UpdateSceneResponse)
_RENDERSERVER = _descriptor.ServiceDescriptor(
name="RenderServer",
full_name="renderserver.RenderServer",
file=DESCRIPTOR,
index=0,
serialized_options=None,
serialized_start=400,
serialized_end=804,
methods=[
_descriptor.MethodDescriptor(
name="AnimationStatus",
full_name="renderserver.RenderServer.AnimationStatus",
index=0,
containing_service=None,
input_type=_EMPTYREQUEST,
output_type=_EMPTYRESPONSE,
serialized_options=None,
),
_descriptor.MethodDescriptor(
name="ManimStatus",
full_name="renderserver.RenderServer.ManimStatus",
index=1,
containing_service=None,
input_type=_MANIMSTATUSREQUEST,
output_type=_MANIMSTATUSRESPONSE,
serialized_options=None,
),
_descriptor.MethodDescriptor(
name="UpdateScene",
full_name="renderserver.RenderServer.UpdateScene",
index=2,
containing_service=None,
input_type=_UPDATESCENEREQUEST,
output_type=_UPDATESCENERESPONSE,
serialized_options=None,
),
_descriptor.MethodDescriptor(
name="NewScene",
full_name="renderserver.RenderServer.NewScene",
index=3,
containing_service=None,
input_type=_NEWSCENEREQUEST,
output_type=_EMPTYRESPONSE,
serialized_options=None,
),
_descriptor.MethodDescriptor(
name="SceneFinished",
full_name="renderserver.RenderServer.SceneFinished",
index=4,
containing_service=None,
input_type=_EMPTYREQUEST,
output_type=_EMPTYRESPONSE,
serialized_options=None,
),
],
)
_sym_db.RegisterServiceDescriptor(_RENDERSERVER)
DESCRIPTOR.services_by_name["RenderServer"] = _RENDERSERVER
# @@protoc_insertion_point(module_scope)

View file

@ -0,0 +1,248 @@
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
import grpc
import renderserver_pb2 as renderserver__pb2
class RenderServerStub(object):
"""Missing associated documentation comment in .proto file"""
def __init__(self, channel):
"""Constructor.
Args:
channel: A grpc.Channel.
"""
self.AnimationStatus = channel.unary_unary(
"/renderserver.RenderServer/AnimationStatus",
request_serializer=renderserver__pb2.EmptyRequest.SerializeToString,
response_deserializer=renderserver__pb2.EmptyResponse.FromString,
)
self.ManimStatus = channel.unary_unary(
"/renderserver.RenderServer/ManimStatus",
request_serializer=renderserver__pb2.ManimStatusRequest.SerializeToString,
response_deserializer=renderserver__pb2.ManimStatusResponse.FromString,
)
self.UpdateScene = channel.unary_unary(
"/renderserver.RenderServer/UpdateScene",
request_serializer=renderserver__pb2.UpdateSceneRequest.SerializeToString,
response_deserializer=renderserver__pb2.UpdateSceneResponse.FromString,
)
self.NewScene = channel.unary_unary(
"/renderserver.RenderServer/NewScene",
request_serializer=renderserver__pb2.NewSceneRequest.SerializeToString,
response_deserializer=renderserver__pb2.EmptyResponse.FromString,
)
self.SceneFinished = channel.unary_unary(
"/renderserver.RenderServer/SceneFinished",
request_serializer=renderserver__pb2.EmptyRequest.SerializeToString,
response_deserializer=renderserver__pb2.EmptyResponse.FromString,
)
class RenderServerServicer(object):
"""Missing associated documentation comment in .proto file"""
def AnimationStatus(self, request, context):
"""Used to signal that a new animation is ready to be animated."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def ManimStatus(self, request, context):
"""Used to signal when manim starts and stops."""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def UpdateScene(self, request, context):
"""Missing associated documentation comment in .proto file"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def NewScene(self, request, context):
"""Missing associated documentation comment in .proto file"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def SceneFinished(self, request, context):
"""Missing associated documentation comment in .proto file"""
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
context.set_details("Method not implemented!")
raise NotImplementedError("Method not implemented!")
def add_RenderServerServicer_to_server(servicer, server):
rpc_method_handlers = {
"AnimationStatus": grpc.unary_unary_rpc_method_handler(
servicer.AnimationStatus,
request_deserializer=renderserver__pb2.EmptyRequest.FromString,
response_serializer=renderserver__pb2.EmptyResponse.SerializeToString,
),
"ManimStatus": grpc.unary_unary_rpc_method_handler(
servicer.ManimStatus,
request_deserializer=renderserver__pb2.ManimStatusRequest.FromString,
response_serializer=renderserver__pb2.ManimStatusResponse.SerializeToString,
),
"UpdateScene": grpc.unary_unary_rpc_method_handler(
servicer.UpdateScene,
request_deserializer=renderserver__pb2.UpdateSceneRequest.FromString,
response_serializer=renderserver__pb2.UpdateSceneResponse.SerializeToString,
),
"NewScene": grpc.unary_unary_rpc_method_handler(
servicer.NewScene,
request_deserializer=renderserver__pb2.NewSceneRequest.FromString,
response_serializer=renderserver__pb2.EmptyResponse.SerializeToString,
),
"SceneFinished": grpc.unary_unary_rpc_method_handler(
servicer.SceneFinished,
request_deserializer=renderserver__pb2.EmptyRequest.FromString,
response_serializer=renderserver__pb2.EmptyResponse.SerializeToString,
),
}
generic_handler = grpc.method_handlers_generic_handler(
"renderserver.RenderServer", rpc_method_handlers
)
server.add_generic_rpc_handlers((generic_handler,))
# This class is part of an EXPERIMENTAL API.
class RenderServer(object):
"""Missing associated documentation comment in .proto file"""
@staticmethod
def AnimationStatus(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/renderserver.RenderServer/AnimationStatus",
renderserver__pb2.EmptyRequest.SerializeToString,
renderserver__pb2.EmptyResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
@staticmethod
def ManimStatus(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/renderserver.RenderServer/ManimStatus",
renderserver__pb2.ManimStatusRequest.SerializeToString,
renderserver__pb2.ManimStatusResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
@staticmethod
def UpdateScene(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/renderserver.RenderServer/UpdateScene",
renderserver__pb2.UpdateSceneRequest.SerializeToString,
renderserver__pb2.UpdateSceneResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
@staticmethod
def NewScene(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/renderserver.RenderServer/NewScene",
renderserver__pb2.NewSceneRequest.SerializeToString,
renderserver__pb2.EmptyResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)
@staticmethod
def SceneFinished(
request,
target,
options=(),
channel_credentials=None,
call_credentials=None,
compression=None,
wait_for_ready=None,
timeout=None,
metadata=None,
):
return grpc.experimental.unary_unary(
request,
target,
"/renderserver.RenderServer/SceneFinished",
renderserver__pb2.EmptyRequest.SerializeToString,
renderserver__pb2.EmptyResponse.FromString,
options,
channel_credentials,
call_credentials,
compression,
wait_for_ready,
timeout,
metadata,
)

View file

View file

@ -0,0 +1,262 @@
from ...config import camera_config
from ...config import file_writer_config
from ...scene import scene
from ..gen import frameserver_pb2
from ..gen import frameserver_pb2_grpc
from ..gen import renderserver_pb2
from ..gen import renderserver_pb2_grpc
from concurrent import futures
from google.protobuf import json_format
from watchdog.events import LoggingEventHandler, FileSystemEventHandler
from watchdog.observers import Observer
import grpc
import subprocess as sp
import threading
import time
import ctypes
from ...utils.module_ops import (
get_module,
get_scene_classes_from_module,
get_scenes_to_render,
)
from ...config.logger import logger
from ...constants import JS_RENDERER_INFO
class FrameServer(frameserver_pb2_grpc.FrameServerServicer):
def animation_index_is_cached(self, animation_index):
return animation_index < len(self.keyframes)
def __init__(self, server, scene_class):
self.server = server
self.keyframes = []
self.scene = scene_class(self)
self.scene_thread = threading.Thread(
target=lambda s: s.render(), args=(self.scene,)
)
self.previous_frame_animation_index = None
self.scene_finished = False
path = "./example_scenes/basic.py"
event_handler = UpdateFrontendHandler(self)
observer = Observer()
observer.schedule(event_handler, path)
observer.start()
# If a javascript renderer is running, notify it of the scene being served. If
# not, spawn one and it will request the scene when it starts.
with grpc.insecure_channel("localhost:50052") as channel:
stub = renderserver_pb2_grpc.RenderServerStub(channel)
request = renderserver_pb2.NewSceneRequest(name=str(self.scene))
try:
stub.NewScene(request)
except grpc._channel._InactiveRpcError:
logger.warning(f"No frontend was detected at localhost:50052.")
try:
sp.Popen(camera_config["js_renderer_path"])
except PermissionError:
logger.info(JS_RENDERER_INFO)
self.server.stop(None)
return
self.scene_thread.start()
def signal_pending_animation(self, animation_index):
self.scene.start_animation = animation_index
self.scene.animation_finished.set()
return frameserver_pb2.FrameResponse(frame_pending=True)
def GetFrameAtTime(self, request, context):
selected_scene = None
if self.animation_index_is_cached(request.animation_index):
selected_scene = self.keyframes[request.animation_index]
else:
return self.signal_pending_animation(request.animation_index)
# play() uses run_time and wait() uses duration TODO: Fix this inconsistency.
# TODO: What about animations without a fixed duration?
duration = (
selected_scene.run_time
if selected_scene.animations
else selected_scene.duration
)
if request.animation_offset > duration:
if self.animation_index_is_cached(request.animation_index + 1):
# TODO: Clone scenes to allow reuse.
selected_scene = self.keyframes[request.animation_index + 1]
else:
return self.signal_pending_animation(request.animation_index + 1)
setattr(selected_scene, "camera", self.scene.camera)
if selected_scene.animations:
# This is a call to play().
selected_scene.update_animation_to_time(request.animation_offset)
selected_scene.update_frame(
selected_scene.moving_mobjects,
selected_scene.static_image,
)
serialized_mobject_list, duration = selected_scene.add_frame(
selected_scene.get_frame()
)
resp = list_to_frame_response(
selected_scene, duration, serialized_mobject_list
)
return resp
else:
# This is a call to wait().
if selected_scene.should_update_mobjects():
# TODO, be smart about setting a static image
# the same way Scene.play does
selected_scene.update_animation_to_time(time)
selected_scene.update_frame()
serialized_mobject_list, duration = selected_scene.add_frame(
selected_scene.get_frame()
)
frame_response = list_to_frame_response(
selected_scene, duration, serialized_mobject_list
)
if (
selected_scene.stop_condition is not None
and selected_scene.stop_condition()
):
selected_scene.animation_finished.set()
frame_response.frame_pending = True
selected_scene.renderer_waiting = True
return frame_response
elif selected_scene.skip_animations:
# Do nothing
return
else:
selected_scene.update_frame()
dt = 1 / selected_scene.camera.frame_rate
serialized_mobject_list, duration = selected_scene.add_frame(
selected_scene.get_frame(),
num_frames=int(selected_scene.duration / dt),
)
resp = list_to_frame_response(
selected_scene, duration, serialized_mobject_list
)
return resp
def RendererStatus(self, request, context):
response = frameserver_pb2.RendererStatusResponse()
response.scene_name = str(self.scene)
return response
# def UpdateSceneLocation(self, request, context):
# # Reload self.scene.
# print(scene_classes_to_render)
# response = frameserver_pb2.SceneLocationResponse()
# return response
def list_to_frame_response(scene, duration, serialized_mobject_list):
response = frameserver_pb2.FrameResponse()
response.frame_pending = False
response.duration = duration
for mob_serialization in serialized_mobject_list:
mob_proto = response.mobjects.add()
mob_proto.id = mob_serialization["id"]
mob_proto.needs_redraw = mob_serialization["needs_redraw"]
for point in mob_serialization["points"]:
point_proto = mob_proto.points.add()
point_proto.x = point[0]
point_proto.y = point[1]
point_proto.z = point[2]
mob_proto.style.fill_color = mob_serialization["style"]["fill_color"]
mob_proto.style.fill_opacity = float(mob_serialization["style"]["fill_opacity"])
mob_proto.style.stroke_color = mob_serialization["style"]["stroke_color"]
mob_proto.style.stroke_opacity = float(
mob_serialization["style"]["stroke_opacity"]
)
mob_proto.style.stroke_width = float(mob_serialization["style"]["stroke_width"])
return response
class UpdateFrontendHandler(FileSystemEventHandler):
"""Logs all the events captured."""
def __init__(self, frame_server):
super().__init__()
self.frame_server = frame_server
def on_moved(self, event):
super().on_moved(event)
raise NotImplementedError("Update not implemented for moved files.")
def on_deleted(self, event):
super().on_deleted(event)
raise NotImplementedError("Update not implemented for deleted files.")
def on_modified(self, event):
super().on_modified(event)
module = get_module(file_writer_config["input_file"])
all_scene_classes = get_scene_classes_from_module(module)
scene_classes_to_render = get_scenes_to_render(all_scene_classes)
scene_class = scene_classes_to_render[0]
# Get the old thread's ID.
old_thread_id = None
old_thread = self.frame_server.scene_thread
if hasattr(old_thread, "_thread_id"):
old_thread_id = old_thread._thread_id
if old_thread_id is None:
for thread_id, thread in threading._active.items():
if thread is old_thread:
old_thread_id = thread_id
# Stop the old thread.
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
old_thread_id, ctypes.py_object(SystemExit)
)
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(old_thread_id, 0)
print("Exception raise failure")
old_thread.join()
# Start a new thread.
self.frame_server.initialize_scene(scene_class, start_animation=1)
self.frame_server.scene.reached_start_animation.wait()
# Serialize data on Animations up to the target one.
animations = []
for scene in self.frame_server.keyframes:
if scene.animations:
animation_duration = scene.run_time
if len(scene.animations) == 1:
animation_name = str(scene.animations[0])
else:
animation_name = f"{str(scene.animations[0])}..."
else:
animation_duration = scene.duration
animation_name = "Wait"
animations.append(
renderserver_pb2.Animation(
name=animation_name,
duration=animation_duration,
)
)
# Reset the renderer.
with grpc.insecure_channel("localhost:50052") as channel:
stub = renderserver_pb2_grpc.RenderServerStub(channel)
request = renderserver_pb2.ManimStatusRequest(
scene_name=str(self.frame_server.scene), animations=animations
)
try:
stub.ManimStatus(request)
except grpc._channel._InactiveRpcError:
sp.Popen(camera_config["js_renderer_path"])
def get(scene_class):
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
frameserver_pb2_grpc.add_FrameServerServicer_to_server(
FrameServer(server, scene_class), server
)
server.add_insecure_port("localhost:50051")
return server

View file

@ -0,0 +1,57 @@
syntax = "proto3";
package frameserver;
service FrameServer {
// Updates the scene to the specified animation offset and returns a
// serialization of the frame at that time.
rpc GetFrameAtTime (FrameRequest) returns (FrameResponse);
// Used to signal to the renderer that manim is running.
rpc RendererStatus (RendererStatusRequest) returns (RendererStatusResponse);
rpc UpdateSceneLocation (SceneLocationRequest) returns (SceneLocationResponse);
}
message FrameRequest {
int64 animation_index = 1;
float animation_offset = 2;
}
message Style {
string fill_color = 1;
float fill_opacity = 2;
string stroke_color = 3;
float stroke_opacity = 4;
float stroke_width = 5;
}
message Point {
float x = 1;
float y = 2;
float z = 3;
}
message MobjectData {
int64 id = 1;
repeated Point points = 2;
Style style = 3;
bool needs_redraw = 4;
}
message FrameResponse {
repeated MobjectData mobjects = 1;
bool frame_pending = 2;
bool animation_finished = 3;
bool scene_finished = 4;
float duration = 5;
string animation_name = 6;
}
message RendererStatusRequest {}
message RendererStatusResponse {
string scene_name = 1;
}
message SceneLocationRequest {}
message SceneLocationResponse {}

View file

@ -0,0 +1,51 @@
syntax = "proto3";
package renderserver;
service RenderServer {
// Used to signal that a new animation is ready to be animated.
rpc AnimationStatus (EmptyRequest) returns (EmptyResponse);
// Used to signal when manim starts and stops.
rpc ManimStatus (ManimStatusRequest) returns (ManimStatusResponse);
rpc UpdateScene (UpdateSceneRequest) returns (UpdateSceneResponse);
rpc NewScene (NewSceneRequest) returns (EmptyResponse);
rpc SceneFinished (EmptyRequest) returns (EmptyResponse);
}
message EmptyRequest {}
message EmptyResponse {}
message NewSceneRequest {
string name = 1;
}
message NewSceneResponse {}
message Animation {
string name = 1;
float duration = 2;
}
message AnimationStatusRequest {}
message AnimationStatusResponse {}
message ManimStatusRequest {
// The name of the scene whose frames are currently being served. This is
// only seen if a javascript renderer is running prior to manim being
// started.
string scene_name = 1;
// Indicates that the current Scene is finished.
bool scene_finished = 2;
// Indicates that the Animations have been updated and should be changed.
repeated Animation animations = 3;
}
message ManimStatusResponse {}
message UpdateSceneRequest {}
message UpdateSceneResponse {}

9
manim/grpc/update_protos.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
python \
-m grpc_tools.protoc \
-I./proto \
--python_out=./gen \
--grpc_python_out=./gen \
./proto/frameserver.proto \
./proto/renderserver.proto

View file

@ -52,6 +52,7 @@ class Mobject(Container):
def __init__(self, **kwargs):
Container.__init__(self, **kwargs)
self.point_hash = None
self.submobjects = []
self.color = Color(self.color)
if self.name is None:

View file

@ -13,8 +13,7 @@ __all__ = [
import itertools as it
import sys
from colour import Color
import colour
from ...constants import *
from ...mobject.mobject import Mobject
@ -211,21 +210,29 @@ class VMobject(Mobject):
self.color_using_background_image(background_image_file)
return self
def get_style(self):
return {
"fill_color": self.get_fill_colors(),
"fill_opacity": self.get_fill_opacities(),
"stroke_color": self.get_stroke_colors(),
"stroke_width": self.get_stroke_width(),
def get_style(self, simple=False):
ret = {
"stroke_opacity": self.get_stroke_opacity(),
"background_stroke_color": self.get_stroke_colors(background=True),
"background_stroke_width": self.get_stroke_width(background=True),
"background_stroke_opacity": self.get_stroke_opacity(background=True),
"sheen_factor": self.get_sheen_factor(),
"sheen_direction": self.get_sheen_direction(),
"background_image_file": self.get_background_image_file(),
"stroke_width": self.get_stroke_width(),
}
if simple:
ret["fill_color"] = colour.rgb2hex(self.get_fill_color().get_rgb())
ret["fill_opacity"] = self.get_fill_opacity()
ret["stroke_color"] = colour.rgb2hex(self.get_stroke_color().get_rgb())
else:
ret["fill_color"] = self.get_fill_colors()
ret["fill_opacity"] = self.get_fill_opacities()
ret["stroke_color"] = self.get_stroke_colors()
ret["background_stroke_color"] = self.get_stroke_colors(background=True)
ret["background_stroke_width"] = self.get_stroke_width(background=True)
ret["background_stroke_opacity"] = self.get_stroke_opacity(background=True)
ret["sheen_factor"] = self.get_sheen_factor()
ret["sheen_direction"] = self.get_sheen_direction()
ret["background_image_file"] = self.get_background_image_file()
return ret
def match_style(self, vmobject, family=True):
self.set_style(**vmobject.get_style(), family=False)
@ -290,7 +297,7 @@ class VMobject(Mobject):
return self.get_fill_opacities()[0]
def get_fill_colors(self):
return [Color(rgb=rgba[:3]) for rgba in self.get_fill_rgbas()]
return [colour.Color(rgb=rgba[:3]) for rgba in self.get_fill_rgbas()]
def get_fill_opacities(self):
return self.get_fill_rgbas()[:, 3]
@ -319,7 +326,9 @@ class VMobject(Mobject):
return self.get_stroke_opacities(background)[0]
def get_stroke_colors(self, background=False):
return [Color(rgb=rgba[:3]) for rgba in self.get_stroke_rgbas(background)]
return [
colour.Color(rgb=rgba[:3]) for rgba in self.get_stroke_rgbas(background)
]
def get_stroke_opacities(self, background=False):
return self.get_stroke_rgbas(background)[:, 3]

145
manim/scene/js_scene.py Normal file
View file

@ -0,0 +1,145 @@
from . import scene
from ..camera.camera import Camera
from ..camera.js_camera import JsCamera
from ..config import config
from ..grpc.gen import renderserver_pb2
from ..grpc.gen import renderserver_pb2_grpc
from ..grpc.impl.frame_server_impl import FrameServer
from ..mobject.mobject import Mobject
from ..constants import DEFAULT_WAIT_TIME
from threading import Event
import copy
import grpc
import inspect
import random
import string
import types
from ..config.logger import logger
def get_random_name(name_map):
while True:
random_name = "".join(random.sample(string.ascii_lowercase, k=10))
if random_name not in name_map:
return random_name
class JsScene(scene.Scene):
def __init__(self, frame_server):
super().__init__(camera_class=JsCamera)
self.frame_server = frame_server
self.start_animation = 0
self.reached_start_animation = Event()
self.animation_finished = Event()
self.renderer_waiting = False
def __deepcopy__(self, memo):
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
for k, v in self.__dict__.items():
if type(v) in [FrameServer, Event, Camera]:
continue
setattr(result, k, copy.deepcopy(v, memo))
# Update updaters
for mobject in self.mobjects:
cloned_updaters = []
for updater in mobject.updaters:
# Make the cloned updater use the cloned Mobjects as free variables
# rather than the original ones.
# TODO: The the same for function calls recursively.
free_variable_map = inspect.getclosurevars(updater).nonlocals
cloned_co_freevars = []
cloned_closure = []
for i, free_variable_name in enumerate(updater.__code__.co_freevars):
free_variable_value = free_variable_map[free_variable_name]
if isinstance(free_variable_value, Mobject):
random_name = get_random_name(free_variable_map)
# Put the cloned Mobject in the function's scope.
free_variable_map[random_name] = memo[id(free_variable_value)]
# Add the cloned Mobject's name to the free variable list.
cloned_co_freevars.append(random_name)
# Add a cell containing the cloned Mobject's reference to the
# closure list.
cloned_closure.append(
types.CellType(memo[id(free_variable_value)])
)
else:
cloned_co_freevars.append(free_variable_name)
cloned_closure.append(updater.__closure__[i])
cloned_updater = types.FunctionType(
updater.__code__.replace(co_freevars=tuple(cloned_co_freevars)),
updater.__globals__,
updater.__name__,
updater.__defaults__,
tuple(cloned_closure),
)
cloned_updaters.append(cloned_updater)
memo[id(mobject)].updaters = cloned_updaters
return result
def render(self):
try:
self.construct()
except scene.EndSceneEarlyException:
pass
self.frame_server.scene_finished = True
with grpc.insecure_channel("localhost:50052") as channel:
stub = renderserver_pb2_grpc.RenderServerStub(channel)
try:
stub.SceneFinished(renderserver_pb2.EmptyRequest())
except grpc._channel._InactiveRpcError as e:
logger.error(e)
def progress_through_animations(self):
self.frame_server.keyframes.append(copy.deepcopy(self))
self.animation_finished.clear()
if self.num_plays == self.start_animation:
with grpc.insecure_channel("localhost:50052") as channel:
stub = renderserver_pb2_grpc.RenderServerStub(channel)
try:
stub.AnimationStatus(renderserver_pb2.EmptyRequest())
except grpc._channel._InactiveRpcError as e:
logger.error(e)
self.animation_finished.wait()
@scene.handle_play_like_call
def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
self.update_mobjects(dt=0) # Any problems with this?
self.animations = []
self.duration = duration
self.stop_condition = stop_condition
self.last_t = 0
self.frame_server.keyframes.append(copy.deepcopy(self))
self.animation_finished.clear()
if self.num_plays == self.start_animation:
with grpc.insecure_channel("localhost:50052") as channel:
stub = renderserver_pb2_grpc.RenderServerStub(channel)
try:
stub.AnimationStatus(renderserver_pb2.EmptyRequest())
except grpc._channel._InactiveRpcError as e:
logger.error(e)
self.animation_finished.wait()
def add_frame(self, serialized_frame, num_frames=1):
dt = 1 / self.camera.frame_rate
self.increment_time(num_frames * dt)
if num_frames != 1:
duration = num_frames / self.camera.frame_rate
else:
duration = 0
return self.camera.serialized_frame, duration
def get_frame(self):
return self.camera.serialized_frame
if config["use_js_renderer"]:
scene.Scene = JsScene

View file

@ -25,6 +25,39 @@ from ..utils.iterables import list_update
from ..utils.hashing import get_hash_from_play_call, get_hash_from_wait_call
def handle_play_like_call(func):
"""
This method is used internally to wrap the
passed function, into a function that
actually writes to the video stream.
Simultaneously, it also adds to the number
of animations played.
Parameters
----------
func : function
The play() like function that has to be
written to the video file stream.
Returns
-------
function
The play() like function that can now write
to the video file stream.
"""
def wrapper(self, *args, **kwargs):
allow_write = not file_writer_config["skip_animations"]
if not self.camera.use_js_renderer:
self.file_writer.begin_animation(allow_write)
func(self, *args, **kwargs)
if not self.camera.use_js_renderer:
self.file_writer.end_animation(allow_write)
self.num_plays += 1
return wrapper
class Scene(Container):
"""A Scene is the canvas of your animation.
@ -65,11 +98,13 @@ class Scene(Container):
def __init__(self, **kwargs):
Container.__init__(self, **kwargs)
self.camera = self.camera_class(**camera_config)
self.file_writer = SceneFileWriter(
self,
**file_writer_config,
)
if not self.camera.use_js_renderer:
self.file_writer = SceneFileWriter(
self,
**file_writer_config,
)
self.play_hashes_list = []
self.mobjects = []
self.original_skipping_status = file_writer_config["skip_animations"]
# TODO, remove need for foreground mobjects
@ -82,10 +117,16 @@ class Scene(Container):
np.random.seed(self.random_seed)
self.setup()
def render(self):
"""
Render this Scene.
"""
try:
self.construct()
except EndSceneEarlyException:
pass
self.tear_down()
# We have to reset these settings in case of multiple renders.
file_writer_config["skip_animations"] = False
@ -204,7 +245,7 @@ class Scene(Container):
self.foreground_mobjects,
)
if background is not None:
self.camera.set_pixel_array(background)
self.camera.set_frame_to_background(background)
else:
self.camera.reset()
@ -684,8 +725,7 @@ class Scene(Container):
ProgressDisplay
The CommandLine Progress Bar.
"""
run_time = self.get_run_time(animations)
time_progression = self.get_time_progression(run_time)
time_progression = self.get_time_progression(self.run_time)
time_progression.set_description(
"".join(
[
@ -868,36 +908,6 @@ class Scene(Container):
return wrapper
def handle_play_like_call(func):
"""
This method is used internally to wrap the
passed function, into a function that
actually writes to the video stream.
Simultaneously, it also adds to the number
of animations played.
Parameters
----------
func : function
The play() like function that has to be
written to the video file stream.
Returns
-------
function
The play() like function that can now write
to the video file stream.
"""
def wrapper(self, *args, **kwargs):
allow_write = not file_writer_config["skip_animations"]
self.file_writer.begin_animation(allow_write)
func(self, *args, **kwargs)
self.file_writer.end_animation(allow_write)
self.num_plays += 1
return wrapper
def begin_animations(self, animations):
"""
This method begins the list of animations that is passed,
@ -914,32 +924,33 @@ class Scene(Container):
# Begin animation
animation.begin()
def progress_through_animations(self, animations):
def progress_through_animations(self):
"""
This method progresses through each animation
in the list passed and and updates the frames as required.
"""
for t in self.get_animation_time_progression(self.animations):
self.update_animation_to_time(t)
self.update_frame(self.moving_mobjects, self.static_image)
self.add_frame(self.get_frame())
def update_animation_to_time(self, t):
"""
Updates the current animation to the specified time.
Parameters
----------
animations : list
List of involved animations.
t : int
Offset from the start of the animation to which to update the current
animation.
"""
# Paint all non-moving objects onto the screen, so they don't
# have to be rendered every frame
moving_mobjects = self.get_moving_mobjects(*animations)
self.update_frame(excluded_mobjects=moving_mobjects)
static_image = self.get_frame()
last_t = 0
for t in self.get_animation_time_progression(animations):
dt = t - last_t
last_t = t
for animation in animations:
animation.update_mobjects(dt)
alpha = t / animation.run_time
animation.interpolate(alpha)
self.update_mobjects(dt)
self.update_frame(moving_mobjects, static_image)
self.add_frames(self.get_frame())
dt = t - self.last_t
self.last_t = t
for animation in self.animations:
animation.update_mobjects(dt)
alpha = t / animation.run_time
animation.interpolate(alpha)
self.update_mobjects(dt)
def finish_animations(self, animations):
"""
@ -977,10 +988,20 @@ class Scene(Container):
if len(args) == 0:
warnings.warn("Called Scene.play with no animations")
return
animations = self.compile_play_args_to_animation_list(*args, **kwargs)
self.begin_animations(animations)
self.progress_through_animations(animations)
self.finish_animations(animations)
self.animations = self.compile_play_args_to_animation_list(*args, **kwargs)
self.begin_animations(self.animations)
# Paint all non-moving objects onto the screen, so they don't
# have to be rendered every frame
self.moving_mobjects = self.get_moving_mobjects(*self.animations)
self.update_frame(excluded_mobjects=self.moving_mobjects)
self.static_image = self.get_frame()
self.last_t = 0
self.run_time = self.get_run_time(self.animations)
self.progress_through_animations()
self.finish_animations(self.animations)
def clean_up_animations(self, *animations):
"""
@ -1073,29 +1094,29 @@ class Scene(Container):
The scene, after waiting.
"""
self.update_mobjects(dt=0) # Any problems with this?
self.animations = []
self.duration = duration
self.stop_condition = stop_condition
self.last_t = 0
if self.should_update_mobjects():
time_progression = self.get_wait_time_progression(duration, stop_condition)
# TODO, be smart about setting a static image
# the same way Scene.play does
last_t = 0
for t in time_progression:
dt = t - last_t
last_t = t
self.update_mobjects(dt)
self.update_animation_to_time(t)
self.update_frame()
self.add_frames(self.get_frame())
self.add_frame(self.get_frame())
if stop_condition is not None and stop_condition():
time_progression.close()
break
elif file_writer_config["skip_animations"]:
elif self.skip_animations:
# Do nothing
return self
else:
self.update_frame()
dt = 1 / self.camera.frame_rate
n_frames = int(duration / dt)
frame = self.get_frame()
self.add_frames(*[frame] * n_frames)
self.add_frame(self.get_frame(), num_frames=int(duration / dt))
return self
def wait_until(self, stop_condition, max_time=60):
@ -1145,20 +1166,22 @@ class Scene(Container):
file_writer_config["skip_animations"] = self.original_skipping_status
return self
def add_frames(self, *frames):
def add_frame(self, frame, num_frames=1):
"""
Adds a frame to the video_file_stream
Parameters
----------
*frames : numpy.ndarray
The frames to add, as pixel arrays.
frame : numpy.ndarray
The frame to add, as a pixel array.
num_frames: int
The number of times to add frame.
"""
dt = 1 / self.camera.frame_rate
self.increment_time(len(frames) * dt)
self.increment_time(num_frames * dt)
if file_writer_config["skip_animations"]:
return
for frame in frames:
for _ in range(num_frames):
self.file_writer.write_frame(frame)
def add_sound(self, sound_file, time_offset=0, gain=None, **kwargs):

View file

@ -366,7 +366,7 @@ class SceneFileWriter(object):
self.update_frame()
n_frames = 1
frame = self.get_frame()
self.add_frames(*[frame] * n_frames)
self.add_frame(*[frame] * n_frames)
b = datetime.datetime.now()
time_diff = (b - a).total_seconds()
frame_duration = 1 / self.scene.camera.frame_rate

View file

@ -10,10 +10,11 @@ __all__ = [
import os
import subprocess as sp
import platform
import numpy as np
import time
import re
import subprocess as sp
def add_extension_if_not_present(file_name, extension):

105
manim/utils/module_ops.py Normal file
View file

@ -0,0 +1,105 @@
from .. import constants
from ..config import file_writer_config
from ..config.logger import logger, console
import importlib.util
import inspect
import os
import sys
import types
def get_module(file_name):
if file_name == "-":
module = types.ModuleType("input_scenes")
logger.info(
"Enter the animation's code & end with an EOF (CTRL+D on Linux/Unix, CTRL+Z on Windows):"
)
code = sys.stdin.read()
if not code.startswith("from manim import"):
logger.warn(
"Didn't find an import statement for Manim. Importing automatically..."
)
code = "from manim import *\n" + code
logger.info("Rendering animation from typed code...")
try:
exec(code, module.__dict__)
return module
except Exception as e:
logger.error(f"Failed to render scene: {str(e)}")
sys.exit(2)
else:
if os.path.exists(file_name):
if file_name[-3:] != ".py":
raise Exception(f"{file_name} is not a valid Manim python script.")
module_name = file_name[:-3].replace(os.sep, ".").split(".")[-1]
spec = importlib.util.spec_from_file_location(module_name, file_name)
module = importlib.util.module_from_spec(spec)
sys.modules[module_name] = module
spec.loader.exec_module(module)
return module
else:
raise FileNotFoundError(f"{file_name} not found")
def get_scene_classes_from_module(module):
from ..scene.scene import Scene
def is_child_scene(obj, module):
return (
inspect.isclass(obj)
and issubclass(obj, Scene)
and obj != Scene
and obj.__module__.startswith(module.__name__)
)
return [
member[1]
for member in inspect.getmembers(module, lambda x: is_child_scene(x, module))
]
def get_scenes_to_render(scene_classes):
if not scene_classes:
logger.error(constants.NO_SCENE_MESSAGE)
return []
if file_writer_config["write_all"]:
return scene_classes
result = []
for scene_name in file_writer_config["scene_names"]:
found = False
for scene_class in scene_classes:
if scene_class.__name__ == scene_name:
result.append(scene_class)
found = True
break
if not found and (scene_name != ""):
logger.error(constants.SCENE_NOT_FOUND_MESSAGE.format(scene_name))
if result:
return result
return (
[scene_classes[0]]
if len(scene_classes) == 1
else prompt_user_for_choice(scene_classes)
)
def prompt_user_for_choice(scene_classes):
num_to_class = {}
for count, scene_class in enumerate(scene_classes):
count += 1 # start with 1 instead of 0
name = scene_class.__name__
console.print(f"{count}: {name}", style="logging.level.info")
num_to_class[count] = scene_class
try:
user_input = console.input(
f"[log.message] {constants.CHOOSE_NUMBER_MESSAGE} [/log.message]"
)
return [
num_to_class[int(num_str)]
for num_str in re.split(r"\s*,\s*", user_input.strip())
]
except KeyError:
logger.error(constants.INVALID_NUMBER_MESSAGE)
sys.exit(2)
except EOFError:
sys.exit(1)

862
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -39,10 +39,15 @@ pycairo = [
{ version = "^1.19", markers = "sys_platform == 'win32'", source="testpypi" },
]
dataclasses = {version = "^0.7", python = "~3.6"}
grpcio = "*"
grpcio-tools = "*"
watchdog = "*"
[tool.poetry.dev-dependencies]
pytest = "^6.0"
pylint = "*"
guzzle_sphinx_theme = "*"
recommonmark = "*"
[tool.poetry.dev-dependencies.black]
version = "^20.8b1"

View file

@ -8,6 +8,9 @@ pydub
pygments
pyreadline; sys_platform == 'win32'
rich>=4.2.1
grpcio
grpcio-tools
watchdog
# for development only:
# pytest

View file

@ -77,6 +77,7 @@ class GraphicalUnitTester:
# By invoking this, the scene is rendered.
self.scene = scene_object()
self.scene.render()
def _load_data(self):
"""Load the np.array of the last frame of a pre-rendered scene. If not found, throw FileNotFoundError.