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.

675 lines 20 KiB Raw Permalink Blame History

 `{` ` "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 čtvrté 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": [` ` "## Booleovské operace"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "6aa5ba0f",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh BooleanOperations\n",` ` "\n",` ` "\n",` ` "class BooleanOperations(Scene):\n",` ` " def construct(self):\n",` ` "\n",` ` " circle = Circle(fill_opacity=0.75, color=RED).scale(2).shift(LEFT * 1.5)\n",` ` " square = Square(fill_opacity=0.75, color=GREEN).scale(2).shift(RIGHT * 1.5)\n",` ` "\n",` ` " group = VGroup(circle, square)\n",` ` "\n",` ` " self.play(Write(group))\n",` ` "\n",` ` " self.play(group.animate.scale(0.5).shift(UP * 1.6))\n",` ` "\n",` ` " union = Union(circle, square, fill_opacity=1, color=BLUE)\n",` ` "\n",` ` " # postupně voláme Union(), Intersection() a Difference()\n",` ` " for operation, position, name in zip(\n",` ` " [Intersection, Union, Difference],\n",` ` " [LEFT * 3.3, ORIGIN, RIGHT * 4.5],\n",` ` " [\"Průnik\", \"Sjednocení\", \"Rozdíl\"],\n",` ` " ):\n",` ` " result = operation(circle, square, fill_opacity=1, color=DARK_BLUE)\n",` ` " result_position = DOWN * 1.3 + position\n",` ` " \n",` ` " label = Tex(name).move_to(result_position).scale(0.8)\n",` ` " \n",` ` " self.play(\n",` ` " AnimationGroup(\n",` ` " FadeIn(result),\n",` ` " result.animate.move_to(result_position),\n",` ` " FadeIn(label),\n",` ` " lag_ratio=0.5,\n",` ` " )\n",` ` " )"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "3da40017",` ` "metadata": {},` ` "source": [` ` "## Vlastní objekty"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "cf1fb3b5",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh StackExample\n",` ` "\n",` ` "\n",` ` "class Stack(VMobject):\n",` ` " def __init__(self, size, *args, **kwargs):\n",` ` " # inicializace VGroup objektu\n",` ` " super().__init__(**kwargs)\n",` ` "\n",` ` " self.squares = VGroup()\n",` ` " self.labels = VGroup()\n",` ` " self.index = 0\n",` ` " self.pointer = Arrow(ORIGIN, UP * 1.2)\n",` ` "\n",` ` " for _ in range(size):\n",` ` " self.squares.add(Square(side_length=0.8))\n",` ` "\n",` ` " self.squares.arrange(buff=0.15)\n",` ` "\n",` ` " self.pointer.next_to(self.squares[0], DOWN)\n",` ` " self.add()\n",` ` "\n",` ` " # DŮLEŽITÉ - přidáme do objektu všechny podobjekty!\n",` ` " self.add(self.squares, self.labels, self.pointer)\n",` ` "\n",` ` " def __get_index_rectangle_color(self):\n",` ` " \"\"\"Vrátí barvu aktuálního obdelníků stacku.\"\"\"\n",` ` " return self.squares[self.index].get_color()\n",` ` "\n",` ` " def __create_label(self, element):\n",` ` " \"\"\"Vytvoření labelu daného prvku (podle barvy a rozměrů čtverců stacku).\"\"\"\n",` ` " return (\n",` ` " Tex(str(element))\n",` ` " .scale(self.squares[0].height)\n",` ` " .set_color(self.__get_index_rectangle_color())\n",` ` " )\n",` ` "\n",` ` " def push(self, element):\n",` ` " \"\"\"Přidá prvek do zásobníku. Vrátí odpovídající animace.\"\"\"\n",` ` " self.labels.add(self.__create_label(element).move_to(self.squares[self.index]))\n",` ` " self.index += 1\n",` ` "\n",` ` " return AnimationGroup(\n",` ` " FadeIn(self.labels[-1]),\n",` ` " self.pointer.animate.next_to(self.squares[self.index], DOWN),\n",` ` " Indicate(\n",` ` " self.squares[self.index - 1], color=self.__get_index_rectangle_color()\n",` ` " ),\n",` ` " )\n",` ` "\n",` ` " def pop(self):\n",` ` " \"\"\"Odebere prvek ze zásobníku. Vrátí odpovídající animace.\"\"\"\n",` ` " label = self.labels[-1]\n",` ` " self.labels.remove(label)\n",` ` " self.index -= 1\n",` ` "\n",` ` " return AnimationGroup(\n",` ` " FadeOut(label),\n",` ` " self.pointer.animate.next_to(self.squares[self.index], DOWN),\n",` ` " Indicate(\n",` ` " self.squares[self.index],\n",` ` " color=self.__get_index_rectangle_color(),\n",` ` " scale_factor=1 / 1.2,\n",` ` " ),\n",` ` " )\n",` ` "\n",` ` " def clear(self):\n",` ` " \"\"\"Vyčistí zásobník. Vrátí odpovídající animaci.\"\"\"\n",` ` " result = AnimationGroup(*[self.pop() for _ in range(self.index)], lag_ratio=0)\n",` ` "\n",` ` " self.index = 0\n",` ` "\n",` ` " return result\n",` ` "\n",` ` "\n",` ` "class StackExample(Scene):\n",` ` " def construct(self):\n",` ` " stack = Stack(10)\n",` ` "\n",` ` " # na objekt fungují správně libovolné animace\n",` ` " self.play(Write(stack))\n",` ` "\n",` ` " self.wait(0.5)\n",` ` "\n",` ` " for i in range(5):\n",` ` " self.play(stack.push(i))\n",` ` "\n",` ` " self.play(stack.pop())\n",` ` "\n",` ` " self.wait(0.5)\n",` ` "\n",` ` " # můžeme používat i animate syntax!\n",` ` " # měnění barvy se rekurzivně aplikuje na všechny podobjekty\n",` ` " self.play(stack.animate.scale(1.3).set_color(BLUE))\n",` ` "\n",` ` " self.wait(0.5)\n",` ` "\n",` ` " for i in range(2):\n",` ` " self.play(stack.push(i))\n",` ` "\n",` ` " self.play(stack.pop())\n",` ` "\n",` ` " self.play(stack.clear())\n",` ` "\n",` ` " self.play(FadeOut(stack))"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "88f6f285",` ` "metadata": {},` ` "source": [` ` "## Jiné grafy"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "0906f798",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh GraphExample\n",` ` "\n",` ` "from math import sin\n",` ` "\n",` ` "\n",` ` "class GraphExample(Scene):\n",` ` " def construct(self):\n",` ` " # osy - rozmezí a značení os x, y\n",` ` " axes = Axes(x_range=[-5, 5], y_range=[-3, 7])\n",` ` " labels = axes.get_axis_labels(x_label=\"x\", y_label=\"y\")\n",` ` "\n",` ` " def f1(x):\n",` ` " return x ** 2\n",` ` "\n",` ` " def f2(x):\n",` ` " return sin(x)\n",` ` "\n",` ` " # objekty vykreslených funkcí\n",` ` " g1 = axes.plot(f1, color=RED)\n",` ` " g2 = axes.plot(f2, color=BLUE)\n",` ` "\n",` ` " self.play(Write(axes), Write(labels))\n",` ` "\n",` ` " self.play(AnimationGroup(Write(g1), Write(g2), lag_ratio=0.5))\n",` ` "\n",` ` " self.play(Unwrite(axes), Unwrite(labels), Unwrite(g1), Unwrite(g2))"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "84978263",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh DiscontinuousGraphExample\n",` ` "\n",` ` "from math import sin\n",` ` "\n",` ` "\n",` ` "class DiscontinuousGraphExample(Scene):\n",` ` " def construct(self):\n",` ` " axes = Axes(x_range=[-5, 5], y_range=[-3, 7])\n",` ` " labels = axes.get_axis_labels(x_label=\"x\", y_label=\"y\")\n",` ` "\n",` ` " def f(x):\n",` ` " return 1 / x\n",` ` "\n",` ` " g_bad = axes.plot(f, color=RED)\n",` ` "\n",` ` " # rozdělení na dvě části podle hodnot x\n",` ` " g_left = axes.plot(f, x_range=[-5, -0.1], color=GREEN)\n",` ` " g_right = axes.plot(f, x_range=[0.1, 5], color=GREEN)\n",` ` "\n",` ` " self.play(Write(axes), Write(labels))\n",` ` "\n",` ` " self.play(Write(g_bad))\n",` ` " self.play(FadeOut(g_bad))\n",` ` "\n",` ` " self.play(AnimationGroup(Write(g_left), Write(g_right), lag_ratio=0.5))\n",` ` "\n",` ` " self.play(Unwrite(axes), Unwrite(labels), Unwrite(g_left), Unwrite(g_right))"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "521d3286",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh ParametricGraphExample\n",` ` "\n",` ` "from math import sin, cos\n",` ` "\n",` ` "\n",` ` "class ParametricGraphExample(Scene):\n",` ` " def construct(self):\n",` ` " axes = Axes(x_range=[-10, 10], y_range=[-5, 5])\n",` ` " labels = axes.get_axis_labels(x_label=\"x\", y_label=\"y\")\n",` ` "\n",` ` " def f1(t):\n",` ` " \"\"\"Parametrická funkce kružnice.\"\"\"\n",` ` " return (cos(t) * 3 - 4.5, sin(t) * 3)\n",` ` "\n",` ` " def f2(t):\n",` ` " \"\"\"Parametrická funkce <3.\"\"\"\n",` ` " return (\n",` ` " 0.2 * (16 * (sin(t)) ** 3) + 4.5,\n",` ` " 0.2 * (13 * cos(t) - 5 * cos(2 * t) - 2 * cos(3 * t) - cos(4 * t)),\n",` ` " )\n",` ` "\n",` ` " # objekty vykreslených funkcí\n",` ` " # místo axes.plot používáme axes.plot_parametric_curve\n",` ` " # parametr t_range určuje, jaké je rozmezí parametru t\n",` ` " g1 = axes.plot_parametric_curve(f1, color=RED, t_range=[0, 2 * PI])\n",` ` " g2 = axes.plot_parametric_curve(f2, color=BLUE, t_range=[-PI, PI])\n",` ` "\n",` ` " self.play(Write(axes), Write(labels))\n",` ` "\n",` ` " self.play(AnimationGroup(Write(g1), Write(g2), lag_ratio=0.5))\n",` ` "\n",` ` " self.play(Unwrite(axes), Unwrite(labels), Unwrite(g1), Unwrite(g2))"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "c0e61a1b",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh LineGraphExample\n",` ` "\n",` ` "from random import random, seed\n",` ` "\n",` ` "\n",` ` "class LineGraphExample(Scene):\n",` ` " def construct(self):\n",` ` " seed(0xDEADBEEF2) # hezčí hodnoty :P\n",` ` "\n",` ` " # hodnoty ke grafování (x a y)\n",` ` " # u np.arange(l, r, step) vrátí pole hodnot od l do r (nevčetně) s kroky velikosti step\n",` ` " x_values = np.arange(-1, 1 + 0.25, 0.25)\n",` ` " y_values = [random() for _ in x_values]\n",` ` "\n",` ` " # osy (tentokrát s čísly)\n",` ` " axes = Axes(\n",` ` " x_range=[-1, 1, 0.25],\n",` ` " y_range=[-0.1, 1, 0.25],\n",` ` " # nastavení čísel - hodnoty a počet desetinných míst\n",` ` " x_axis_config={\"numbers_to_include\": x_values},\n",` ` " y_axis_config={\"numbers_to_include\": np.arange(0, 1, 0.25)},\n",` ` " axis_config={\"decimal_number_config\": {\"num_decimal_places\": 2}},\n",` ` " )\n",` ` "\n",` ` " labels = axes.get_axis_labels(x_label=\"x\", y_label=\"y\")\n",` ` "\n",` ` " # místo axes.plot používáme axes.plot_line_graph\n",` ` " graph = axes.plot_line_graph(x_values=x_values, y_values=y_values)\n",` ` "\n",` ` " self.play(Write(axes), Write(labels))\n",` ` "\n",` ` " self.play(Write(graph), run_time=2)\n",` ` "\n",` ` " self.play(Unwrite(axes), Unwrite(labels), Unwrite(graph))"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "bb5423f4",` ` "metadata": {},` ` "source": [` ` "## 3D operace"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "ab85ca2d",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh Axes3DExample\n",` ` "\n",` ` "\n",` ` "class Axes3DExample(ThreeDScene):\n",` ` " def construct(self):\n",` ` " # 3D osy\n",` ` " axes = ThreeDAxes()\n",` ` "\n",` ` " x_label = axes.get_x_axis_label(Tex(\"x\"))\n",` ` " y_label = axes.get_y_axis_label(Tex(\"y\")).shift(UP * 1.8)\n",` ` "\n",` ` " # 3D varianta Dot() objektu\n",` ` " dot = Dot3D()\n",` ` "\n",` ` " # zmenšení zoomu, abychom viděli osy\n",` ` " self.set_camera_orientation(zoom=0.5)\n",` ` "\n",` ` " self.play(FadeIn(axes), FadeIn(dot), FadeIn(x_label), FadeIn(y_label))\n",` ` "\n",` ` " self.wait(0.5)\n",` ` "\n",` ` " # animace posunutí kamery tak, aby byly osy dobře vidět\n",` ` " self.move_camera(phi=75 * DEGREES, theta=30 * DEGREES, zoom=1, run_time=1.5)\n",` ` "\n",` ` " # vestavěný updater, který kameru začne rotovat (aby na scénu bylo lépe vidět)\n",` ` " self.begin_ambient_camera_rotation(rate=0.15)\n",` ` "\n",` ` " # jedna tečka za každý směr\n",` ` " upDot = dot.copy().set_color(RED)\n",` ` " rightDot = dot.copy().set_color(BLUE)\n",` ` " outDot = dot.copy().set_color(GREEN)\n",` ` "\n",` ` " self.wait(1)\n",` ` "\n",` ` " self.play(\n",` ` " upDot.animate.shift(UP),\n",` ` " rightDot.animate.shift(RIGHT),\n",` ` " outDot.animate.shift(OUT),\n",` ` " )\n",` ` "\n",` ` " self.wait(2)"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "efc147d5",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh Rotation3DExample\n",` ` "\n",` ` "\n",` ` "class Rotation3DExample(ThreeDScene):\n",` ` " def construct(self):\n",` ` " # přidání jednoduché základní krychle\n",` ` " cube = Cube(side_length=3, fill_opacity=1)\n",` ` "\n",` ` " self.begin_ambient_camera_rotation(rate=0.3)\n",` ` "\n",` ` " # posunutí orientace kamery bez animace\n",` ` " self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)\n",` ` "\n",` ` " self.play(Write(cube), run_time=2)\n",` ` "\n",` ` " self.wait(3)\n",` ` "\n",` ` " self.play(Unwrite(cube), run_time=2)"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "127ccd71",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh Basic3DExample\n",` ` "\n",` ` "\n",` ` "class Basic3DExample(ThreeDScene):\n",` ` " def construct(self):\n",` ` " # přidání jednoduché základní krychle\n",` ` " cube = Cube(side_length=3, fill_opacity=0.5)\n",` ` "\n",` ` " self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)\n",` ` "\n",` ` " self.play(FadeIn(cube))\n",` ` "\n",` ` " for axis in [RIGHT, UP, OUT]:\n",` ` " self.play(Rotate(cube, PI / 2, about_point=ORIGIN, axis=axis))\n",` ` "\n",` ` " self.play(FadeOut(cube))"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "98daca2b",` ` "metadata": {},` ` "source": [` ` "## Kostry úloh"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "8746ae11",` ` "metadata": {},` ` "source": [` ` "### Simulace binomického rozložení [6b]"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "18fb5e35",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh BinomialDistributionSimulation\n",` ` "\n",` ` "\n",` ` "class BinomialDistributionSimulation(Scene):\n",` ` " def construct(self):\n",` ` " pass"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "b7139b0d",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh BezierExample\n",` ` "\n",` ` "from random import choice, seed\n",` ` "\n",` ` "\n",` ` "class MoveAndFade(Animation):\n",` ` " def __init__(self, mobject: Mobject, path: VMobject, **kwargs):\n",` ` " self.path = path\n",` ` " self.original = mobject.copy()\n",` ` " super().__init__(mobject, **kwargs)\n",` ` "\n",` ` " def interpolate_mobject(self, alpha: float) -> None:\n",` ` " point = self.path.point_from_proportion(self.rate_func(alpha))\n",` ` "\n",` ` " # tohle není úplně čisté, jelikož pokaždé vytváříme nový objekt\n",` ` " # je to kvůli tomu, že obj.fade() nenastavuje průhlednost ale přidává jí\n",` ` " self.mobject.become(self.original.copy()).move_to(point).fade(alpha)\n",` ` "\n",` ` "\n",` ` "class BezierExample(Scene):\n",` ` " def construct(self):\n",` ` " # křivku definujeme přes čtyři body:\n",` ` " # 2 krajní, ve kterých začíná a končí\n",` ` " # 2 kontrolní, které určují tvar\n",` ` " positions = [\n",` ` " UP + LEFT * 3, # počáteční\n",` ` " UP + RIGHT * 2, # 1. kontrolní\n",` ` " DOWN + LEFT * 2, # 2. kontrolní\n",` ` " DOWN + RIGHT * 3, # koncový\n",` ` " ]\n",` ` "\n",` ` " points = VGroup(*[Dot().move_to(position) for position in positions]).scale(1.5)\n",` ` "\n",` ` " # rozlišíme kontrolní body\n",` ` " points[1].set_color(BLUE)\n",` ` " points[2].set_color(BLUE)\n",` ` "\n",` ` " bezier = CubicBezier(*positions).scale(1.5)\n",` ` "\n",` ` " self.play(Write(bezier), Write(points))\n",` ` "\n",` ` " # animace posunu\n",` ` " circle = Circle(fill_opacity=1, stroke_opacity=0).scale(0.25).move_to(points[0])\n",` ` "\n",` ` " self.play(FadeIn(circle, shift=RIGHT * 0.5))\n",` ` " self.play(MoveAlongPath(circle, bezier))\n",` ` "\n",` ` " self.play(FadeOut(circle))\n",` ` "\n",` ` " # animace posunu s mizením\n",` ` " circle = (\n",` ` " Circle(fill_color=GREEN, fill_opacity=1, stroke_opacity=0)\n",` ` " .scale(0.25)\n",` ` " .move_to(points[0])\n",` ` " )\n",` ` "\n",` ` " self.play(FadeIn(circle, shift=RIGHT * 0.5))\n",` ` " self.play(MoveAndFade(circle, bezier))\n",` ` "\n",` ` " self.play(FadeOut(bezier), FadeOut(points), FadeOut(circle))"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "4de93509",` ` "metadata": {},` ` "source": [` ` "### 3D Game of Life [9b]"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "eee60fd6",` ` "metadata": {},` ` "source": [` ` "#### Dvoustavový [5b]"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "d95d2328",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh GOLTwoState\n",` ` "\n",` ` "\n",` ` "class GOLTwoState(ThreeDScene):\n",` ` " def construct(self):\n",` ` " pass"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "3fd9c87c",` ` "metadata": {},` ` "source": [` ` "#### Vícestavový [3b]"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "9525cea2",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh GOLMultiState\n",` ` "\n",` ` "\n",` ` "class GOLMultiState(ThreeDScene):\n",` ` " def construct(self):\n",` ` " pass"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "464edd8b",` ` "metadata": {},` ` "source": [` ` "#### Vlastní [1b]"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "01394a96",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh GOLCustom\n",` ` "\n",` ` "\n",` ` "class GOLCustom(ThreeDScene):\n",` ` " def construct(self):\n",` ` " pass"` ` ]` ` }` ` ],` ` "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` ```} ``` ``` ```