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.

#### 446 lines 16 KiB Raw Permalink Blame History

 `{` ` "cells": [` ` {` ` "cell_type": "markdown",` ` "id": "36cee816",` ` "metadata": {},` ` "source": [` ` "# Řešení 3. série"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "efe16561",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "from manim import *"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "36ec08cc",` ` "metadata": {},` ` "source": [` ` "## Simulace binomického rozložení "` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "e7a3da39",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "%%manim -v WARNING -qh --disable_caching BinomialDistributionSimulation\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 BinomialDistributionSimulation(Scene):\n",` ` " def construct(self):\n",` ` " seed(0xDEADBEEF2) # hezčí vstupy :)\n",` ` "\n",` ` " radius = 0.13\n",` ` " x_spacing = radius * 1.5\n",` ` " y_spacing = 4 * radius\n",` ` "\n",` ` " n = 9\n",` ` " pyramid = VGroup()\n",` ` " pyramid_values = []\n",` ` "\n",` ` " for i in range(1, n + 1):\n",` ` " row = VGroup()\n",` ` "\n",` ` " for j in range(i):\n",` ` " obj = Dot()\n",` ` "\n",` ` " # if it's the last row, make the rows numbers instead\n",` ` " if i == n:\n",` ` " obj = Tex(\"0\")\n",` ` " pyramid_values.append(0)\n",` ` "\n",` ` " row.add(obj)\n",` ` "\n",` ` " row.arrange(buff=2 * x_spacing)\n",` ` "\n",` ` " if len(pyramid) != 0:\n",` ` " row.move_to(pyramid[-1]).shift(DOWN * y_spacing)\n",` ` "\n",` ` " pyramid.add(row)\n",` ` "\n",` ` " pyramid.move_to(RIGHT * 3.4)\n",` ` "\n",` ` " x_values = np.arange(-n // 2 + 1, n // 2 + 1, 1)\n",` ` "\n",` ` " def create_graph(x_values, y_values):\n",` ` " y_values_all = list(range(0, (max(y_values) or 1) + 1))\n",` ` "\n",` ` " axes = (\n",` ` " Axes(\n",` ` " x_range=[-n // 2 + 1, n // 2, 1],\n",` ` " y_range=[0, max(y_values) or 1, 1],\n",` ` " x_axis_config={\"numbers_to_include\": x_values},\n",` ` " tips=False,\n",` ` " )\n",` ` " .scale(0.45)\n",` ` " .shift(LEFT * 3.0)\n",` ` " )\n",` ` "\n",` ` " graph = axes.plot_line_graph(x_values=x_values, y_values=y_values)\n",` ` "\n",` ` " return graph, axes\n",` ` "\n",` ` " graph, axes = create_graph(x_values, pyramid_values)\n",` ` "\n",` ` " self.play(Write(axes), Write(pyramid), Write(graph), run_time=1.5)\n",` ` "\n",` ` " for iteration in range(120):\n",` ` " circle = (\n",` ` " Circle(fill_opacity=1, stroke_opacity=0)\n",` ` " .scale(radius)\n",` ` " .next_to(pyramid[0][0], UP, buff=0)\n",` ` " )\n",` ` "\n",` ` " run_time = (\n",` ` " 0.5\n",` ` " if iteration == 0\n",` ` " else 0.1\n",` ` " if iteration == 1\n",` ` " else 0.02\n",` ` " if iteration < 20\n",` ` " else 0.003\n",` ` " )\n",` ` "\n",` ` " self.play(FadeIn(circle, shift=DOWN * 0.5), run_time=run_time * 2)\n",` ` "\n",` ` " x = 0\n",` ` " for i in range(1, n):\n",` ` " next_position = choice([0, 1])\n",` ` " x += next_position\n",` ` "\n",` ` " dir = LEFT if next_position == 0 else RIGHT\n",` ` "\n",` ` " circle_center = circle.get_center()\n",` ` "\n",` ` " # if it's not the last row, make the animation regular\n",` ` " if i != n - 1:\n",` ` " b = CubicBezier(\n",` ` " circle_center,\n",` ` " circle_center + dir * x_spacing,\n",` ` " circle_center + dir * x_spacing + DOWN * y_spacing / 2,\n",` ` " circle.copy().next_to(pyramid[i][x], UP, buff=0).get_center(),\n",` ` " )\n",` ` "\n",` ` " self.play(\n",` ` " MoveAlongPath(circle, b, rate_func=rate_functions.ease_in_quad),\n",` ` " run_time=run_time,\n",` ` " )\n",` ` "\n",` ` " # if it is, fade the circle and increment the number\n",` ` " else:\n",` ` " b = CubicBezier(\n",` ` " circle_center,\n",` ` " circle_center + dir * x_spacing,\n",` ` " circle_center + dir * x_spacing + DOWN * y_spacing / 2,\n",` ` " pyramid[i][x].get_center(),\n",` ` " )\n",` ` "\n",` ` " pyramid_values[x] += 1\n",` ` "\n",` ` " n_graph, n_axes = create_graph(x_values, pyramid_values)\n",` ` "\n",` ` " self.play(\n",` ` " AnimationGroup(\n",` ` " AnimationGroup(\n",` ` " MoveAndFade(\n",` ` " circle, b, rate_func=rate_functions.ease_in_quad\n",` ` " ),\n",` ` " run_time=run_time,\n",` ` " ),\n",` ` " AnimationGroup(\n",` ` " pyramid[i][x]\n",` ` " .animate(run_time=run_time)\n",` ` " .become(\n",` ` " Tex(str(pyramid_values[x])).move_to(pyramid[i][x])\n",` ` " ),\n",` ` " graph.animate.become(n_graph),\n",` ` " axes.animate.become(n_axes),\n",` ` " run_time=run_time,\n",` ` " ),\n",` ` " lag_ratio=0.3,\n",` ` " )\n",` ` " )\n",` ` "\n",` ` " self.play(FadeOut(axes), FadeOut(pyramid), FadeOut(graph), run_time=1)\n"` ` ]` ` },` ` {` ` "cell_type": "markdown",` ` "id": "e6f13b54",` ` "metadata": {},` ` "source": [` ` "## 3D Game of Life"` ` ]` ` },` ` {` ` "cell_type": "code",` ` "execution_count": null,` ` "id": "fc2941bf",` ` "metadata": {},` ` "outputs": [],` ` "source": [` ` "from manim import *\n",` ` "from random import random, seed\n",` ` "from enum import Enum\n",` ` "\n",` ` "# https://softologyblog.wordpress.com/2019/12/28/3d-cellular-automata-3/\n",` ` "\n",` ` "class Grid:\n",` ` " class ColorType(Enum):\n",` ` " FROM_COORDINATES = 0\n",` ` " FROM_PALETTE = 1\n",` ` "\n",` ` " def __init__(self, scene, grid_size, survives_when, revives_when, state_count=2, size=1, palette=[\"#000b5e\", \"#001eff\"], color_type=ColorType.FROM_PALETTE):\n",` ` " self.grid = {}\n",` ` " self.scene = scene\n",` ` " self.grid_size = grid_size\n",` ` " self.size = size\n",` ` " self.survives_when = survives_when\n",` ` " self.revives_when = revives_when\n",` ` " self.state_count = state_count\n",` ` " self.palette = palette\n",` ` " self.color_type = color_type\n",` ` "\n",` ` " self.bounding_box = Cube(side_length = self.size, color=GRAY, fill_opacity=0.05)\n",` ` " self.scene.add(self.bounding_box)\n",` ` "\n",` ` " def fadeOut(self):\n",` ` " self.scene.play(\n",` ` " FadeOut(self.bounding_box),\n",` ` " *[FadeOut(self.grid[index][0]) for index in self.grid],\n",` ` " )\n",` ` "\n",` ` " def __index_to_position(self, index):\n",` ` " \"\"\"Convert the index of a cell to its position in 3D.\"\"\"\n",` ` " dirs = [RIGHT, UP, OUT]\n",` ` "\n",` ` " # pozor!\n",` ` " # nemůžeme k originu jen tak přičítat, jelikož to nevytváří nové objekty\n",` ` " # tím pádem bychom pořád měnili ten samý objekt, což nechceme -- proto list()\n",` ` " result = list(ORIGIN)\n",` ` " for dir, value in zip(dirs, index):\n",` ` " result += ((value - (self.grid_size- 1) / 2) /\n",` ` " self.grid_size) * dir * self.size\n",` ` "\n",` ` " return result\n",` ` "\n",` ` " def __get_new_cell(self, index):\n",` ` " \"\"\"Create a new cell\"\"\"\n",` ` " cell = (Cube(side_length=1/self.grid_size * self.size, color=BLUE, fill_opacity=1).move_to(\n",` ` " self.__index_to_position(index)\n",` ` " ), self.state_count - 1)\n",` ` "\n",` ` " self.__update_cell_color(index, *cell)\n",` ` "\n",` ` " return cell\n",` ` "\n",` ` " def __return_neighbouring_cell_coordinates(self, index):\n",` ` " \"\"\"Return the coordinates of the neighbourhood of a given index.\"\"\"\n",` ` " neighbourhood = set()\n",` ` " for dx in range(-1, 1 + 1):\n",` ` " for dy in range(-1, 1 + 1):\n",` ` " for dz in range(-1, 1 + 1):\n",` ` " if dx == 0 and dy == 0 and dz == 0:\n",` ` " continue\n",` ` "\n",` ` " nx = index[0] + dx\n",` ` " ny = index[1] + dy\n",` ` " nz = index[2] + dz\n",` ` "\n",` ` " if nx < 0 or nx >= self.grid_size or ny < 0 or ny >= self.grid_size or nz < 0 or nz >= self.grid_size:\n",` ` " continue\n",` ` "\n",` ` " neighbourhood.add((nx, ny, nz))\n",` ` "\n",` ` " return neighbourhood\n",` ` "\n",` ` " def __count_neighbours(self, index):\n",` ` " \"\"\"Return the number of neighbouring cells for a given index (excluding itself).\"\"\"\n",` ` " total = 0\n",` ` " for neighbour_index in self.__return_neighbouring_cell_coordinates(index):\n",` ` " if neighbour_index in self.grid:\n",` ` " total += 1\n",` ` "\n",` ` " return total\n",` ` "\n",` ` " def __return_possible_cell_change_indexes(self):\n",` ` " \"\"\"Return the indexes of all possible cells that could change.\"\"\"\n",` ` " changes = set()\n",` ` " for index in self.grid:\n",` ` " changes |= self.__return_neighbouring_cell_coordinates(index).union({\n",` ` " index})\n",` ` " return changes\n",` ` "\n",` ` " def toggle(self, index):\n",` ` " \"\"\"Toggle a given cell.\"\"\"\n",` ` " if index in self.grid:\n",` ` " self.scene.remove(self.grid[index][0])\n",` ` " del self.grid[index]\n",` ` " else:\n",` ` " self.grid[index] = self.__get_new_cell(index)\n",` ` " self.scene.add(self.grid[index][0])\n",` ` "\n",` ` " def __update_cell_color(self, index, cell, age):\n",` ` " if self.color_type == self.ColorType.FROM_PALETTE:\n",` ` " state_colors = color_gradient(self.palette, self.state_count - 1)\n",` ` "\n",` ` " cell.set_color(state_colors[age - 1])\n",` ` " else:\n",` ` " def coordToHex(n):\n",` ` " return hex(int(n * (256 / self.grid_size)))[2:].ljust(2, \"0\")\n",` ` "\n",` ` " cell.set_color(f\"#{coordToHex(index[0])}{coordToHex(index[1])}{coordToHex(index[2])}\")\n",` ` "\n",` ` " def do_iteration(self):\n",` ` " new_grid = {}\n",` ` " something_changed = False\n",` ` "\n",` ` " for index in self.__return_possible_cell_change_indexes():\n",` ` " neighbours = self.__count_neighbours(index)\n",` ` "\n",` ` " # alive rules\n",` ` " if index in self.grid:\n",` ` " cell, age = self.grid[index]\n",` ` "\n",` ` " # always decrease age\n",` ` " if age != 1:\n",` ` " age -= 1\n",` ` " something_changed = True\n",` ` "\n",` ` " # survive if within range or age isn't 1\n",` ` " if neighbours in self.survives_when or age != 1:\n",` ` " self.__update_cell_color(index, cell, age)\n",` ` " new_grid[index] = (cell, age)\n",` ` " else:\n",` ` " self.scene.remove(self.grid[index][0])\n",` ` " something_changed = True\n",` ` "\n",` ` " # dead rules\n",` ` " else:\n",` ` " # revive if within range\n",` ` " if neighbours in self.revives_when:\n",` ` " new_grid[index] = self.__get_new_cell(index)\n",` ` " self.scene.add(new_grid[index][0])\n",` ` " something_changed = True\n",` ` "\n",` ` " self.grid = new_grid\n",` ` "\n",` ` " return something_changed\n",` ` "\n",` ` "class GOLFirst(ThreeDScene):\n",` ` " def construct(self):\n",` ` " seed(0xDEADBEEF)\n",` ` "\n",` ` " self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)\n",` ` " self.begin_ambient_camera_rotation(rate=-0.20)\n",` ` "\n",` ` " grid_size = 16\n",` ` " size=3.5\n",` ` " grid = Grid(self, grid_size, [4, 5], [5], state_count=2, size=size, color_type=Grid.ColorType.FROM_COORDINATES)\n",` ` "\n",` ` " for i in range(grid_size):\n",` ` " for j in range(grid_size):\n",` ` " for k in range(grid_size):\n",` ` " if random() < 0.2:\n",` ` " grid.toggle((i, j, k))\n",` ` "\n",` ` " grid.fadeIn()\n",` ` "\n",` ` " self.wait(1)\n",` ` "\n",` ` " for i in range(50):\n",` ` " something_changed = grid.do_iteration()\n",` ` "\n",` ` " if not something_changed:\n",` ` " break\n",` ` "\n",` ` " self.wait(0.2)\n",` ` "\n",` ` " self.wait(2)\n",` ` "\n",` ` " grid.fadeOut()\n",` ` "\n",` ` "\n",` ` "class GOL(ThreeDScene):\n",` ` " def construct(self):\n",` ` " seed(0xDEADBEEF)\n",` ` "\n",` ` " self.set_camera_orientation(phi=75 * DEGREES, theta=30 * DEGREES)\n",` ` " self.begin_ambient_camera_rotation(rate=0.15)\n",` ` "\n",` ` " grid_size = 16\n",` ` " size=3.5\n",` ` "\n",` ` " # decay grid = Grid(self, grid_size, [1, 4, 8, 11] + list(range(13, 26 + 1)), list(range(13, 26 + 1)), state_count=5, size=size, color_type=Grid.ColorType.FROM_PALETTE)\n",` ` " grid = Grid(self, grid_size, [2, 6, 9], [4, 6, 8, 9], state_count=10, size=size, color_type=Grid.ColorType.FROM_PALETTE)\n",` ` "\n",` ` " for i in range(grid_size):\n",` ` " for j in range(grid_size):\n",` ` " for k in range(grid_size):\n",` ` " if random() < 0.3:\n",` ` " grid.toggle((i, j, k))\n",` ` "\n",` ` " self.wait(2)\n",` ` "\n",` ` " for i in range(70):\n",` ` " something_changed = grid.do_iteration()\n",` ` "\n",` ` " if not something_changed:\n",` ` " break\n",` ` "\n",` ` " self.wait(0.1)\n",` ` "\n",` ` " self.wait(2)\n",` ` "\n",` ` " grid.fadeOut()\n"` ` ]` ` }` ` ],` ` "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` ```} ``` ``` ```