mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
* [pre-commit.ci] pre-commit autoupdate updates: - [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.6.0) - [github.com/pycqa/isort: 5.12.0 → 5.13.2](https://github.com/pycqa/isort/compare/5.12.0...5.13.2) - [github.com/asottile/pyupgrade: v3.10.1 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.15.2) - [github.com/psf/black: 23.7.0 → 24.4.0](https://github.com/psf/black/compare/23.7.0...24.4.0) - [github.com/asottile/blacken-docs: 1.15.0 → 1.16.0](https://github.com/asottile/blacken-docs/compare/1.15.0...1.16.0) - [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0) - [github.com/pre-commit/mirrors-mypy: v1.5.1 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.1...v1.9.0) - [github.com/codespell-project/codespell: v2.2.5 → v2.2.6](https://github.com/codespell-project/codespell/compare/v2.2.5...v2.2.6) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * make smoothererstep readable again, avoid overlong line * zoom_value more readable * fix blacken-docs touching .github * fix codespell setup, remove unnecessary file, fix some typos * flake8: ignore E704, triggered by overload * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update docs/source/tutorials/quickstart.rst * more flake fixes * try to make blacken-docs happy --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
526 lines
15 KiB
Python
526 lines
15 KiB
Python
from pathlib import Path
|
|
|
|
import manim.utils.opengl as opengl
|
|
from manim import *
|
|
from manim.opengl import * # type: ignore
|
|
|
|
# Copied from https://3b1b.github.io/manim/getting_started/example_scenes.html#surfaceexample.
|
|
# Lines that do not yet work with the Community Version are commented.
|
|
|
|
|
|
def get_plane_mesh(context):
|
|
shader = Shader(context, name="vertex_colors")
|
|
attributes = np.zeros(
|
|
18,
|
|
dtype=[
|
|
("in_vert", np.float32, (4,)),
|
|
("in_color", np.float32, (4,)),
|
|
],
|
|
)
|
|
attributes["in_vert"] = np.array(
|
|
[
|
|
# xy plane
|
|
[-1, -1, 0, 1],
|
|
[-1, 1, 0, 1],
|
|
[1, 1, 0, 1],
|
|
[-1, -1, 0, 1],
|
|
[1, -1, 0, 1],
|
|
[1, 1, 0, 1],
|
|
# yz plane
|
|
[0, -1, -1, 1],
|
|
[0, -1, 1, 1],
|
|
[0, 1, 1, 1],
|
|
[0, -1, -1, 1],
|
|
[0, 1, -1, 1],
|
|
[0, 1, 1, 1],
|
|
# xz plane
|
|
[-1, 0, -1, 1],
|
|
[-1, 0, 1, 1],
|
|
[1, 0, 1, 1],
|
|
[-1, 0, -1, 1],
|
|
[1, 0, -1, 1],
|
|
[1, 0, 1, 1],
|
|
],
|
|
)
|
|
attributes["in_color"] = np.array(
|
|
[
|
|
# xy plane
|
|
[1, 0, 0, 1],
|
|
[1, 0, 0, 1],
|
|
[1, 0, 0, 1],
|
|
[1, 0, 0, 1],
|
|
[1, 0, 0, 1],
|
|
[1, 0, 0, 1],
|
|
# yz plane
|
|
[0, 1, 0, 1],
|
|
[0, 1, 0, 1],
|
|
[0, 1, 0, 1],
|
|
[0, 1, 0, 1],
|
|
[0, 1, 0, 1],
|
|
[0, 1, 0, 1],
|
|
# xz plane
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
],
|
|
)
|
|
return Mesh(shader, attributes)
|
|
|
|
|
|
class TextTest(Scene):
|
|
def construct(self):
|
|
import string
|
|
|
|
text = Text(string.ascii_lowercase, stroke_width=4, stroke_color=BLUE).scale(2)
|
|
text2 = (
|
|
Text(string.ascii_uppercase, stroke_width=4, stroke_color=BLUE)
|
|
.scale(2)
|
|
.next_to(text, DOWN)
|
|
)
|
|
# self.add(text, text2)
|
|
self.play(Write(text))
|
|
self.play(Write(text2))
|
|
self.interactive_embed()
|
|
|
|
|
|
class GuiTest(Scene):
|
|
def construct(self):
|
|
mesh = get_plane_mesh(self.renderer.context)
|
|
# mesh.attributes["in_vert"][:, 0]
|
|
self.add(mesh)
|
|
|
|
def update_mesh(mesh, dt):
|
|
mesh.model_matrix = np.matmul(
|
|
opengl.rotation_matrix(z=dt),
|
|
mesh.model_matrix,
|
|
)
|
|
|
|
mesh.add_updater(update_mesh)
|
|
|
|
self.interactive_embed()
|
|
|
|
|
|
class GuiTest2(Scene):
|
|
def construct(self):
|
|
mesh = get_plane_mesh(self.renderer.context)
|
|
mesh.attributes["in_vert"][:, 0] -= 2
|
|
self.add(mesh)
|
|
|
|
mesh2 = get_plane_mesh(self.renderer.context)
|
|
mesh2.attributes["in_vert"][:, 0] += 2
|
|
self.add(mesh2)
|
|
|
|
def callback(sender, data):
|
|
mesh2.attributes["in_color"][:, 3] = dpg.get_value(sender)
|
|
|
|
self.widgets.append(
|
|
{
|
|
"name": "mesh2 opacity",
|
|
"widget": "slider_float",
|
|
"callback": callback,
|
|
"min_value": 0,
|
|
"max_value": 1,
|
|
"default_value": 1,
|
|
},
|
|
)
|
|
|
|
self.interactive_embed()
|
|
|
|
|
|
class ThreeDMobjectTest(Scene):
|
|
def construct(self):
|
|
# config["background_color"] = "#333333"
|
|
|
|
s = Square(fill_opacity=0.5).shift(2 * RIGHT)
|
|
self.add(s)
|
|
|
|
sp = Sphere().shift(2 * LEFT)
|
|
self.add(sp)
|
|
|
|
mesh = get_plane_mesh(self.renderer.context)
|
|
self.add(mesh)
|
|
|
|
def update_mesh(mesh, dt):
|
|
mesh.model_matrix = np.matmul(
|
|
opengl.rotation_matrix(z=dt),
|
|
mesh.model_matrix,
|
|
)
|
|
|
|
mesh.add_updater(update_mesh)
|
|
|
|
self.interactive_embed()
|
|
|
|
|
|
class NamedFullScreenQuad(Scene):
|
|
def construct(self):
|
|
surface = FullScreenQuad(self.renderer.context, fragment_shader_name="design_3")
|
|
surface.shader.set_uniform(
|
|
"u_resolution",
|
|
(config["pixel_width"], config["pixel_height"], 0.0),
|
|
)
|
|
surface.shader.set_uniform("u_time", 0)
|
|
self.add(surface)
|
|
|
|
t = 0
|
|
|
|
def update_surface(surface, dt):
|
|
nonlocal t
|
|
t += dt
|
|
surface.shader.set_uniform("u_time", t / 4)
|
|
|
|
surface.add_updater(update_surface)
|
|
|
|
# self.wait()
|
|
self.interactive_embed()
|
|
|
|
|
|
class InlineFullScreenQuad(Scene):
|
|
def construct(self):
|
|
surface = FullScreenQuad(
|
|
self.renderer.context,
|
|
"""
|
|
#version 330
|
|
|
|
|
|
#define TWO_PI 6.28318530718
|
|
|
|
uniform vec2 u_resolution;
|
|
uniform float u_time;
|
|
out vec4 frag_color;
|
|
|
|
// Function from Iñigo Quiles
|
|
// https://www.shadertoy.com/view/MsS3Wc
|
|
vec3 hsb2rgb( in vec3 c ){
|
|
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
|
|
6.0)-3.0)-1.0,
|
|
0.0,
|
|
1.0 );
|
|
rgb = rgb*rgb*(3.0-2.0*rgb);
|
|
return c.z * mix( vec3(1.0), rgb, c.y);
|
|
}
|
|
|
|
void main(){
|
|
vec2 st = gl_FragCoord.xy/u_resolution;
|
|
vec3 color = vec3(0.0);
|
|
|
|
// Use polar coordinates instead of cartesian
|
|
vec2 toCenter = vec2(0.5)-st;
|
|
float angle = atan(toCenter.y,toCenter.x);
|
|
angle += u_time;
|
|
float radius = length(toCenter)*2.0;
|
|
|
|
// Map the angle (-PI to PI) to the Hue (from 0 to 1)
|
|
// and the Saturation to the radius
|
|
color = hsb2rgb(vec3((angle/TWO_PI)+0.5,radius,1.0));
|
|
|
|
frag_color = vec4(color,1.0);
|
|
}
|
|
""",
|
|
)
|
|
surface.shader.set_uniform(
|
|
"u_resolution",
|
|
(config["pixel_width"], config["pixel_height"]),
|
|
)
|
|
shader_time = 0
|
|
|
|
def update_surface(surface):
|
|
nonlocal shader_time
|
|
surface.shader.set_uniform("u_time", shader_time)
|
|
shader_time += 1 / 60.0
|
|
|
|
surface.add_updater(update_surface)
|
|
self.add(surface)
|
|
# self.wait(5)
|
|
self.interactive_embed()
|
|
|
|
|
|
class SimpleInlineFullScreenQuad(Scene):
|
|
def construct(self):
|
|
surface = FullScreenQuad(
|
|
self.renderer.context,
|
|
"""
|
|
#version 330
|
|
|
|
uniform float v_red;
|
|
uniform float v_green;
|
|
uniform float v_blue;
|
|
out vec4 frag_color;
|
|
|
|
void main() {
|
|
frag_color = vec4(v_red, v_green, v_blue, 1);
|
|
}
|
|
""",
|
|
)
|
|
surface.shader.set_uniform("v_red", 0)
|
|
surface.shader.set_uniform("v_green", 0)
|
|
surface.shader.set_uniform("v_blue", 0)
|
|
|
|
increase = True
|
|
val = 0.5
|
|
surface.shader.set_uniform("v_red", val)
|
|
surface.shader.set_uniform("v_green", val)
|
|
surface.shader.set_uniform("v_blue", val)
|
|
|
|
def update_surface(mesh, dt):
|
|
nonlocal increase
|
|
nonlocal val
|
|
if increase:
|
|
val += dt
|
|
else:
|
|
val -= dt
|
|
if val >= 1:
|
|
increase = False
|
|
elif val <= 0:
|
|
increase = True
|
|
surface.shader.set_uniform("v_red", val)
|
|
surface.shader.set_uniform("v_green", val)
|
|
surface.shader.set_uniform("v_blue", val)
|
|
|
|
surface.add_updater(update_surface)
|
|
|
|
self.add(surface)
|
|
self.wait(5)
|
|
|
|
|
|
class InlineShaderExample(Scene):
|
|
def construct(self):
|
|
config["background_color"] = "#333333"
|
|
|
|
c = Circle(fill_opacity=0.7).shift(UL)
|
|
self.add(c)
|
|
|
|
shader = Shader(
|
|
self.renderer.context,
|
|
source={
|
|
"vertex_shader": """
|
|
#version 330
|
|
|
|
in vec4 in_vert;
|
|
in vec4 in_color;
|
|
out vec4 v_color;
|
|
uniform mat4 u_model_view_matrix;
|
|
uniform mat4 u_projection_matrix;
|
|
|
|
void main() {
|
|
v_color = in_color;
|
|
vec4 camera_space_vertex = u_model_view_matrix * in_vert;
|
|
vec4 clip_space_vertex = u_projection_matrix * camera_space_vertex;
|
|
gl_Position = clip_space_vertex;
|
|
}
|
|
""",
|
|
"fragment_shader": """
|
|
#version 330
|
|
|
|
in vec4 v_color;
|
|
out vec4 frag_color;
|
|
|
|
void main() {
|
|
frag_color = v_color;
|
|
}
|
|
""",
|
|
},
|
|
)
|
|
shader.set_uniform("u_model_view_matrix", opengl.view_matrix())
|
|
shader.set_uniform(
|
|
"u_projection_matrix",
|
|
opengl.orthographic_projection_matrix(),
|
|
)
|
|
|
|
attributes = np.zeros(
|
|
6,
|
|
dtype=[
|
|
("in_vert", np.float32, (4,)),
|
|
("in_color", np.float32, (4,)),
|
|
],
|
|
)
|
|
attributes["in_vert"] = np.array(
|
|
[
|
|
[-1, -1, 0, 1],
|
|
[-1, 1, 0, 1],
|
|
[1, 1, 0, 1],
|
|
[-1, -1, 0, 1],
|
|
[1, -1, 0, 1],
|
|
[1, 1, 0, 1],
|
|
],
|
|
)
|
|
attributes["in_color"] = np.array(
|
|
[
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
[0, 0, 1, 1],
|
|
],
|
|
)
|
|
mesh = Mesh(shader, attributes)
|
|
self.add(mesh)
|
|
|
|
self.wait(5)
|
|
# self.embed_2()
|
|
|
|
|
|
class NamedShaderExample(Scene):
|
|
def construct(self):
|
|
shader = Shader(self.renderer.context, "manim_coords")
|
|
shader.set_uniform("u_color", (0.0, 1.0, 0.0, 1.0))
|
|
|
|
view_matrix = self.camera.formatted_view_matrix
|
|
shader.set_uniform("u_model_view_matrix", view_matrix)
|
|
shader.set_uniform(
|
|
"u_projection_matrix",
|
|
opengl.perspective_projection_matrix(),
|
|
)
|
|
attributes = np.zeros(
|
|
6,
|
|
dtype=[
|
|
("in_vert", np.float32, (4,)),
|
|
],
|
|
)
|
|
attributes["in_vert"] = np.array(
|
|
[
|
|
[-1, -1, 0, 1],
|
|
[-1, 1, 0, 1],
|
|
[1, 1, 0, 1],
|
|
[-1, -1, 0, 1],
|
|
[1, -1, 0, 1],
|
|
[1, 1, 0, 1],
|
|
],
|
|
)
|
|
mesh = Mesh(shader, attributes)
|
|
self.add(mesh)
|
|
|
|
self.wait(5)
|
|
|
|
|
|
class InteractiveDevelopment(Scene):
|
|
def construct(self):
|
|
circle = Circle()
|
|
circle.set_fill(BLUE, opacity=0.5)
|
|
circle.set_stroke(BLUE_E, width=4)
|
|
square = Square()
|
|
|
|
self.play(Create(square))
|
|
self.wait()
|
|
|
|
# This opens an iPython terminal where you can keep writing
|
|
# lines as if they were part of this construct method.
|
|
# In particular, 'square', 'circle' and 'self' will all be
|
|
# part of the local namespace in that terminal.
|
|
# self.embed()
|
|
|
|
# Try copying and pasting some of the lines below into
|
|
# the interactive shell
|
|
self.play(ReplacementTransform(square, circle))
|
|
self.wait()
|
|
self.play(circle.animate.stretch(4, 0))
|
|
self.play(Rotate(circle, 90 * DEGREES))
|
|
self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
|
|
|
|
# text = Text(
|
|
# """
|
|
# In general, using the interactive shell
|
|
# is very helpful when developing new scenes
|
|
# """
|
|
# )
|
|
# self.play(Write(text))
|
|
|
|
# # In the interactive shell, you can just type
|
|
# # play, add, remove, clear, wait, save_state and restore,
|
|
# # instead of self.play, self.add, self.remove, etc.
|
|
|
|
# # To interact with the window, type touch(). You can then
|
|
# # scroll in the window, or zoom by holding down 'z' while scrolling,
|
|
# # and change camera perspective by holding down 'd' while moving
|
|
# # the mouse. Press 'r' to reset to the standard camera position.
|
|
# # Press 'q' to stop interacting with the window and go back to
|
|
# # typing new commands into the shell.
|
|
|
|
# # In principle you can customize a scene to be responsive to
|
|
# # mouse and keyboard interactions
|
|
# always(circle.move_to, self.mouse_point)
|
|
|
|
|
|
class SurfaceExample(Scene):
|
|
def construct(self):
|
|
# surface_text = Text("For 3d scenes, try using surfaces")
|
|
# surface_text.fix_in_frame()
|
|
# surface_text.to_edge(UP)
|
|
# self.add(surface_text)
|
|
# self.wait(0.1)
|
|
|
|
torus1 = Torus(major_radius=1, minor_radius=1)
|
|
torus2 = Torus(major_radius=3, minor_radius=1)
|
|
sphere = Sphere(radius=3, resolution=torus1.resolution)
|
|
# You can texture a surface with up to two images, which will
|
|
# be interpreted as the side towards the light, and away from
|
|
# the light. These can be either urls, or paths to a local file
|
|
# in whatever you've set as the image directory in
|
|
# the custom_config.yml file
|
|
|
|
script_location = Path(__file__).resolve().parent
|
|
day_texture = (
|
|
script_location / "assets" / "1280px-Whole_world_-_land_and_oceans.jpg"
|
|
)
|
|
night_texture = script_location / "assets" / "1280px-The_earth_at_night.jpg"
|
|
|
|
surfaces = [
|
|
OpenGLTexturedSurface(surface, day_texture, night_texture)
|
|
for surface in [sphere, torus1, torus2]
|
|
]
|
|
|
|
for mob in surfaces:
|
|
mob.shift(IN)
|
|
mob.mesh = OpenGLSurfaceMesh(mob)
|
|
mob.mesh.set_stroke(BLUE, 1, opacity=0.5)
|
|
|
|
# Set perspective
|
|
frame = self.renderer.camera
|
|
frame.set_euler_angles(
|
|
theta=-30 * DEGREES,
|
|
phi=70 * DEGREES,
|
|
)
|
|
|
|
surface = surfaces[0]
|
|
|
|
self.play(
|
|
FadeIn(surface),
|
|
Create(surface.mesh, lag_ratio=0.01, run_time=3),
|
|
)
|
|
for mob in surfaces:
|
|
mob.add(mob.mesh)
|
|
surface.save_state()
|
|
self.play(Rotate(surface, PI / 2), run_time=2)
|
|
for mob in surfaces[1:]:
|
|
mob.rotate(PI / 2)
|
|
|
|
self.play(Transform(surface, surfaces[1]), run_time=3)
|
|
|
|
self.play(
|
|
Transform(surface, surfaces[2]),
|
|
# Move camera frame during the transition
|
|
frame.animate.increment_phi(-10 * DEGREES),
|
|
frame.animate.increment_theta(-20 * DEGREES),
|
|
run_time=3,
|
|
)
|
|
# Add ambient rotation
|
|
frame.add_updater(lambda m, dt: m.increment_theta(-0.1 * dt))
|
|
|
|
# Play around with where the light is
|
|
# light_text = Text("You can move around the light source")
|
|
# light_text.move_to(surface_text)
|
|
# light_text.fix_in_frame()
|
|
|
|
# self.play(FadeTransform(surface_text, light_text))
|
|
light = self.camera.light_source
|
|
self.add(light)
|
|
light.save_state()
|
|
self.play(light.animate.move_to(3 * IN), run_time=5)
|
|
self.play(light.animate.shift(10 * OUT), run_time=5)
|
|
|
|
# drag_text = Text("Try moving the mouse while pressing d or s")
|
|
# drag_text.move_to(light_text)
|
|
# drag_text.fix_in_frame()
|