Skip to content

Instantly share code, notes, and snippets.

@eyaler
Last active September 13, 2022 14:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eyaler/0e7ff1833852b03fd26c38e2e79836ee to your computer and use it in GitHub Desktop.
Save eyaler/0e7ff1833852b03fd26c38e2e79836ee to your computer and use it in GitHub Desktop.
Plotting Pi
Display the source blob
Display the rendered blob
Raw
{
"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