Last active
September 13, 2022 14:38
-
-
Save eyaler/0e7ff1833852b03fd26c38e2e79836ee to your computer and use it in GitHub Desktop.
Plotting Pi
This file contains 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 characters
{ | |
"nbformat": 4, | |
"nbformat_minor": 0, | |
"metadata": { | |
"colab": { | |
"name": "Plotting Pi", | |
"provenance": [], | |
"collapsed_sections": [], | |
"private_outputs": true, | |
"include_colab_link": true | |
}, | |
"kernelspec": { | |
"name": "python3", | |
"display_name": "Python 3" | |
} | |
}, | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"id": "view-in-github", | |
"colab_type": "text" | |
}, | |
"source": [ | |
"<a href=\"https://colab.research.google.com/gist/eyaler/0e7ff1833852b03fd26c38e2e79836ee/plotting-pi.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"source": [ | |
"# Plotting Pi and Searching for Mona Lisa\n", | |
"\n", | |
"### \"Could somebody send me the number that draws my face?\"\n", | |
"\n", | |
"![](https://eyalgruss.com/share/plotpi.jpg?)\n", | |
"\n", | |
"Inspired by: [Plotting Pi and Searching for Mona Lisa - Numberphile](https://youtu.be/tkC1HHuuk7c)\n", | |
"\n", | |
"Shortcut to this notebook: [bit.ly/plotpi](https://bit.ly/plotpi)\n", | |
"\n", | |
"Notebook by: [Eyal Gruss](https://eyalgruss.com) \\([@eyaler](https://twitter.com/eyaler)\\)\n", | |
"\n", | |
"A curated list of online generative tools: [j.mp/generativetools](https://j.mp/generativetools)\n" | |
], | |
"metadata": { | |
"id": "Boe5k2j2LCCY" | |
} | |
}, | |
{ | |
"cell_type": "code", | |
"source": [ | |
"#@markdown Optionally upload svg file\n", | |
"\n", | |
"#@markdown You can use [ai-draw.tokyo/en](https://ai-draw.tokyo/en) to convert your photo to svg \n", | |
"from google.colab import files\n", | |
"\n", | |
"upload = files.upload()\n", | |
"if upload:\n", | |
" your_svg = list(upload.values())[0].decode()\n" | |
], | |
"metadata": { | |
"id": "qqe92EF5-38L", | |
"cellView": "form" | |
}, | |
"execution_count": null, | |
"outputs": [] | |
}, | |
{ | |
"cell_type": "code", | |
"metadata": { | |
"id": "ZPvFmqADNs-H", | |
"cellView": "form" | |
}, | |
"source": [ | |
"from google.colab.patches import cv2_imshow\n", | |
"from google.colab import files\n", | |
"from ipywidgets import Textarea\n", | |
"from mpmath import *\n", | |
"import numpy as np\n", | |
"from PIL import Image\n", | |
"import re\n", | |
"from skimage.draw import line, line_aa\n", | |
"import sys\n", | |
"try:\n", | |
" sys.set_int_max_str_digits(0)\n", | |
"except AttributeError:\n", | |
" pass\n", | |
"\n", | |
"#@markdown Pick an option from the dropdown list or enter your own expression or number:\n", | |
"number = 'your svg' #@param ['your svg', 'matt henderson', 'mona lisa', 'pi', 'e', 'phi', 'euler', 'ln(2)', 'sqrt(2)', 'sqrt(3)', 'fraction(1,7)', 'fraction(1,23)', 'fraction(1,119)'] {allow-input: true}\n", | |
"base = 10#@param {type: 'slider', min:3, max:36}\n", | |
"steps = 10000#@param {type: 'integer'}\n", | |
"canvas_size = 3000#@param {type: 'integer'}\n", | |
"svg_stride = 1#@param {type: 'number'}\n", | |
"stride = 10#@param {type: 'number'}\n", | |
"relative_angle = True #@param {type: 'boolean'}\n", | |
"anti_aliasing = False #@param {type: 'boolean'}\n", | |
"svg_scale = 20\n", | |
"\n", | |
"svgs = ['your svg', 'matt henderson', 'mona lisa']\n", | |
"if number in svgs:\n", | |
" if number == 'your svg':\n", | |
" try:\n", | |
" if your_svg:\n", | |
" svg = your_svg\n", | |
" else:\n", | |
" number = svgs[1]\n", | |
" except Exception:\n", | |
" number = svgs[1]\n", | |
" if number != 'your svg':\n", | |
" filename = number.split()[0] + '.svg'\n", | |
" !wget -q -nc --no-check-certificate https://eyalgruss.com/share/$filename\n", | |
" with open(filename) as f:\n", | |
" svg = f.read()\n", | |
" paths = []\n", | |
" for x0, y0, x1, y1 in re.findall('M (\\d+.?\\d*),(\\d+\\.?\\d*) [^\"]* (\\d+\\.?\\d*),(\\d+\\.?\\d*)\"', svg):\n", | |
" b = (y0, x0)\n", | |
" e = (y1, x1)\n", | |
" if not paths or paths[-1][-1] != b:\n", | |
" paths.append([b])\n", | |
" if paths[-1][-1] != e:\n", | |
" paths[-1].append(e)\n", | |
" paths = [[(float(y), float(x)) for y, x in path] for path in paths]\n", | |
" paths.sort(key=lambda p: (p[0][0] + p[-1][0], p[0][1] + p[-1][1]))\n", | |
" path = [p for path in paths for p in path]\n", | |
" ymin = min([p[0] for p in path])\n", | |
" ymax = max([p[0] for p in path])\n", | |
" xmin = min([p[1] for p in path])\n", | |
" xmax = max([p[1] for p in path])\n", | |
" largest = max(ymax - ymin, xmax - xmin)\n", | |
" factor = canvas_size / largest * stride / svg_scale\n", | |
" y0 = int((path[0][0] - ymin/2 - ymax/2) * factor + canvas_size/2)\n", | |
" x0 = int((path[0][1] - xmin/2 - xmax/2) * factor + canvas_size/2)\n", | |
" path = [(y1 - y0, x1 - x0) for (y0, x0), (y1, x1) in zip(path[:-1], path[1:]) if y1 != y0 or x1 != x0]\n", | |
" path_copy = path[:]\n", | |
" path = []\n", | |
" leftover_x = leftover_y = 0\n", | |
" while path_copy:\n", | |
" dy, dx = path_copy.pop(0)\n", | |
" dy += leftover_y\n", | |
" dx += leftover_x\n", | |
" angle = int(round(np.arctan2(dy, dx) % (2 * np.pi) * base / 2 / np.pi)) % base\n", | |
" dist = np.sqrt(dy*dy + dx*dx)\n", | |
" rdist = round(dist / svg_stride)\n", | |
" path += [angle] * rdist\n", | |
" leftover_y = dy - rdist * svg_stride * np.sin(angle * 2 * np.pi / base)\n", | |
" leftover_x = dx - rdist * svg_stride * np.cos(angle * 2 * np.pi / base)\n", | |
" if relative_angle:\n", | |
" path[1:] = [(a1-a0) % base for a0, a1 in zip(path[:-1], path[1:])]\n", | |
" if base != 10:\n", | |
" path = [np.base_repr(a, base=base) for a in path]\n", | |
" num_str = ''.join(str(a) for a in path)\n", | |
" stride = factor\n", | |
"else:\n", | |
" try:\n", | |
" mp.dps = max(int(steps / np.log10(base)) + 1, len(number))\n", | |
" number = mpmathify(number)\n", | |
" number = nstr(number, n=mp.dps)\n", | |
" except Exception:\n", | |
" try:\n", | |
" number = eval(number)\n", | |
" except Exception:\n", | |
" pass\n", | |
" num_str = str(number)\n", | |
" raw_input = number == num_str\n", | |
" num_str = [n for n in num_str.split('.') if n != '0'][-1]\n", | |
" try:\n", | |
" if not raw_input and base != 10:\n", | |
" num_str = np.base_repr(int(num_str), base=base)\n", | |
" except Exception:\n", | |
" pass\n", | |
" y0 = x0 = canvas_size // 2\n", | |
"display(Textarea(value=num_str, layout={'width': '1500px', 'height': '300px'}))\n", | |
"canvas = np.ones([canvas_size, canvas_size]) * 255\n", | |
"angle = 0\n", | |
"acum = 0\n", | |
"svg_out = f'<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{canvas_size}\" height=\"{canvas_size}\"><path d=\"M {x0},{y0} '\n", | |
"for a, a1 in zip(num_str,num_str[1:]+'1'):\n", | |
" angle = angle*relative_angle + int(a, base)*2*np.pi/base\n", | |
" angle %= 2 * np.pi\n", | |
" acum += stride\n", | |
" if relative_angle and a1 == '0' or not relative_angle and a1 == a:\n", | |
" continue\n", | |
" y1 = max(0, min(canvas_size - 1, int(round(y0 + acum*np.sin(angle)))))\n", | |
" x1 = max(0, min(canvas_size - 1, int(round(x0 + acum*np.cos(angle)))))\n", | |
" svg_out += f'L {x1},{y1}'\n", | |
" acum = 0\n", | |
" if anti_aliasing:\n", | |
" rr, cc, val = line_aa(y0, x0, y1, x1)\n", | |
" else:\n", | |
" rr, cc = line(y0, x0, y1, x1)\n", | |
" val = 1\n", | |
" canvas[rr, cc] = 255 * (1-val)\n", | |
" y0 = y1\n", | |
" x0 = x1\n", | |
"cv2_imshow(canvas)\n", | |
"filename = f'plotting_pi_base{base}.'\n", | |
"Image.fromarray(canvas).convert('RGB').save(filename+'png')\n", | |
"files.download(filename+'png')\n", | |
"svg_out += '\" stroke=\"black\" fill=\"none\"></path></svg>'\n", | |
"with open(filename+'svg', 'w') as f:\n", | |
" f.write(svg_out)\n", | |
"files.download(filename+'svg')" | |
], | |
"execution_count": null, | |
"outputs": [] | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment