Zdrojové kódy k příkladům a úlohám 34. série KSP (Manim).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

631 lines
19 KiB

{
"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
}