{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "78b956f6",
   "metadata": {},
   "source": [
    "<h1 style=\"text-align:center; font-size: 30px;\"> Procedural Generation</h1>\n",
    "<br>\n",
    "\n",
    "<p style=\"text-align:center; font-size: 16px;\"><i>The artistry of programming lies in the fusion of creativity, problem-solving, and expression,<br>thereby providing a philosophical reflection of the human mind's capacity to create and shape the world around us.</i></p>\n",
    "<br>\n",
    "\n",
    "---\n",
    "\n",
    "<br>\n",
    "<br>\n",
    "<br>\n",
    "\n",
    "<div style=\"display: flex; justify-content: center;\">\n",
    "    <figure>\n",
    "        <img src=\"img/v5_upscale.png\" width=\"300\">\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 16px;\">\n",
    "            <strong><a href=\"https://www.midjourney.com/\">Midjourney</a></strong> by <i>V5_upscale</i>\n",
    "        </figcaption>\n",
    "    </figure>\n",
    "    <div style=\"margin-left: 50px;\">\n",
    "        <figure >\n",
    "            <img src=\"img/pg_unity.jpg\" width=\"500\">\n",
    "            <figcaption>\n",
    "                <p style=\"text-align:center; font-size: 16px;\">\n",
    "                <strong><a href=\"https://unity.com/\">Unity</a> Example:</strong> <i>Procedural Generated Terrain</i>\n",
    "            </figcaption>\n",
    "        </figure> \n",
    "        <figure style=\"margin-top: 20px;\">\n",
    "            <img src=\"img/townscaper.png\" width=\"300\">\n",
    "            <figcaption>\n",
    "                <p style=\"text-align:center; font-size: 16px;\">\n",
    "                <strong>\n",
    "                    <a href=\"https://oskarstalberg.com/Townscaper/\">Townscaper</a> Example:\n",
    "                </strong> <i>Wave Function Collapse</i>\n",
    "                </p>\n",
    "            </figcaption>\n",
    "        </figure>\n",
    "    </div>\n",
    "</div>\n",
    "\n",
    "<br>\n",
    "<br>\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f33eddd",
   "metadata": {},
   "source": [
    "There are many ways to create an artificial world. Such as writing it down on paper, drawing it or by the pure excellence of Math. Since the emergence of AI-generated art and its ascent to the highest ranks of digital representation, the masses have become aware of the importance of digital worlds in our lives.\n",
    "\n",
    "But even before elaborately trained computer models were developed, there were artists who designed digital worlds. Larger than any human being could imagine. Therefore, I would like to take you on a journey into the world of algorithms.\n",
    "\n",
    "---\n",
    "\n",
    "But what is _Procedural Generation_?\n",
    "\n",
    "Procedural generation is a technique in computer graphics and computer game development that enables the automatic creation of content using algorithms.\n",
    "\n",
    "Unlike manual content creation by developers, procedural generation allows content to be generated in a dynamic and algorithmic way, based on rules, randomness, or other mathematical methods. This can be used, for example, to generate landscapes, buildings, weapons, characters, levels, or entire worlds.\n",
    "\n",
    "It is used in various applications such as video games, movies, simulations, architecture, music, and art to increase efficiency and versatility in content creation, while creating a unique and ever-changing experience for the user.\n",
    "\n",
    "The modern game designer should be familiar with 3 methods for generating procedural worlds:\n",
    "\n",
    "- The Black Box of AI Generated Worlds <br>Demo: [Midjourney](https://www.midjourney.com/), [Nvidia Canvas](https://www.nvidia.com/en-us/studio/canvas/)\n",
    "\n",
    "- Wave Function Collapse by Oskar Stalberg <br>Demo: [Townscaper](https://oskarstalberg.com/Townscaper/)\n",
    "\n",
    "- Perlin Noise Composition / Fields by Ken Perlin\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a52b9898",
   "metadata": {},
   "source": [
    "Steigen wir ein in Medias Res\n",
    "\n",
    "Um zu verstehen wie wir Terrain erschaffen können probieren wir einfach ein paar Funktionen aus.\n",
    "\n",
    "- Random PCG\n",
    "- Sine / Cosine\n",
    "- Random Sine\n",
    "- Perlin Noise\n",
    "- Perlin Noise Composition https://gamedevacademy.org/procedural-2d-maps-unity-tutorial/\n",
    "https://web.archive.org/web/20170812230846/http://www.gameprogrammer.com/fractal.html\n",
    "https://devforum.roblox.com/t/dungeon-generation-a-procedural-generation-guide/342413/11\n",
    "\n",
    "Wichtig wir nutzen x & y als Map normale und z als höhenwert das ist in Game Engines anders."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "cf99cec5",
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "from mpl_toolkits import mplot3d\n",
    "import numpy as np\n",
    "from perlin_noise import PerlinNoise\n",
    "import random\n",
    "from itertools import chain\n",
    "import operator as op"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "8da2e7c3",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot3D_terrain(pic, cmap='viridis'):\n",
    "    '''\n",
    "    Function interprets extends a xy-plain in the z-plane,\n",
    "    by reinterpreting pixel values as height information.\n",
    "    '''\n",
    "    \n",
    "    X = np.linspace(-width/2, width/2, width)\n",
    "    Y = np.linspace(-height/3, height*2/3, height)\n",
    "    \n",
    "    X, Y = np.meshgrid(X, Y)\n",
    "    Z = np.interp(np.array(pic), [-1, 0, 1], [-64, 0, 256])\n",
    "    \n",
    "    ax = plt.axes(projection='3d')\n",
    "    ax.set_zlim(-64, 256)\n",
    "    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cmap, edgecolor='none')\n",
    "    \n",
    "    \n",
    "def plot2D_picture(pic, cmap='gray'):\n",
    "    '''\n",
    "    Just a usual image plot\n",
    "    '''\n",
    "    plt.imshow(pic, cmap=cmap)\n",
    "    plt.show()\n",
    "\n",
    "def plot_scatter(f, space):\n",
    "    '''\n",
    "    Just a scatter plot...\n",
    "    '''\n",
    "    plt.scatter(space, f)\n",
    "    plt.show()\n",
    "\n",
    "def plot_line_scatter(f, space):\n",
    "    '''\n",
    "    plots the same function overlapping as a scatter and a line plot\n",
    "    '''\n",
    "    plt.scatter(space, f, color='blue')\n",
    "    plt.plot(space, f, color='green')\n",
    "    plt.show()\n",
    "\n",
    "def plot_rel_sin(f, space):\n",
    "    '''\n",
    "    plot the input function relativ to normal sin(x)\n",
    "    '''\n",
    "    plt.scatter(space, f, color='blue')\n",
    "    plt.plot(space, np.sin(space), color='red')\n",
    "    plt.axhline(y = 0, color = 'gray', linestyle = ':')\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "fa237d88",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Constants\n",
    "width, height = 16, 16\n",
    "np.random.seed(width*height) # define seed by width & height constants"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "09663455",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "IPython.OutputArea.prototype._should_scroll = function(lines) {\n",
       "    // JS Magic bitch!\n",
       "    return false;\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "%%javascript\n",
    "IPython.OutputArea.prototype._should_scroll = function(lines) {\n",
    "    // JS Magic bitch!\n",
    "    return false;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "573c035e",
   "metadata": {},
   "source": [
    "## A word about Randomness\n",
    "\n",
    "Randomness has many definitions. But what exactly is randomness? Rolling a dice, for example, is not random. Any roll of the dice can be predicted, provided all the necessary data is available. Weather is not random either. All events can be predicted if there is enough data, the only problem is that there is never enough data. \n",
    "\n",
    "Every other example can be disproved as \"random\" with the same argumentation. Therefore, these phenomena are called pseudo-random. \n",
    "\n",
    "But this aside for the moment. There is a phenomenon in physics which is completely unpredictable. If a single photon is shot at a semi-transparent mirror, it can __never__ be predicted on which of the two detectors the photon will hit. This form of really true coincidence is used by the company (ID Quntique) [https://www.idquantique.com/random-number-generation/overview/] and sells devices which use the previously explained quantum phenomenon.\n",
    "\n",
    "But enough about the true coincidence and back to the pseudo-randomness and how computers treat this.\n",
    "\n",
    "## Pseudo Randomness\n",
    "\n",
    "Zufallszahlen in Computern werden über Formeln berechnet. Einer dieser Generatoren ist der _Linear Congruent Generator (LCG)_, dessen mathematische Grundlage leicht verdaulich ist.\n",
    "\n",
    "$$ X_{n+1} = (aX_n + c) \\;mod\\; m; \\quad n \\geq 0 $$\n",
    "\n",
    "Wenn $ c = 0 $ dann nennt man den Generator auch _Multiplicative Congruent Generator (MCG)_.\n",
    "\n",
    "Die Werte haben folgenden nutzen in der Funktion:\n",
    "\n",
    "- $X_n$ ist der Startwert oder seed\n",
    "- $X_{n+1}$ ist der folgewert der im nächsten schritt für $X_n$ eingesetzt wird\n",
    "- $a$ ist der Vorfaktor vom Startwert dieser wird skaliert, deshalb wird er skalar gennant\n",
    "- $c$ ist das hinzuaddierte Offset\n",
    "- $m$ ist der Restklassenring oder auch Modulus genannt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c00efcb8",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Schreibe einen _Linear Congruent Generator_ mit dem funktionsnamen lcg. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6a3eb7a",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Nutze die vorher geschriebene LCG Funktion, um eine weitere Funktion zu schreiben. die einen MCG korrekt implementiert."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "17708968",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Baue den lcg zu einem Genrator um. Das Python Wiki stellt dafür eine Einführung vor (Generators)[https://wiki.python.org/moin/Generators]."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f29fdb4a",
   "metadata": {},
   "source": [
    "Mit der Python Generator Syntax lässt sich ein vernünftig zu verwendender LCG implementieren."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "0e4dba58",
   "metadata": {},
   "outputs": [],
   "source": [
    "def lcg(seed: int, scalar: int, modulus: int, offset: int) -> int:\n",
    "    ''' \n",
    "    Linear Congruential Generators\n",
    "\n",
    "    X(n+1) = (a X(n) + c) mod m; n >= 0\n",
    "\n",
    "    m > 0; \n",
    "    0 <= a < m;\n",
    "    c > 0; a > 0\n",
    "\n",
    "    '''\n",
    "    assert modulus > 0, \"Modulus must be greater than 0\"\n",
    "    assert 0 <= scalar and scalar < modulus, \"Scalar must be in range 0 <= a < m\"\n",
    "\n",
    "    while seed > 1:\n",
    "        seed = (scalar*seed+offset) % modulus\n",
    "        assert seed >= 0\n",
    "        yield seed\n",
    "\n",
    "def mcg(seed: int, scalar: int, modulus: int) -> int:\n",
    "    '''\n",
    "    Multiplicative Congruential Generator\n",
    "    or Lehmer Generator\n",
    "\n",
    "    Just the same as lcg with the Property c=0 & a>0\n",
    "    '''\n",
    "    return lcg(seed=seed, scalar=scalar, modulus=modulus, offset=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "588fa1e9",
   "metadata": {},
   "source": [
    "Mit den magischen Werten \n",
    "\n",
    "- seed = $3935559000370003845$\n",
    "- scalar = $3203021881815356449$\n",
    "- modulus = $2^{64}-1$\n",
    "- offset = $11742185885288659963$\n",
    "\n",
    "lässt sich folgende Zufallsfolge erzeugen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "000a2ef5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. 3089810780120156248\n",
      "2. 8356396685252565260\n",
      "3. 1921117399837525548\n",
      "4. 14806858147081821235\n",
      "5. 2557599628047639428\n",
      "6. 16453652254840064460\n",
      "7. 15995401842808378843\n",
      "8. 681272290641816305\n",
      "9. 10955466795170118648\n"
     ]
    }
   ],
   "source": [
    "gen = lcg(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963)\n",
    "\n",
    "for i in range(1, 10):\n",
    "    print(\"{}.\".format(i), next(gen))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "694c3cb5",
   "metadata": {},
   "source": [
    "Nutzt man kleinere Werte (die ersten 4 Primzahlen), wiederholt sich der LCG nicht mehr:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "817a9142",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "3\n",
      "3\n",
      "3\n",
      "3\n",
      "3\n",
      "3\n",
      "3\n",
      "3\n",
      "3\n"
     ]
    }
   ],
   "source": [
    "gen = lcg(3, 5, 7, 2)\n",
    "\n",
    "for _ in range(10):\n",
    "    #try:\n",
    "    print(next(gen))\n",
    "  #  except:\n",
    "   #     continue"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a946da39",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Basteln wir im nächsten Schritt daraus einen QR Code. Die zur Verfügung gestellte Funktion _plot2D_picture_ erwartet ein 2-Dimensionales Array aus Zahlen.\n",
    "Nutze zur Dimensionierung des Arrays die Werte _width_ & _height_, diese sind Standardmässig auf 16 gestellt.\n",
    "\n",
    "Das Bild sollte aus den Zufallswerten des LCG bestehen.\n",
    "\n",
    "Nutze dafür folgende Werte:\n",
    "\n",
    "- seed = $3935559000370003845$\n",
    "- scalar = $3203021881815356449$\n",
    "- modulus = $2^{64}-1$\n",
    "- offset = $11742185885288659963$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "901c3f73",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10188 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "gen = lcg(3935559000370003845, 3203021881815356449, 2**64-1, 11742185885288659963)\n",
    "pic = [[next(gen) for x in range(width)] for y in range(height)]\n",
    "\n",
    "# Just a Linear Interpolation Trick\n",
    "ncol = len(pic[0])\n",
    "flattened = chain.from_iterable(pic)\n",
    "max_idx, max_val = max(enumerate(flattened), key=op.itemgetter(1))\n",
    "row = max_idx // ncol\n",
    "col = max_idx % ncol\n",
    "pic = np.interp(np.array(pic), [pic[row][0], pic[col][0]], [-64, 256])\n",
    "\n",
    "plot2D_picture(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "589ab45d",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Um nun aus dem Bild ein Terrain zu generieren nutze die Funktion _plot3D_terrain_ diese nimmt ein 2-Dimensionales Bild entgegen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "562734c8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 98304 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46c06d0d",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "## A Family of Better Random Number Generators\n",
    "\n",
    "<div style=\"display: flex; justify-content: center;\">\n",
    "    <figure style=\"margin-top: 30px;\">\n",
    "        <img src=\"img/melissa_o_neill.jpg\" width=\"300\">\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 20px;\">\n",
    "                <strong>Melissa E. O’Neill</strong>\n",
    "            </p>\n",
    "        </figcaption>\n",
    "    </figure>\n",
    "    <div style=\"margin-left: 40px; max-width: 60%; word-wrap:break-word;\">\n",
    "        <br/>\n",
    "            <p><i>Linear Congruent Generators</i> zeichnen sich durch ihre Stabilität und Geschwindigkeit als Hervorragende Zufallsgeneratoren. Doch 2014 gelang Melissa E. O'Neil ein neuer durchbruch in der Konzeption von Pseudozufallsgeneratoren. Das Problem mit existierenden Zufallsgeneratoren ist entweder ihre Stabilität (Wie vorhersehbar die Zufallszahlen sind) oder ihrer Geschwindigkeit (Wie lange der Zufallsgenerator braucht um die nächste Zufallszahl zu errechnen).</p>\n",
    "            <p>Ihr Durchbruch gelang indem Sie die Vorteile eines <i>Linear Congruent Generators</i> mit dem eines <i>XorShift Generators</i> verband. Dadurch erreichte Sie nicht nur eine Normalverteilung in den generierten Zufallszahlen (und eine damit einhergende Stabilität), Sie hatte auch eine Family von schnellen einfachen Algorithmen entwickeln. Diese nennen sich <i>PCG Permuted Congruential Generator</i>.</p>\n",
    "            <p>Die Implementierungsdetails lassen wir im nächsten Schritt aus, da diese sich nicht einfach in Python umzusetzen sind. Auf der Webseite <a href=\"https://www.pcg-random.org\" >pcg-random.org</a> lassen sich implemtierungen für C & C++ finden. Als weiterführende Literatur ist <a href=\"https://www.pcg-random.org/pdf/hmc-cs-2014-0905.pdf\" >PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation - Melissa E. O’Neill</a> als Literatur angegeben.</p>\n",
    "    </div>\n",
    "</div>\n",
    "\n",
    "Zum Glück hat _Numpy_ eine Implementierung des _PCG_. Diese findet sich unter (Numpy PCG64)[https://numpy.org/doc/stable/reference/random/bit_generators/pcg64.html]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb4dbdf8",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Schau dir im folgenden die Dokumentation zu (Numpy PCG64)[https://numpy.org/doc/stable/reference/random/bit_generators/pcg64.html].\n",
    "\n",
    "Schreibe einen kleinen Kommentar darüber welchen Output folgende Funktionen generieren:\n",
    "\n",
    "Generatoren beschreiben!!!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "00f5a13c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from numpy.random import Generator, PCG64, SeedSequence\n",
    "\n",
    "def pcg64(seed=2**5-1, seq=100):\n",
    "    sg = SeedSequence(seed)\n",
    "    n = seq//10\n",
    "    rg = [Generator(PCG64(s)) for s in sg.spawn(n)]\n",
    "\n",
    "    while 1:\n",
    "        for i in range(n):\n",
    "            gen = rg[i]\n",
    "            yield gen.random()\n",
    "\n",
    "def pcg64_gen(seed=2**5-1, seq=100):\n",
    "    gen = pcg64(seed, seq)\n",
    "    for _ in range(seq):\n",
    "        yield next(gen)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b1dca402",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "Erstelle nun ein 2-Dimensionales Array und erzeuge damit ein bild.\n",
    "\n",
    "Nutze dafür den Seed _seed = 65³-1_"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "e67cb342",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 11488 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pic = [[rand for rand in pcg64_gen(y, width)] for y in range(height)] # To Do make it 2D'\n",
    "\n",
    "plot2D_picture(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "46bf7f9c",
   "metadata": {},
   "source": [
    "## Aufgabe\n",
    "\n",
    "plotte das Terrain aus dem Vorheriger Aufgabe"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "0269b119",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 102004 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1e29115c",
   "metadata": {},
   "source": [
    "Aus diesem terrain sieht man bereits, dass die uniformierte Verteilung an Zufallszahlen bereits zu einem besser Sichtbaren Terrain führt.\n",
    "\n",
    "\n",
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07a328bd",
   "metadata": {},
   "source": [
    "# Smoothing Functions \n",
    "\n",
    "Das Problem aus voherigem Abschnitt zu lösen, müssen wir das Terrain smoothen. Was bedeutet das die Zufallszahlen smoother sein müssen.\n",
    "\n",
    "Dies erreicht man mit Trigonomertrischen Funktionen insbesondere mit dem Sinus & Cosinus. Da der Cosinus sich vom Sinus nur um eine Verschiebung von $\\phi + \\frac{\\pi}{2}$ unterscheidet\n",
    "\n",
    "$$\\cos(\\phi) = \\sin(\\phi + \\frac{\\pi}{2})$$\n",
    "\n",
    "bzw.\n",
    "\n",
    "$$\\sin(\\phi) = \\cos(\\phi - \\frac{\\pi}{2})$$\n",
    "\n",
    "beschränken wir nachfolgende Rechnungen komplett auf den Sinus.\n",
    "\n",
    "Dieser sieht wie folgt aus:\n",
    "\n",
    "- Sinus plotten\n",
    "- Sinus Terrain\n",
    "\n",
    "- kombination mit Zufallszahlen\n",
    "- Plot 1D\n",
    "- Terrain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "7f83ae3e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 28572 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "space = np.linspace(-np.pi, np.pi, width*height)\n",
    "plt.plot(space, np.sin(space), color='red')\n",
    "plt.axhline(y = 0, color = 'gray', linestyle = ':')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ce68253f",
   "metadata": {},
   "source": [
    "Daraus lässt sich wiederrum ein periodisch wiederholendes Terrain basteln, indem die Sinuswerte der x & y Werte miteinander addiert werden:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "2fc20f15",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 101640 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sint = [[np.sin(x)+np.sin(y) for x in range(width)] for y in range(height)]\n",
    "\n",
    "plot3D_terrain(sint)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e339efbf",
   "metadata": {},
   "source": [
    "Aus dieser Figur lässt sich schon erkennen, wie man Terrain im allgemeinen Smoother macht.\n",
    "\n",
    "Der nächste Schritt wäre den Zufall mit einzubauen. Eine richtige Berglandschaft besteht nicht ausschließlich aus uniform periodisch angeordneten Tälern.\n",
    "\n",
    "Die einfachste Möglichkeit wäre die Werte aus dem Zufallsgenerator als Eingabe für den Sinus zu verwenden. Wichtig ist hierbei nur, dass der PCG ausschließlich positive Werte aus dem offenem Intervall (0,1) erzeugt, während der Sinus eine Funktion ist die $[0, 2\\pi] \\mapsto [-1, 1]$ mappt. Daher folgt $f(x,y) = -1^y\\cdot\\sin(PCG64(x)) $"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c80c5857",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'np' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 2\u001b[0m\n\u001b[1;32m      1\u001b[0m T \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m4\u001b[39m\n\u001b[0;32m----> 2\u001b[0m space \u001b[38;5;241m=\u001b[39m \u001b[43mnp\u001b[49m\u001b[38;5;241m.\u001b[39mlinspace(T\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m-\u001b[39mnp\u001b[38;5;241m.\u001b[39mpi, T\u001b[38;5;241m*\u001b[39mnp\u001b[38;5;241m.\u001b[39mpi, width\u001b[38;5;241m*\u001b[39mheight)\n\u001b[1;32m      3\u001b[0m f \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mlambda\u001b[39;00m x,y: np\u001b[38;5;241m.\u001b[39msin(x)\u001b[38;5;241m*\u001b[39m(\u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m)\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39my \u001b[38;5;66;03m# just the definition x is substituted externaly\u001b[39;00m\n\u001b[1;32m      4\u001b[0m pic \u001b[38;5;241m=\u001b[39m [[f(x,y) \u001b[38;5;28;01mfor\u001b[39;00m x \u001b[38;5;129;01min\u001b[39;00m pcg64_gen(y, width)] \u001b[38;5;28;01mfor\u001b[39;00m y \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(height)]\n",
      "\u001b[0;31mNameError\u001b[0m: name 'np' is not defined"
     ]
    }
   ],
   "source": [
    "T = 4\n",
    "space = np.linspace(T*-np.pi, T*np.pi, width*height)\n",
    "f = lambda x,y: np.sin(x)*(-1)**y # just the definition x is substituted externaly\n",
    "pic = [[f(x,y) for x in pcg64_gen(y, width)] for y in range(height)]\n",
    "\n",
    "plot_rel_sin(pic, space)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcf84dab",
   "metadata": {},
   "source": [
    "Das daraus Resultierende Terrain verreint dementsprechend die Beiden Eigenschaften der Zufallsverteilung und die der periodischen Wiederholung\n",
    "\n",
    "Dennoch sieht es nicht gerade schön aus:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "7060606a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 92448 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e28e762",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d3a1ebe",
   "metadata": {},
   "source": [
    "# Anmerkungen\n",
    "\n",
    "jede andere Funktion lässt sich auch zum smoothen verwenden. Hierzu sollte die Funktion gegebenenfalls beschränkt werden.\n",
    "\n",
    "besonderns gut sind\n",
    "\n",
    "- Gaußsche Normalverteilung $$f(x) = \\frac{1}{\\sqrt{2\\pi}\\sigma}\\cdot e^{\\frac{-(x-\\mu)^2}{2\\sigma^2}}$$\n",
    "- Sigmoid $$f(x) = \\frac{1}{1+\\exp(-x)}$$\n",
    "- Absolut Werte $$f(x) = \\left| x \\right|$$\n",
    "- ZickZack Funktion $$f(x) = 1 - \\frac{2}{\\pi}\\cdot\\arccos(\\cos(x))$$"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6b63ab1",
   "metadata": {},
   "source": [
    "## Gaußsche Normalverteilung\n",
    "\n",
    "$$f(x) = \\frac{1}{\\sqrt{2\\pi}\\sigma}\\cdot e^{\\frac{-(x-\\mu)^2}{2\\sigma^2}}$$\n",
    "\n",
    "Mit der Gaußschen Normalverteilung lässt sich ein extrem realistischer Hügel erzeugen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "696eb2c1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 41940 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Random Sine Wave Distribution\n",
    "T = 10\n",
    "x = np.linspace(-T, T, T**2)\n",
    "f = lambda x: np.interp(np.random.normal(x), [-T, 0, T], [-1, 0, 1])\n",
    "\n",
    "plot_line_scatter(f(x)*4, x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "944d8191",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 11004 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Random 2D Image\n",
    "pic = [[f(x)*f(y) for x in range(width)] for y in range(height)]\n",
    "plot2D_picture(pic)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "92795745",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 101272 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Random Terrain\n",
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07cda234",
   "metadata": {},
   "source": [
    "## Sigmoid\n",
    "\n",
    "$$f(x) = \\frac{1}{1+\\exp(-x)}$$\n",
    "\n",
    "Erzeugt sehr glatte Stufen. Vorsichtig der Interessante Bereich ist von $ \\mathbb{R} \\mapsto (0, 1) $"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "649e5359",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 20436 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "sigmoid = lambda x: 1/(1+np.exp(-x)) # Numerical unstable\n",
    "\n",
    "space = np.linspace(-width, width, width**2)\n",
    "f = sigmoid(space)\n",
    "\n",
    "plot_line_scatter(f, space)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "a791a22f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 108520 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pic = [[sigmoid(y+x) for x in range(-width//2, width//2)] for y in range(-height//2, height//2)]\n",
    "\n",
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b7035878",
   "metadata": {},
   "source": [
    "## Absolut Werte\n",
    "\n",
    "$$f(x) = \\left| x \\right|$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "48c4074b",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'width' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[3], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m space \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mlinspace(\u001b[38;5;241m-\u001b[39m\u001b[43mwidth\u001b[49m, width, width\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m2\u001b[39m)\n\u001b[1;32m      2\u001b[0m f \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mabs(space)\n\u001b[1;32m      4\u001b[0m plot_line_scatter(f, space)\n",
      "\u001b[0;31mNameError\u001b[0m: name 'width' is not defined"
     ]
    }
   ],
   "source": [
    "space = np.linspace(-width, width, width**2)\n",
    "f = np.abs(space)\n",
    "\n",
    "plot_line_scatter(f, space)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "id": "09efd547",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 90996 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pic = [[np.abs(x*y) for x in range(-width//2, width//2)] for y in range(-height//2, height//2)]\n",
    "\n",
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25106a3d",
   "metadata": {},
   "source": [
    "## ZickZack \n",
    "\n",
    "$$f(x) = 1 - \\frac{2}{\\pi}\\cdot\\arccos(\\cos(x))$$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "id": "bff83ee6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 62140 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "zick_zack = lambda x: 1-(2/np.pi)*np.arccos(np.cos(x))\n",
    "space = np.linspace(-width, width, width**2)\n",
    "f = zick_zack(space)\n",
    "\n",
    "plot_line_scatter(f, space)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "2e685597",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 96856 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pic = [[zick_zack(x*y) for x in range(-width//2, width//2)] for y in range(-height//2, height//2)]\n",
    "\n",
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d2f3bfed",
   "metadata": {},
   "source": [
    "---"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3362f8d2",
   "metadata": {},
   "source": [
    "Erwähnen alles teil von perlin noise"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19c08ea6",
   "metadata": {},
   "source": [
    "# Perlin Noise\n",
    "\n",
    "The year is 1982 and programmer Kevin Flynn is trying to find proof that several games he developed were stolen by Ed Dillinger to secure his position as CEO of ENCOM.\n",
    "\n",
    "The introduction to the movie Tron can be explained in a similar way. But helping in the production of the film was __Kennneth (Ken) H. Perlin__, now a professor of computer science. His algorithm _Perlin Noise_ even brought him an Oscar in 1997. He used it to develop \"realistic textures\" for the film Tron. \n",
    "<div style=\"display: flex; justify-content: center;\">\n",
    "    <figure style=\"margin-top: 30px;\">\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 20px;\">\n",
    "                <strong>Tron (1982)</strong>\n",
    "            </p>\n",
    "        </figcaption>\n",
    "        <img src=\"img/tron.jpg\" width=\"600\">\n",
    "    </figure>\n",
    "    <figure style=\"margin-top: 30px; margin-left: 50px;\">\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 20px;\">\n",
    "                <strong>Ken Perlin</strong>\n",
    "            </p>\n",
    "        </figcaption>\n",
    "        <img src=\"img/ken-perlin.jpg\" width=\"305\">\n",
    "    </figure>\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "05934442",
   "metadata": {},
   "source": [
    " ## Perlin Noise Flow Fields\n",
    " \n",
    " <div style=\"display: flex; justify-content: left; width: 75%\">\n",
    " <figure style=\"margin-top: 90px;\">\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 20px;\">\n",
    "                <strong>Interactive Perlin Noise Field</strong>\n",
    "            </p>\n",
    "        </figcaption>\n",
    "        <iframe src=\"https://davenewt.github.io/p5-perlin-noise/\" title=\"Interactive Perlin Noise Field\" height=500 width=\"120%\"></iframe>\n",
    "        <figcaption>\n",
    "            <p style=\"text-align:center; font-size: 20px;\">\n",
    "                <strong>by Davenewt</strong>\n",
    "            </p>\n",
    "        </figcaption>\n",
    "    </figure>\n",
    " </div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19ee1765",
   "metadata": {},
   "source": [
    "https://www.redblobgames.com/maps/terrain-from-noise/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "d264d254",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'PerlinNoise' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 3\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[39m# Perlin Noise 2D\u001b[39;00m\n\u001b[1;32m      2\u001b[0m o \u001b[39m=\u001b[39m \u001b[39m1\u001b[39m\n\u001b[0;32m----> 3\u001b[0m noise1 \u001b[39m=\u001b[39m PerlinNoise(octaves\u001b[39m=\u001b[39m\u001b[39m2\u001b[39m\u001b[39m*\u001b[39mo, seed\u001b[39m=\u001b[39mwidth\u001b[39m*\u001b[39mheight)\n\u001b[1;32m      4\u001b[0m noise2 \u001b[39m=\u001b[39m PerlinNoise(octaves\u001b[39m=\u001b[39m\u001b[39m2\u001b[39m\u001b[39m*\u001b[39mo, seed\u001b[39m=\u001b[39mwidth\u001b[39m*\u001b[39mheight)\n\u001b[1;32m      5\u001b[0m noise3 \u001b[39m=\u001b[39m PerlinNoise(octaves\u001b[39m=\u001b[39m\u001b[39m2\u001b[39m\u001b[39m*\u001b[39mo, seed\u001b[39m=\u001b[39mwidth\u001b[39m*\u001b[39mheight)\n",
      "\u001b[0;31mNameError\u001b[0m: name 'PerlinNoise' is not defined"
     ]
    }
   ],
   "source": [
    "# Perlin Noise 2D\n",
    "o = 1\n",
    "noise1 = PerlinNoise(octaves=2*o, seed=width*height)\n",
    "noise2 = PerlinNoise(octaves=2*o, seed=width*height)\n",
    "noise3 = PerlinNoise(octaves=2*o, seed=width*height)\n",
    "noise4 = PerlinNoise(octaves=2*o, seed=width*height)\n",
    "\n",
    "pic = []\n",
    "for y in range(height):\n",
    "    row = []\n",
    "    for x in range(width):\n",
    "        noise_val = noise1([x/width, y/height])\n",
    "        noise_val += 0.5 * noise2([x/width, y/height])\n",
    "        noise_val += 0.25 * noise3([x/width, y/height])\n",
    "        noise_val += 0.125 * noise4([x/width, y/height])\n",
    "        row.append(noise_val)\n",
    "    pic.append(row)\n",
    "\n",
    "pic = np.array(pic)\n",
    "plot2D_picture(pic)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "707a33ec",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 100536 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Perlin Noise Terrain\n",
    "plot3D_terrain(pic)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "60a3cf60",
   "metadata": {},
   "source": [
    "<p style=\"text-align:center; font-size: 20px;\"><i> The more beautiful the digital appearance, the more real & true are the projected alternative worlds.</i></p>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8b92a91d",
   "metadata": {},
   "source": [
    " klick auf das Canvas (Vorsichtig die Animation kann je nach Computerleistung varieren)\n",
    " <div style=\"display: flex; justify-content: center;\">\n",
    "    <iframe src=\"https://gignac-cha.github.io/wave-function-collapse/\" title=\"Wave Function Collapse\" height=400 width=\"100%\"></iframe> \n",
    " </div>"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ed378ce",
   "metadata": {},
   "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.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
