Created
November 10, 2021 13:51
Revisions
-
zorbash created this gist
Nov 10, 2021 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,145 @@ # Animating Outputs with Kino.animate ## The Grid This is an experimentation notebook to try out `Kino.animate/3` See https://github.com/livebook-dev/kino/pull/49 <!-- livebook:{"break_markdown":true} --> The following Life implementation is adapted from [this gist](https://gist.github.com/sasa1977/6877c52c3c35c2c03c82). ```elixir defmodule Life.Grid do defstruct data: nil def new(data) when is_list(data) do %Life.Grid{data: list_to_data(data)} end def size(%Life.Grid{data: data}), do: tuple_size(data) def cell_status(grid, x, y) do grid.data |> elem(y) |> elem(x) end def next(grid) do %Life.Grid{grid | data: new_data(size(grid), &next_cell_status(grid, &1, &2))} end defp new_data(size, fun) do for y <- 0..(size - 1) do for x <- 0..(size - 1) do fun.(x, y) end end |> list_to_data end defp list_to_data(data) do data |> Enum.map(&List.to_tuple/1) |> List.to_tuple() end def next_cell_status(grid, x, y) do case {cell_status(grid, x, y), alive_neighbours(grid, x, y)} do {1, 2} -> 1 {1, 3} -> 1 {0, 3} -> 1 {_, _} -> 0 end end defp alive_neighbours(grid, cell_x, cell_y) do for x <- (cell_x - 1)..(cell_x + 1), y <- (cell_y - 1)..(cell_y + 1), x in 0..(size(grid) - 1) and y in 0..(size(grid) - 1) and (x != cell_x or y != cell_y) and cell_status(grid, x, y) == 1 do 1 end |> Enum.sum() end end ``` Define patterns as lists. `TODO` Write a parser for the [RLE](https://www.conwaylife.com/wiki/Run_Length_Encoded) format and read the pattern from an input. ```elixir queen_bee = [ [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0] ] pattern = queen_bee ``` ```elixir defmodule Life.Svg do @cell_size 10 def render(grid) do size = Life.Grid.size(grid) cells = for y <- 0..(size - 1), x <- 0..(size - 1), into: "" do status = Life.Grid.cell_status(grid, x, y) fill = if status == 0, do: "#EEE", else: "purple" "<rect x=\"#{x * @cell_size}\" y=\"#{y * @cell_size}\" width=\"10\" height=\"10\" fill=\"#{fill}\" />\n" end """ <svg viewBox="0 0 #{@cell_size * size} #{@cell_size * size}" xmlns="http://www.w3.org/2000/svg"> #{cells} </svg> """ |> Kino.Image.new(:svg) end end ``` ```elixir Kino.animate(100, Life.Grid.new(pattern), fn grid -> {:cont, Life.Svg.render(grid), Life.Grid.next(grid)} end) ``` The following snippet expects [NervesLivebook](https://github.com/livebook-dev/nerves_livebook) running in embedded mode with the picam package installed on a raspberry pi with a camera module. ```elixir Picam.set_img_effect(:sketch) Kino.animate(50, 0, fn acc -> {:cont, Kino.Image.new(Picam.next_frame(), :jpeg), acc} end) ```