{ "cells": [ { "cell_type": "markdown", "id": "935a37a9", "metadata": {}, "source": [ "[Home](Home.ipynb)\n", "# Fractals as Unicode Art\n", "\n", "ASCII art was and is a genre, wherein one makes graphical pictures out of lexical components. The field has since expaned to all of Unicode, although I have yet to see much art labeled as such, while taking advantage of the full range of codepoints available.\n", "\n", "An advantage of printing ASCII art is it's usually easier to write characters to a text file than pixels to a picture file. An introduction to the Mandelbrot Set, a paradigm fractal, might confine itself to text file output, in order to focus mainly on the math. More colorful graphical output could follow.\n", "\n", "The Tractor in a Field pattern, presented in the Tractors Notebook, sets up our two main players: the Tractor, and the Field. The former is the read-write head while the latter is the canvas or paper the read-write head reads from and writes to. We may use the metaphor of \"plowing\" for writing, although it's really more \"planting\" that we're doing (feel free to take control of the method names in your own future versions)." ] }, { "cell_type": "markdown", "id": "6041b136", "metadata": {}, "source": [ "The picture of Barack Obama below would have started with the scan of a grayscale picture, with shades of gray replaced by ASCII characters. The final result appears squished together, with characters have less space between them, vertically at least, than we would expect from ordinary text files. This final enhancement step is a part of the pipeline developed here.\n", "\n", "\"ascii_art\"" ] }, { "cell_type": "markdown", "id": "1cbeab4c", "metadata": {}, "source": [ "The ASCII output below is the famous Mandelbrot Set. This is a very rough or course view of it, similar to what Benoit Mandelbrot first saw on a primitive display in the early days of his research. Later came the colorful zoom-ins we all enjoy.\n", "\n", "The borderline between being \"in\" versus \"not in\" the set is endlessly detailed, \"like the coastline of England\" (a comparison commonly made).\n", "\n", "The version below doubled as a kind of advertisement. I used multiple Tractors, one for writing stuff, and another for computing whether a (row, column) cell was in or out of the set, signified by \"#\" and \"\\*\" respectively. But what about \"borderline\" cells?\n", "\n", "When the Tractor keeps track of \"how quickly\" a given cell spirals away from its initial position, following the algorithm z = z * z + c, where c is the (x+yj) cell position and z starts as 0+0j, then we get the nuances of color. I use \".\" and \"@\" to hint at the many shades we get when considering this \"twilight zone\" of \"closeness to membership\".\n", "\n", "\"Making" ] }, { "cell_type": "code", "execution_count": 29, "id": "a204607a", "metadata": {}, "outputs": [], "source": [ "! python tractor_2.py" ] }, { "cell_type": "markdown", "id": "2fb6986c", "metadata": {}, "source": [ "Below is the output of our tractor_2.py, a text file. Only by shrinking the font considerably does the outline of the Mandelbrot become clear. Four markers are used, so we see only the beginnings of the colorful details. This is a \"coarse\" look at the set.\n", "\n", "\"mandelbrot\"" ] }, { "cell_type": "markdown", "id": "4203ee05", "metadata": {}, "source": [ "Is the picture below real or photoshopped? The shape is ubiquitous. When the Mandelbrot Set first burst into public consciousness, it was widely celebrated on T-shirts, posters, and coffee mugs. Then came the zoom-in movies. Which are your favorites?\n", "\n", "![CropCircle Fractal](https://i.pinimg.com/736x/ff/65/b8/ff65b8fe9718df08a09c83cfa46948bb--mandelbrot-fractal-crop-circles.jpg)" ] }, { "cell_type": "markdown", "id": "41abee87", "metadata": {}, "source": [ "In the code cells below, we're investigating the core algorithm for the Mandelbrot Set. By reiterating the formula $z = z^{2} + c$ over and over, we reach a verdict, as to whether c should be \"in\" or \"out\" and, if \"out\", in what color? How quickly has z grown?" ] }, { "cell_type": "code", "execution_count": 13, "id": "95bdfb93", "metadata": {}, "outputs": [], "source": [ "c = complex(0, .5)" ] }, { "cell_type": "code", "execution_count": 14, "id": "3c3cc6be", "metadata": {}, "outputs": [], "source": [ "z = complex(0, 0)" ] }, { "cell_type": "code", "execution_count": 15, "id": "54117cb9", "metadata": {}, "outputs": [], "source": [ "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 16, "id": "46610604", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0.5j" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z" ] }, { "cell_type": "code", "execution_count": 17, "id": "ef4db957", "metadata": {}, "outputs": [], "source": [ "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 18, "id": "23456068", "metadata": {}, "outputs": [], "source": [ "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 19, "id": "e55f13cc", "metadata": {}, "outputs": [], "source": [ "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 20, "id": "5f2d9c90", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.02734375+0.40625j)" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z" ] }, { "cell_type": "markdown", "id": "7cb00400", "metadata": {}, "source": [ "One way to measure how far z has grown is to check its r distance from the origin. Think of a clock hand spinning around. Regardless of angle, how long is the hand?" ] }, { "cell_type": "code", "execution_count": 21, "id": "c9d23e6b", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(0.4071691824832308, 1.6380026525772615)" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import cmath\n", "cmath.polar(z)" ] }, { "cell_type": "code", "execution_count": 22, "id": "3f7d367d", "metadata": {}, "outputs": [], "source": [ "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 23, "id": "80b99e54", "metadata": {}, "outputs": [], "source": [ "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 24, "id": "c4e1fa19", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.2012851310428232+0.3430086746811867j)" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z" ] }, { "cell_type": "code", "execution_count": 25, "id": "feabf233", "metadata": {}, "outputs": [], "source": [ "z = z*z + c\n", "z = z*z + c\n", "z = z*z + c\n", "z = z*z + c\n", "z = z*z + c\n", "z = z*z + c" ] }, { "cell_type": "code", "execution_count": 26, "id": "e0d6c59d", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(-0.1591998890681479+0.4047811883667751j)" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "z" ] }, { "cell_type": "code", "execution_count": 30, "id": "363a1059", "metadata": {}, "outputs": [], "source": [ "# %load tractor_2.py\n", "\"\"\"\n", "CropCircleTractor\n", "\n", "Inherits from Tractor with same __next__ based raster pattern,\n", "however in this subclass, planting a @ occurs when the underlying\n", "complex number in the corresponding plane does not diverge after\n", "10 iterations of z = z * z + c. Creates a file of ASCII art best\n", "viewed fixed width font, small font size.\n", "\n", "EXAMPLE OUTPUT: https://flic.kr/p/xyNXhN\n", "\n", "(cl) MIT License 2015 by 4dsolutions.net\n", "\"\"\"\n", "\n", "from tractor_1 import Tractor, Field\n", "\n", "class CropCircleTractor(Tractor):\n", "\n", " def config(self, x_scale, y_scale, x_offset, y_offset):\n", " self.x_scale, self.y_scale = x_scale, y_scale\n", " self.x_offset, self.y_offset = x_offset, y_offset\n", "\n", " def __next__(self):\n", " super().__next__() # updates pos\n", " c = complex((self.col + self.y_offset) * self.y_scale, \n", " (self.row + self.x_offset) * self.x_scale)\n", " z = complex(0,0)\n", " # here is where we could add more iterations and also\n", " # start to add nuance, in terms of \"shady characters\"\n", " for _ in range(15):\n", " z = z*z + c\n", " if abs(z) <= 2:\n", " self.plant(\"#\")\n", " elif abs(z) <= 100:\n", " self.plant(\"*\")\n", " elif abs(z) <= 10000:\n", " self.plant(\"@\")\n", " return z\n", " \n", " def __iter__(self):\n", " return self\n", "\n", "if __name__ == \"__main__\":\n", " the_field = Field(100, 250)\n", " the_field.add_tractor(CropCircleTractor) # initialized as added\n", " the_tractor = the_field.Ts[0] # grab reference to instance\n", " the_tractor.config(.025, .01, -50, -200)\n", " the_tractor.fuel_level = 100 * 250\n", " for z in the_tractor:\n", " if the_tractor.pos == (99, 249):\n", " break\n", " with open(\"mandelbrot.txt\", \"w\") as fractal:\n", " print(the_field, file = fractal)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.7.9" } }, "nbformat": 4, "nbformat_minor": 5 }