{ "cells": [ { "cell_type": "markdown", "id": "substantial-impact", "metadata": {}, "source": [ "# Vítej!" ] }, { "cell_type": "markdown", "id": "first-armenia", "metadata": {}, "source": [ "Tento dokument obsahuje zdrojové kódy animací ke třetí sérii seriálu KSP. Před spouštěním opět nezapomeň Manim importovat spuštěním následujícího řádku!" ] }, { "cell_type": "code", "execution_count": null, "id": "e03b150c", "metadata": {}, "outputs": [], "source": [ "from manim import *" ] }, { "cell_type": "markdown", "id": "45897e9c", "metadata": {}, "source": [ "## `save` a `restore`" ] }, { "cell_type": "code", "execution_count": null, "id": "6aa5ba0f", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh SaveAndRestoreExample\n", "\n", "\n", "class SaveAndRestoreExample(Scene):\n", " def construct(self):\n", " square = Square()\n", "\n", " # uložení toho, jak aktualně čtverec vypadá\n", " square.save_state()\n", "\n", " self.play(Write(square))\n", "\n", " self.play(square.animate.set_fill(WHITE, 1))\n", " self.play(square.animate.scale(1.5).rotate(PI / 4))\n", " self.play(square.animate.set_color(BLUE))\n", "\n", " # navrácení do původního stavu\n", " self.play(square.animate.restore())\n", "\n", " # nová animace!\n", " self.play(Unwrite(square))" ] }, { "cell_type": "markdown", "id": "88f6f285", "metadata": {}, "source": [ "## Grafy" ] }, { "cell_type": "code", "execution_count": null, "id": "0906f798", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh GraphExample\n", "\n", "\n", "class GraphExample(Scene):\n", " def construct(self):\n", " # graf očekává vrcholy a hrany v tomto tvaru\n", " vertices = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n", " edges = [\n", " (1, 2),\n", " (2, 3),\n", " (3, 4),\n", " (2, 4),\n", " (2, 5),\n", " (6, 5),\n", " (1, 7),\n", " (5, 7),\n", " (2, 8),\n", " (1, 9),\n", " (10, 8),\n", " (5, 11),\n", " ]\n", "\n", " # layout_config používáme k tomu, aby byl algoritmus nastavující pozice vrcholů\n", " # deterministický (ten defaultní potřebuje seed)\n", " g = Graph(vertices, edges, layout_config={\"seed\": 0}).scale(1.6)\n", "\n", " self.play(Write(g))\n", "\n", " # graf obsahuje updatery, aby se hrany posouvaly s vrcholy\n", " self.play(g.vertices[6].animate.shift((LEFT + DOWN) * 0.5))\n", "\n", " self.play(g.animate.shift(LEFT * 3))\n", "\n", " # grafy mohou rovněž obsahovat labely a mohou být uspořádány do jiných layoutů\n", " # (pro ukázku všech viz link na dokumentaci třídy v seriálu)\n", " h = Graph(vertices, edges, labels=True, layout=\"circular\").shift(RIGHT * 3)\n", "\n", " self.play(Write(h))\n", "\n", " # pokud chceme větší vrcholy i bez labelů, tak si je musíme obstarat manuálně\n", " self.play(*[g.vertices[v].animate.scale(2.15) for v in g.vertices])\n", "\n", " # obarvíme vrchol 5 a jemu odpovídající hrany (může se hodit v jedné z úloh :)\n", " v = 5\n", " self.play(\n", " Flash(g.vertices[v], color=RED, flash_radius=0.5),\n", " g.vertices[v].animate.set_color(RED),\n", " *[g.edges[e].animate.set_color(RED) for e in g.edges if v in e],\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "98df83d3", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh GraphGenerationExample\n", "\n", "from random import *\n", "import networkx as nx\n", "\n", "\n", "class GraphGenerationExample(Scene):\n", " def construct(self):\n", " seed(0xDEADBEEF)\n", "\n", " n = 12 # počet vrcholů\n", " p = 3 / n # pravděpodobnost, že mezi dvěma vrcholy je hrana\n", "\n", " # generujeme, dokud nemáme spojitý graf\n", " graph = None\n", " while graph is None or not nx.is_connected(graph):\n", " graph = nx.generators.random_graphs.gnp_random_graph(n, p)\n", "\n", " g = (\n", " Graph(graph.nodes, graph.edges, layout_config={\"seed\": 0})\n", " .scale(2.2)\n", " .rotate(-PI / 2)\n", " )\n", "\n", " self.play(Write(g))" ] }, { "cell_type": "markdown", "id": "bb5423f4", "metadata": {}, "source": [ "## Kamera" ] }, { "cell_type": "code", "execution_count": null, "id": "ab85ca2d", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh MovingCameraExample\n", "\n", "\n", "class MovingCameraExample(MovingCameraScene):\n", " def construct(self):\n", " square = Square()\n", "\n", " self.play(Write(square))\n", "\n", " # uložíme si stav kamery, protože s ní následně budeme pracovat\n", " self.camera.frame.save_state()\n", "\n", " # zoomneme tak, aby čtverec zaplňoval celý obraz (+ drobná mezera)\n", " self.play(self.camera.frame.animate.set_height(square.height * 1.5))\n", "\n", " circle = Circle().next_to(square, LEFT)\n", "\n", " # posunutí kamery k novému objektu (kruhu)\n", " self.play(\n", " AnimationGroup(\n", " self.camera.frame.animate.move_to(circle),\n", " Write(circle),\n", " lag_ratio=0.5,\n", " )\n", " )\n", "\n", " self.wait(0.5)\n", "\n", " # trošku odzoomujeme (zvětšení bude zabírat více scény)\n", " self.play(self.camera.frame.animate.scale(1.3))\n", "\n", " triangle = Triangle().next_to(square, RIGHT)\n", "\n", " # posunutí kamery k novému objektu (trojúhelníku)\n", " self.play(\n", " AnimationGroup(\n", " self.camera.frame.animate.move_to(triangle),\n", " Write(triangle),\n", " lag_ratio=0.5,\n", " )\n", " )\n", "\n", " self.wait(0.5)\n", "\n", " # navrácení kamery do původního stavu\n", " self.play(self.camera.frame.animate.restore())" ] }, { "cell_type": "code", "execution_count": null, "id": "02c31d55", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh MovingCameraUpdaterExample\n", "\n", "from random import seed, uniform\n", "\n", "\n", "class MovingCameraUpdaterExample(MovingCameraScene):\n", " def construct(self):\n", " seed(0xDEADBEEF)\n", "\n", " n = 11 ** 2\n", "\n", " circles = VGroup(\n", " *[\n", " Circle(radius=0.1)\n", " .scale(uniform(0.5, 2))\n", " .shift(UP * uniform(-3, 3) + RIGHT * uniform(-5, 5))\n", " .set_color(WHITE)\n", " for _ in range(n)\n", " ]\n", " )\n", "\n", " # kamerou budeme pozorovat kruh, který je v půlce\n", " target = circles[n // 2]\n", "\n", " def update_curve(camera):\n", " \"\"\"Updater, který udržuje kameru nad cílem.\"\"\"\n", " camera.move_to(target.get_center())\n", "\n", " self.camera.frame.add_updater(update_curve)\n", "\n", " # POZOR!\n", " # updatery fungují pouze na věci přidané na scénu\n", " # self.camera.frame na scéně nejprve není, je ho potřeba přidat\n", " self.add(self.camera.frame)\n", "\n", " self.play(FadeIn(circles))\n", "\n", " # animace pozicování kružnic a postupné Zoomování\n", " scale_factor = 0.7\n", "\n", " self.play(\n", " circles.animate.arrange_in_grid().set_color(RED),\n", " self.camera.frame.animate.scale(scale_factor),\n", " run_time=1.5,\n", " )\n", "\n", " self.play(\n", " circles.animate.arrange_in_grid(rows=5).set_color(GREEN),\n", " self.camera.frame.animate.scale(scale_factor),\n", " run_time=1.5,\n", " )\n", "\n", " self.play(\n", " circles.animate.arrange_in_grid(cols=14).set_color(BLUE),\n", " self.camera.frame.animate.scale(scale_factor),\n", " run_time=1.5,\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "6cea874e", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh BackgroundColorExample\n", "\n", "\n", "class BackgroundColorExample(MovingCameraScene):\n", " def construct(self):\n", " self.camera.background_color = WHITE\n", " self.camera.frame.scale(0.6)\n", "\n", " square = Square(color=BLACK)\n", "\n", " self.play(Write(square))\n", "\n", " circle = Circle(color=BLACK).next_to(square, LEFT)\n", " triangle = Triangle(color=BLACK).next_to(square, RIGHT)\n", "\n", " self.play(FadeIn(triangle, shift=RIGHT * 0.2), FadeIn(circle, shift=LEFT * 0.2))" ] }, { "cell_type": "markdown", "id": "36661d3b", "metadata": {}, "source": [ "## Rate funkce" ] }, { "cell_type": "code", "execution_count": null, "id": "6ad791dc", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh RateFunctionsExample\n", "\n", "# Kód pochází z Manimové dokumentace (modulo drobné úpravy)\n", "# https://docs.manim.community/en/stable/reference/manim.utils.rate_functions.html\n", "\n", "\n", "class RateFunctionsExample(Scene):\n", " def construct(self):\n", " line1 = Line(3 * LEFT, RIGHT).set_color(RED)\n", " line2 = Line(3 * LEFT, RIGHT).set_color(GREEN)\n", " line3 = Line(3 * LEFT, RIGHT).set_color(BLUE)\n", " line4 = Line(3 * LEFT, RIGHT).set_color(ORANGE)\n", "\n", " lines = VGroup(line1, line2, line3, line4).arrange(DOWN, buff=0.8).move_to(LEFT * 2)\n", "\n", " dot1 = Dot().move_to(line1.get_start())\n", " dot2 = Dot().move_to(line2.get_start())\n", " dot3 = Dot().move_to(line3.get_start())\n", " dot4 = Dot().move_to(line4.get_start())\n", "\n", " dots = VGroup(dot1, dot2, dot3, dot4)\n", "\n", " # pozor na podtržítka při psaní TeXu\n", " label1 = Tex(\"smooth (default)\").next_to(line1, RIGHT, buff=0.5)\n", " label2 = Tex(\"linear\").next_to(line2, RIGHT, buff=0.5)\n", " label3 = Tex(\"there\\_and\\_back\").next_to(line3, RIGHT, buff=0.5)\n", " label4 = Tex(\"rush\\_into\").next_to(line4, RIGHT, buff=0.5)\n", "\n", " labels = VGroup(label1, label2, label3, label4)\n", "\n", " self.play(Write(lines), FadeIn(dots), FadeIn(labels))\n", "\n", " # použití s animate syntaxem\n", " self.play(\n", " dot1.animate(rate_func=smooth).shift(RIGHT * 4),\n", " dot2.animate(rate_func=linear).shift(RIGHT * 4),\n", " dot3.animate(rate_func=there_and_back).shift(RIGHT * 4),\n", " dot4.animate(rate_func=rush_into).shift(RIGHT * 4),\n", " run_time=3,\n", " )\n", " \n", " self.play(FadeOut(lines), FadeOut(dots))\n", " \n", " # použití s normálními animacemi\n", " self.play(\n", " Write(line1, rate_func=smooth),\n", " Write(line2, rate_func=linear),\n", " Write(line3, rate_func=there_and_back),\n", " Write(line4, rate_func=rush_into),\n", " run_time=3,\n", " )" ] }, { "cell_type": "code", "execution_count": null, "id": "26af99f1", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh RateFunctionList\n", "\n", "# Kód pochází z Manimové dokumentace (modulo drobné úpravy)\n", "# https://docs.manim.community/en/stable/reference/manim.utils.rate_functions.html\n", "\n", "\n", "class RateFunctionList(Scene):\n", " def construct(self):\n", " graphs = VGroup()\n", " for k, v in rate_functions.__dict__.items():\n", " if \"function\" in str(v):\n", " try:\n", " rate_func = v\n", " plot = (\n", " ParametricFunction(\n", " lambda x: [x, rate_func(x), 0],\n", " t_range=[0, 1, .01],\n", " use_smoothing=False,\n", " color=YELLOW,\n", " )\n", " .stretch_to_fit_width(1.5)\n", " .stretch_to_fit_height(1)\n", " )\n", " plot_bg = SurroundingRectangle(plot).set_color(WHITE)\n", " plot_title = (\n", " Text(rate_func.__name__, weight=BOLD)\n", " .scale(0.5)\n", " .next_to(plot_bg, UP, buff=0.1)\n", " )\n", " graphs.add(VGroup(plot_bg, plot, plot_title))\n", " except: # některé křivky jsou parametrické (a některé rozbité)\n", " pass\n", " graphs.arrange_in_grid(cols=8)\n", " graphs.height = config.frame_height\n", " graphs.width = config.frame_width\n", " graphs.move_to(ORIGIN).scale(0.9)\n", " \n", " text = Tex(f\"Manim rate functions (v0.13.1)\").scale(1.4).next_to(graphs, UP, buff=0.5)\n", " \n", " self.add(VGroup(graphs, text).move_to(ORIGIN))" ] }, { "cell_type": "markdown", "id": "fd7a5d4c", "metadata": {}, "source": [ "## Krokování" ] }, { "cell_type": "code", "execution_count": null, "id": "f46b6587", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh --save_sections NextSectionExample\n", "\n", "class NextSectionExample(Scene):\n", " def construct(self):\n", " shapes = VGroup(Circle(), Square(), Triangle()).arrange()\n", " \n", " # jméno sekce je nepovinné a nemusí být unikátní\n", " # skip_animations animování sekce přeskočí\n", " self.next_section(\"vykreslení kruhu\", skip_animations=True)\n", " \n", " self.play(Write(shapes[0]))\n", " \n", " self.next_section(\"vykreslení čtverce\")\n", " \n", " self.play(Write(shapes[1]))\n", " \n", " self.next_section(\"vykreslení trojúhelníku\")\n", " \n", " self.play(Write(shapes[2]))" ] }, { "cell_type": "markdown", "id": "98daca2b", "metadata": {}, "source": [ "## Kostry úloh" ] }, { "cell_type": "markdown", "id": "8746ae11", "metadata": {}, "source": [ "### Grafový algoritmus [5b]" ] }, { "cell_type": "code", "execution_count": null, "id": "18fb5e35", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh GraphAlgorithm\n", "\n", "\n", "class GraphAlgorithm(Scene):\n", " def construct(self):\n", " pass" ] }, { "cell_type": "markdown", "id": "4de93509", "metadata": {}, "source": [ "### Fibonacciho posloupnost [5b]" ] }, { "cell_type": "code", "execution_count": null, "id": "49bb1f67", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh --disable_caching FibonacciSequence\n", "\n", "\n", "class FibonacciSequence(MovingCameraScene):\n", " def construct(self):\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "id": "1d647e5f", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh --disable_caching TracePathExample\n", "\n", " \n", "class TracePathExample(Scene):\n", " def construct(self):\n", " dot = Dot().shift(LEFT)\n", " \n", " self.play(Write(dot))\n", " \n", " # objekt TracedPath přijímá funkci, které se opakovaně ptá na pozici objektu, který sleduje\n", " # proto používáme dot.get_center, která vrací aktuální pozici tečky kterou tracujeme\n", " path = TracedPath(dot.get_center)\n", " \n", " # nesmíme zapomenout cestu přidat do scény, aby byla vykreslována!\n", " self.add(path)\n", " \n", " self.play(Rotate(dot, about_point=ORIGIN))\n", " \n", " self.play(dot.animate.shift(UP))\n", " self.play(dot.animate.shift(LEFT * 2))\n", " self.play(dot.animate.shift(DOWN))\n", " \n", " # pro zkončení tracování můžeme použít clear_updaters\n", " path.clear_updaters()\n", " \n", " self.play(dot.animate.shift(RIGHT * 2))" ] }, { "cell_type": "markdown", "id": "159f8abb", "metadata": {}, "source": [ "### Langtonův mravenec [5b]" ] }, { "cell_type": "code", "execution_count": null, "id": "d95d2328", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh LangtonAnt\n", "\n", "\n", "class LangtonAnt(MovingCameraScene):\n", " def construct(self):\n", " pass" ] }, { "cell_type": "code", "execution_count": null, "id": "386b5d61", "metadata": {}, "outputs": [], "source": [ "%%manim -v WARNING -qh SVGExample\n", "\n", "\n", "class SVGExample(Scene):\n", " def construct(self):\n", " image = SVGMobject(\"ant.svg\")\n", "\n", " self.play(Write(image))\n", "\n", " self.play(image.animate.set_color(RED).scale(1.75))\n", "\n", " self.play(Rotate(image, TAU)) # tau = 2 pi\n", "\n", " self.play(FadeOut(image))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.1" } }, "nbformat": 4, "nbformat_minor": 5 }