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.
 

508 lines
15 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í k páté 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": 1,
"id": "e03b150c",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">Manim Community <span style=\"color: #008000; text-decoration-color: #008000\">v0.15.0</span>\n",
"\n",
"</pre>\n"
],
"text/plain": [
"Manim Community \u001b[32mv0.\u001b[0m\u001b[32m15.0\u001b[0m\n",
"\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from manim import *"
]
},
{
"cell_type": "markdown",
"id": "5bd0ac2f",
"metadata": {},
"source": [
"## Vlastní animace"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "01394a96",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" \r"
]
},
{
"data": {
"text/html": [
"<video src=\"media/jupyter/StarFox@2022-05-01@14-58-31.mp4\" controls autoplay loop style=\"max-width: 60%;\" >\n",
" Your browser does not support the <code>video</code> element.\n",
" </video>"
],
"text/plain": [
"<IPython.core.display.Video object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%manim -v WARNING -qh --disable_caching StarFox\n",
"# POZOR! Při změnách musíme opět zakázat cachování\n",
"# (Manim při změně animace nepozná změnu ve scéně)\n",
"\n",
"\n",
"\n",
"class Roll(Animation):\n",
" \"\"\"Animace, která otočí objekt o daný úhel, trochu ho při tom zmenší a posune se do strany.\"\"\"\n",
" \n",
" def __init__(self, mobject: Mobject, angle, direction, scale_ratio=0.85, **kwargs):\n",
" \"\"\"Konstruktor. Inicializuje potřebné věci animace.\"\"\"\n",
" # bude se nám hodit původní verze objektu, který animujeme\n",
" # (v animaci ho otiž budeme měnit)\n",
" self.original = mobject.copy()\n",
" \n",
" self.scale_ratio = scale_ratio\n",
" self.angle = angle\n",
" self.direction = direction\n",
" \n",
" super().__init__(mobject, **kwargs)\n",
"\n",
" def interpolate_mobject(self, alpha: float) -> None:\n",
" \"\"\"Funkce, která se volá každý snímek, aby se animace animovala.\"\"\"\n",
" \n",
" # alpha je od 0 do 1, ale animace mohla jako parametr dostat rate funkci\n",
" # proto je třeba tuto funkci na alphu aplikovat, a by se animace chovala správně\n",
" actual_alpha = self.rate_func(alpha)\n",
"\n",
" # chceme, aby objekt měl na začátku scale 1, v půlce scale_ratio a na konci 1\n",
" # tohle možná není nejelegantnější způsob, ale funguje\n",
" scale_alpha = 1 - (1 - self.scale_ratio) * 2 * (0.5 - abs(actual_alpha - 0.5))\n",
" \n",
" # chceme, aby objekt měl na začátku startovní pozici, pak se posunul a nakonec se vrátil\n",
" direction_alpha = there_and_back(actual_alpha)\n",
" \n",
" self.mobject.become(self.original.copy())\\\n",
" .rotate(actual_alpha * self.angle)\\\n",
" .scale(scale_alpha)\\\n",
" .shift(self.direction * direction_alpha)\n",
"\n",
" \n",
"class Dissolve(AnimationGroup):\n",
" \"\"\"Animace, která 'zmizí' objekt. Používáme zde AnimationGroup,\n",
" jelikož animace mizení je tvořena dvěma různými animacemi.\"\"\"\n",
" \n",
" def __init__(self, mobject: Mobject, **kwargs):\n",
" \"\"\"Konstruktor. Inicializuje potřebné věci animace.\"\"\"\n",
" self.original = mobject.copy()\n",
" \n",
" # způsob, jak do animate syntaxu dostaneme argumenty\n",
" a1 = mobject.animate.scale(0)\n",
" a2 = Flash(mobject, color=mobject.color)\n",
" \n",
" super().__init__(a1, a2, lag_ratio=0.75, **kwargs)\n",
" \n",
" \n",
"class StarFox(Scene):\n",
" def construct(self):\n",
" square = Square(color=BLUE, fill_opacity=0.75).scale(1.5)\n",
" \n",
" self.play(Roll(square, angle=PI, direction=LEFT * 0.75))\n",
" self.play(Roll(square, angle=-PI, direction=RIGHT * 0.75))\n",
" \n",
" self.play(Dissolve(square))"
]
},
{
"cell_type": "markdown",
"id": "cb6584c4",
"metadata": {},
"source": [
"## Pluginy"
]
},
{
"cell_type": "markdown",
"id": "226fa759",
"metadata": {},
"source": [
"### Fyzika"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "49f08ea5",
"metadata": {},
"outputs": [],
"source": [
"from manim_physics import *"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "18742bd7",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" \r"
]
},
{
"data": {
"text/html": [
"<video src=\"media/jupyter/FallingObjectsExample@2022-05-01@14-58-40.mp4\" controls autoplay loop style=\"max-width: 60%;\" >\n",
" Your browser does not support the <code>video</code> element.\n",
" </video>"
],
"text/plain": [
"<IPython.core.display.Video object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%manim -v WARNING -qh FallingObjectsExample\n",
"\n",
"\n",
"# příklad z https://github.com/Matheart/manim-physics\n",
"# SpaceScene je třída podporující fyzikální interakce\n",
"class FallingObjectsExample(SpaceScene):\n",
" def construct(self):\n",
" circle = Circle().shift(UP)\n",
" circle.set_fill(RED, 1)\n",
" circle.shift(DOWN + RIGHT)\n",
"\n",
" rect = Square().shift(UP)\n",
" rect.rotate(PI / 4)\n",
" rect.set_fill(YELLOW_A, 1)\n",
" rect.shift(UP * 2)\n",
" rect.scale(0.5)\n",
"\n",
" ground = Line([-4, -3.5, 0], [4, -3.5, 0])\n",
" wall1 = Line([-4, -3.5, 0], [-4, 3.5, 0])\n",
" wall2 = Line([4, -3.5, 0], [4, 3.5, 0])\n",
" walls = VGroup(ground, wall1, wall2)\n",
" self.add(walls)\n",
"\n",
" self.play(\n",
" DrawBorderThenFill(circle),\n",
" DrawBorderThenFill(rect),\n",
" )\n",
" \n",
" # až doposud se jednalo o normální Manim kód\n",
" # nyní použijeme funkce, které objektům přidají fyziku\n",
" self.make_rigid_body(rect, circle) # čtverec a kruh jsou rigidní (hýbou se)\n",
" self.make_static_body(walls) # zdi jsou statické (nehýbou jse)\n",
" \n",
" # nyní počkáme - funkce výše přidaly objektům updatery\n",
" self.wait(5)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "61d9d3aa",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" \r"
]
},
{
"data": {
"text/html": [
"<video src=\"media/jupyter/ElectricFieldExample@2022-05-01@15-17-33.mp4\" controls autoplay loop style=\"max-width: 60%;\" >\n",
" Your browser does not support the <code>video</code> element.\n",
" </video>"
],
"text/plain": [
"<IPython.core.display.Video object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%manim -v WARNING -qh ElectricFieldExample\n",
"\n",
"\n",
"# zde stačí Scene, protože používáme pouze nové objekty\n",
"# příklad upravený z https://github.com/Matheart/manim-physics\n",
"# POZOR: kód trvá postavit opravdu dlouho, doporučuji používat nižší kvalitu\n",
"class ElectricFieldExample(Scene):\n",
" def construct(self):\n",
" charge1 = Charge(-1, LEFT + DOWN)\n",
" charge2 = Charge(2, RIGHT + DOWN)\n",
" charge3 = Charge(-1, UP)\n",
" \n",
" def rebuild(field):\n",
" \"\"\"Funkce která přestaví elektrické pole.\"\"\"\n",
" field.become(ElectricField(charge1, charge2, charge3))\n",
" \n",
" field = ElectricField(charge1, charge2, charge3)\n",
" \n",
" self.add(field, charge1, charge2, charge3)\n",
" \n",
" self.play(Write(field), FadeIn(charge1), FadeIn(charge2), FadeIn(charge3))\n",
" \n",
" field.add_updater(rebuild)\n",
" \n",
" self.play(\n",
" charge1.animate.shift(LEFT),\n",
" charge2.animate.shift(RIGHT),\n",
" charge3.animate.shift(DOWN * 0.5),\n",
" run_time=2,\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "bba36496",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" \r"
]
},
{
"data": {
"text/html": [
"<video src=\"media/jupyter/MagnetismExample@2022-05-01@15-24-37.mp4\" controls autoplay loop style=\"max-width: 60%;\" >\n",
" Your browser does not support the <code>video</code> element.\n",
" </video>"
],
"text/plain": [
"<IPython.core.display.Video object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%manim -v WARNING -qh MagnetismExample\n",
"\n",
"\n",
"# příklad upravený z https://github.com/Matheart/manim-physics\n",
"# OPĚT POZOR: kód trvá postavit opravdu dlouho, doporučuji používat nižší kvalitu\n",
"class MagnetismExample(Scene):\n",
" def construct(self):\n",
" current1 = Current(LEFT * 2.5)\n",
" current2 = Current(RIGHT * 2.5, direction=IN)\n",
" \n",
" def rebuild(field):\n",
" \"\"\"Funkce která přestaví magnetické pole.\"\"\"\n",
" field.become(MagneticField(current1, current2))\n",
" \n",
" field = MagneticField(current1, current2)\n",
" \n",
" self.play(Write(field), FadeIn(current1), FadeIn(current2))\n",
" \n",
" field.add_updater(rebuild)\n",
" \n",
" self.play(\n",
" Rotate(current1, about_point=ORIGIN, angle=PI),\n",
" Rotate(current2, about_point=ORIGIN, angle=PI),\n",
" run_time=2,\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a63686ae",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" \r"
]
},
{
"data": {
"text/html": [
"<video src=\"media/jupyter/Pendulum@2022-05-01@15-24-56.mp4\" controls autoplay loop style=\"max-width: 60%;\" >\n",
" Your browser does not support the <code>video</code> element.\n",
" </video>"
],
"text/plain": [
"<IPython.core.display.Video object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%manim -v WARNING -qh Pendulum\n",
"\n",
"\n",
"# příklad upravený z https://github.com/Matheart/manim-physics\n",
"# opět používáme SpaceScene, jelikož animujeme fyzikální interakce\n",
"class Pendulum(SpaceScene):\n",
" def construct(self):\n",
" # pozice kuliček pendula\n",
" bob_positions = [RIGHT * 1.5 + UP, RIGHT * 1.5 + UP * 2]\n",
" \n",
" pendulum = MultiPendulum(\n",
" *bob_positions,\n",
" pivot_point=UP,\n",
" bob_style={\"color\": WHITE, \"fill_opacity\": 1, \"radius\": 0.15},\n",
" )\n",
" \n",
" self.make_rigid_body(pendulum.bobs) # kuličky pendula jsou rigidní\n",
" pendulum.start_swinging() # a spojené\n",
" \n",
" self.add(pendulum)\n",
" \n",
" # budeme sledovat cestu obou kuliček\n",
" for i, bob in enumerate(pendulum.bobs):\n",
" self.bring_to_back(TracedPath(bob.get_center, stroke_color=DARK_GRAY))\n",
" \n",
" self.wait(12)"
]
},
{
"cell_type": "markdown",
"id": "b1c1214e",
"metadata": {},
"source": [
"### Chemie"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "74825cc6",
"metadata": {},
"outputs": [],
"source": [
"from chanim import *"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "af492827",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
" \r"
]
},
{
"data": {
"text/html": [
"<video src=\"media/jupyter/ChanimExample@2022-05-01@15-24-57.mp4\" controls autoplay loop style=\"max-width: 60%;\" >\n",
" Your browser does not support the <code>video</code> element.\n",
" </video>"
],
"text/plain": [
"<IPython.core.display.Video object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%manim -v WARNING -qh ChanimExample\n",
"\n",
"\n",
"class ChanimExample(Scene):\n",
" def construct(self):\n",
" # chemická sloučenina\n",
" # interně využívá ChemFigový syntax (https://www.ctan.org/pkg/chemfig)\n",
" chem = ChemWithName(\n",
" \"*6((=O)-N(-CH_3)-*5(-N=-N(-CH_3)-=)--(=O)-N(-H_3C)-)\",\n",
" \"Caffeine\"\n",
" )\n",
" \n",
" chem.move_to(ORIGIN)\n",
" \n",
" self.play(chem.creation_anim())"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b2c6e7b5",
"metadata": {},
"outputs": [],
"source": []
}
],
"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.2"
}
},
"nbformat": 4,
"nbformat_minor": 5
}