[
  {
    "path": ".github/workflows/checks.yml",
    "content": "name: checks.yml\n\non: [push, pull_request]\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        python-version: [\"3.12\", \"3.10\"]\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v2\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        sudo apt update && sudo apt upgrade -y\n        sudo apt-get install libcairo2-dev pdf2svg texlive texlive-science texlive-latex-recommended texlive-latex-extra \n        python -m pip install --upgrade pip\n        pip install -U pip wheel\n        make install.dev install.base install.e install.docs\n    - name: Format with [[ isort ]]\n      run: |\n        # stop the build if there are errors raised by isort\n        make isort\n    - name: Format with [[ black ]]\n      run: |\n        # stop the build if there are errors raised by black\n        make black\n    - name: Lint with [[ flake8 ]]\n      run: |\n        # stop the build if there are Python syntax errors or undefined names\n        make flake\n    - name: Docstring linting -- type check against function/method signature with [[ darglint ]]\n      run: |\n        # stop the build if docstring type-mismatch detected\n        make darglint\n    - name: Doctest coverage check with [[ interrogate ]]\n      run: |\n        # stop the build if doctest threshold does not meet\n        make interrogate\n    - name: Type check with [[ mypy ]]\n      run: |\n        # stop the build if there are type errors\n        make type\n    - name: Unit test with [[ pytest ]]\n      run: |\n        # stop the build if there are type errors\n        make test\n    - name: Make sure examples run\n      run: |\n        make images\n    - name: Make sure docs run\n      run: |\n        make docsapi\n"
  },
  {
    "path": ".github/workflows/mkdocs-publish-ghpages.yml",
    "content": "name: \"MkDocs Publish Docs on GitHub Pages CI\"\non:\n  # Manually trigger workflow\n  workflow_dispatch:\n    inputs:\n      branch:\n        description: Build MkDocs from Branch (Optional)\n        required: false\n  # Trigger when a push happens\n  # to select branches.\n  push:\n    branches:\n       - master\n\nenv:\n  PYTHON_VERSION: \"3.8\"\n  USER_SPECIFIED_BRANCH: ${{ github.event.inputs.branch }}\n  REPO_OWNER: ${{ github.repository_owner }} # https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v2\n\n      - name: Set up Python runtime\n        uses: actions/setup-python@v2\n        with:\n          python-version: ${{ env.PYTHON_VERSION }}\n\n      - name: Install Python dependencies for MkDocs\n        run: |\n          echo \"REPO_OWNER: ${{ env.REPO_OWNER }}\"\n          ls\n          pip install -r requirements/docs.txt\n\n      - name: Install all other dependencies\n        run: |\n          pip install -r requirements.txt\n\n      - name: Deploy documentation\n        env:\n          FONTAWESOME_KIT: ${{ secrets.FONTAWESOME_KIT }}\n        run: |\n          # Check if user-provided branch exists\n          # then switch to that branch.\n          if [[ -z $(git branch --list \"${{ env.USER_SPECIFIED_BRANCH }}\") ]]; \\\n            then (\\\n              echo \"Switching to branch: ${{ env.USER_SPECIFIED_BRANCH }}\" && \\\n              git checkout ${{ env.USER_SPECIFIED_BRANCH }} \\\n            ); else USER_SPECIFIED_BRANCH=${GITHUB_REF##*/} ; fi && \\\n          echo \"Current Git Branch: ${USER_SPECIFIED_BRANCH}\"\n\n          # Install chalk from current branch\n          python -m pip install \".\"\n\n          # Begin Deploying MkDocs\n          mkdocs gh-deploy --force\n          mkdocs --version\n"
  },
  {
    "path": ".github/workflows/selfassign.yml",
    "content": "# Allow users to automatically tag themselves to issues\n#\n# Usage:\n#  - a github user (a member of the repo) needs to comment\n#    with \"#self-assign\" on an issue to be assigned to them.\n#------------------------------------------------------------\n\nname: Self-assign\non:\n  issue_comment:\n    types: created\njobs:\n  one:\n    runs-on: ubuntu-latest\n    if: >-\n      (github.event.comment.body == '#take' ||\n       github.event.comment.body == '#self-assign')\n    steps:\n      - run: |\n          echo \"Assigning issue ${{ github.event.issue.number }} to ${{ github.event.comment.user.login }}\"\n          curl -H \"Authorization: token ${{ secrets.GITHUB_TOKEN }}\" \\\n               -d '{\"assignees\": [\"${{ github.event.comment.user.login }}\"]}' \\\n                  https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/assignees\n          echo \"Done 🔥 \"\n"
  },
  {
    "path": ".gitignore",
    "content": "# User Defined\n.vscode\n\n# Temporary files\n*.swp\n\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\n.archives/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## `v0.2.2` – 2024-06-14\n\nAdded:\n\n- Isometric example (#130)\n- Associative reduce (#129)\n- Subdiagram (#116)\n- Property-based testing (#111)\n\nChanged:\n\n- Rewrite of core (#132)\n- Bug fixes and typos (#110, #127, #128)\n\n## `v0.2.1` — 2022-09-07\n\nAdded:\n\n- Traces and envelopes\n- TikZ backend\n- Arrows and connections\n- More examples (e.g, Big Ben)\n- Gallery of examples\n- Documentation\n\nChanged:\n\n- Use `planar` library for geometry\n- Major code refactoring\n\n## `v0.1.2` — 2022-05-20\n\nOther changes:\n\n- Add license (MIT license)\n- Remove dependency on `streamlit`\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Dan Oneață and Alexander Rush\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include README.md\ninclude chalk/py.typed  # marker file for PEP 561\n\ninclude CHANGELOG.md\ninclude changelog.md\ninclude LICENSE\ninclude LICENSE.txt\ninclude CITATION.cff\ninclude *.cff  # citation info\n\ninclude MANIFEST.in\ninclude pyproject.toml\ninclude setup.py\ninclude setup.cfg\n\ninclude requirements.txt\nrecursive-include requirements *.txt\n\nrecursive-exclude examples *\nrecursive-exclude tests *\nrecursive-exclude docs *\nrecursive-exclude site *\nrecursive-exclude apps *\nrecursive-exclude .archives *\n\nexclude .flake8\nexclude .gitignore\nexclude .pre-commit-config.yaml\nexclude mkdocs.yml\nexclude Makefile\n"
  },
  {
    "path": "Makefile",
    "content": "\n# maintenance\n.PHONY: isort flake black test type interrogate darglint \\\n\t\tclean cleanall style docs check\n\n# installation\n.PHONY: install installplus install.e install.all\n\t\tinstall.base install.dev install.docs\n\n# uninstallation\n.PHONY: uninstall uninstallplus uninstall.e uninstall.all \\\n\t\tuninstall.base uninstall.dev uninstall.docs\n\n# documentation\n.PHONY: pregendocs.doc pregendocs.examples pregendocs.local pregendocs.remote \\\n\t\tgendocs \\\n\t\tpostgendocs.doc postgendocs.local postgendocs.remote gendocsall.local\n\n# generate examples\n.PHONY: intro squares hanoi escher_square lattice lenet logo \\\n\t\thilbert koch tensor latex hex_variation images \\\n                tree serve\n####------------------------------------------------------------####\n\n# libname is either same as PACKAGE_NAME or\n#             as on PYPI (replace - with _)\nLIBNAME := chalk_diagrams\nPACKAGE_NAME := chalk\nTESTPYPI_DOWNLOAD_URL := \"https://test.pypi.org/simple/\"\nPYPIPINSTALL := \"python -m pip install -U --index-url\"\nPIPINSTALL_PYPITEST := \"$(PYPIPINSTALL) $(TESTPYPI_DOWNLOAD_URL)\"\nPKG_INFO := \"import pkginfo; dev = pkginfo.Develop('.'); print((dev.$${FIELD}))\"\n\n# This is where you store the eggfile\n# and other generated archives\nARCHIVES_DIR := \".archives\"\n\n# Folder path for tests\nTESTS_DIR := \"tests\"\n\n# Interrogate will flag the test as FAILED if\n# % success threshold is under the following value\nINTERROGATE_FAIL_UNDER := 0  # ideally this should be 100\n\n# Specify paths of various dependency files\nREQ_FOLDER := \"requirements\"\n# location: requirements.txt\nREQ_FILE := \"requirements.txt\"\n# location: requirements/dev.txt\nDEV_REQ_FILE := \"dev.txt\"\n# location: requirements/docs.txt\nDOCS_REQ_FILE := \"docs.txt\"\n\n####------------------------------------------------------------####\n\n### Code maintenance\n\n## Run isort\n\nisort:\n\t@ echo \"✨ Applying import sorter: isort ... ⏳\"\n\t# The settings are maintained in setup.cfg file.\n\tisort $(PACKAGE_NAME) setup.py \\\n\t\t tests \\\n\n## Run black\n\nblack:\n\t@ echo \"✨ Applying formatter: black ... ⏳\"\n\tblack --target-version py38 --line-length 79 $(PACKAGE_NAME) setup.py \\\n\t\t tests \\\n\n## Run flake8\n\nflake:\n\t@ echo \"✨ Applying formatter: flake8 ... ⏳\"\n\tflake8 --show-source $(PACKAGE_NAME) setup.py \\\n\t\t tests \\\n\n## Run pytest\n\ntest:\n\t@ echo \"✨ Run tests: pytest ... ⏳\"\n\t@if [ -d \"$(TESTS_DIR)\" ]; then pytest $(TESTS_DIR); else echo \"\\n\\t🔥 No tests configured yet. Skipping tests.\\n\"; fi\n\n## Run mypy\n\ntype:\n\t@ echo \"✨ Applying type checker: mypy ... ⏳\"\n\tmypy --strict --ignore-missing-imports --no-warn-unused-ignores $(PACKAGE_NAME) \\\n\t\t tests \\\n\n## Run darglint\n\ndarglint:\n\t@ echo \"✨ Applying docstring type checker: darglint ... ⏳\"\n\t# The settings are maintained in setup.cfg file.\n\tdarglint -v 2 $(PACKAGE_NAME) --ignore-properties\n\n## Run interrogate\n\ninterrogate:\n\t@ echo \"✨ Applying doctest checker: interrogate ... ⏳\"\n\t$(eval INTERROGATE_CONFIG := -vv --ignore-nested-functions --ignore-semiprivate --ignore-private --ignore-magic --ignore-module --ignore-init-method --fail-under $(INTERROGATE_FAIL_UNDER))\n\t$(eval INTERROGATE_COMMAND := interrogate $(INTERROGATE_CONFIG))\n\t# Check tests folder\n\t@if [ -d \"$(TESTS_DIR)\" ]; then $(INTERROGATE_COMMAND) $(TESTS_DIR); else echo \"\\n\\t🔥 No tests configured yet. Skipping tests.\\n\"; fi\n\t# Check package folder\n\t@$(INTERROGATE_COMMAND) $(PACKAGE_NAME)\n\n## Cleanup\n#\n# Instruction:\n#\n# make clean    : if only cleaning artifacts created after running,\n#                 code, tests, etc.\n# make cleanall : if cleaning all artifacts (including the ones\n#                 generated after creating dist and wheels).\n#\n# Note: archives created (dist and wheels) are stored in\n#       $(ARCHIVES_DIR). This is defined at the top of this Makefile.\n#--------------------------------------------------------------------\n\nclean:\n\t@ echo \"🪣 Cleaning repository ... ⏳\"\n\trm -rf \\\n\t\t.ipynb_checkpoints **/.ipynb_checkpoints \\\n\t\t.pytest_cache **/.pytest_cache \\\n\t\t**/__pycache__ **/**/__pycache__\n\ncleanall: clean\n\t@ echo \"🪣 Cleaning dist/archive files ... ⏳\"\n\trm -rf build/* dist/* $(PACKAGE_NAME).egg-info/* $(ARCHIVES_DIR)/*\n\n## Style Checks and Unit Tests\n\nstyle: clean isort black flake clean\n\ndocs: clean darglint interrogate clean\n\ncheck: style docs type test clean\n\n####------------------------------------------------------------####\n\n### Code Installation\n\n## Install for development (from local repository)\n#\n# Instruction: Contributors will need to run...\n#\n# - \"make installplus\": if installing for the first time or want to\n#                         update to the latest dev-requirements or\n#                         other extra dependencies.\n# - \"make install.e\"  : if only installing the local source (after\n#                         making some changes) to the source code.\n#--------------------------------------------------------------------\n\n# .PHONY: install.e\ninstall.e: clean\n\t@echo \"📀🟢🔵 Installing $(PACKAGE_NAME) from local source ... ⏳\"\n\tpython -m pip install -Ue .[tikz,latex,png,svg]\n\n# .PHONY: install\ninstall: clean install.base install.e\n\t@echo \"📀🟢🟡🔵 Installing $(PACKAGE_NAME) and base-dependencies from PyPI ... ⏳\"\n\n# .PHONY: installplus\ninstallplus: install.all install.e\n\t@echo \"📀🟢🟡🔵🟠 Installing $(PACKAGE_NAME) and all-dependencies from PyPI ... ⏳\"\n\n# .PHONY: install.all\ninstall.all: clean install.base install.dev install.docs\n\t@echo \"📀🟢🟡 Installing $(PACKAGE_NAME)'s all-dependencies from PyPI ... ⏳\"\n\n# .PHONY: install.base\ninstall.base:\n\t@echo \"📀🟢🟡 Installing from: $(DEV_REQ_FILE) ... ⏳\"\n\tif [ -f $(REQ_FILE) ]; then python -m pip install -U -r $(REQ_FILE); fi\n\n# .PHONY: install.dev\ninstall.dev:\n\t@echo \"📀🟢🟡 Installing from: $(DEV_REQ_FILE) ... ⏳\"\n\tif [ -f $(REQ_FOLDER)/$(DEV_REQ_FILE) ]; then python -m pip install -U -r $(REQ_FOLDER)/$(DEV_REQ_FILE); fi\n\n# .PHONY: install.docs\ninstall.docs:\n\t@echo \"📀🟢🟡 Installing from: $(DOCS_REQ_FILE) ... ⏳\"\n\t@if [ -f $(REQ_FOLDER)/$(DOCS_REQ_FILE) ]; then python -m pip install -U -r $(REQ_FOLDER)/$(DOCS_REQ_FILE); fi\n\n## Uninstall from dev-environment\n\n# .PHONY: uninstall.e\nuninstall.e: clean\n\t@echo \"📀🟢🔵 Uninstalling $(PACKAGE_NAME)' local editable version ... ⏳\"\n\t@# https://stackoverflow.com/questions/48826015/uninstall-a-package-installed-with-pip-install\n\trm -rf \"$(LIBNAME).egg-info\"\n\n# .PHONY: uninstall\nuninstall: clean uninstall.base uninstall.e\n\t@echo \"📀🔴🟡🔵 Uninstalling $(PACKAGE_NAME) and base-dependencies from PyPI ... ⏳\"\n\n# .PHONY: uninstallplus\nuninstallplus: uninstall.all uninstall.e\n\t@echo \"📀🔴🟡🔵🟠 Uninstalling $(PACKAGE_NAME) and all-dependencies from PyPI ... ⏳\"\n\n# .PHONY: uninstall.all\nuninstall.all: clean uninstall.base uninstall.dev uninstall.docs clean\n\t@echo \"📀🔴🟡 Uninstalling $(PACKAGE_NAME)'s all-dependencies from PyPI ... ⏳\"\n\n# .PHONY: uninstall.base\nuninstall.base:\n\t@echo \"📀🔴🟡 Uninstalling from: $(DEV_REQ_FILE) ... ⏳\"\n\tif [ -f $(REQ_FILE) ]; then python -m pip uninstall -r $(REQ_FILE); fi\n\n# .PHONY: uninstall.dev\nuninstall.dev:\n\t@echo \"📀🔴🟡 Uninstalling from: $(DEV_REQ_FILE) ... ⏳\"\n\tif [ -f $(REQ_FOLDER)/$(DEV_REQ_FILE) ]; then python -m pip uninstall -r $(REQ_FOLDER)/$(DEV_REQ_FILE); fi\n\n# .PHONY: uninstall.docs\nuninstall.docs:\n\t@echo \"📀🔴🟡 Uninstalling from: $(DEV_REQ_FILE) ... ⏳\"\n\t@if [ -f $(REQ_FOLDER)/$(DOCS_REQ_FILE) ]; then python -m pip uninstall -r $(REQ_FOLDER)/$(DOCS_REQ_FILE); fi\n\n\n## Install from test.pypi.org\n#\n# Instruction:\n#\n# 🔥 This is useful if you want to test the latest released package\n#    from the TestPyPI, before you push the release to PyPI.\n#--------------------------------------------------------------------\n\npipinstalltest:\n\t@echo \"💿 Installing $(PACKAGE_NAME) from TestPyPI ($(TESTPYPI_DOWNLOAD_URL)) ... ⏳\"\n\t# Example Usage:\n\t#   👉 To run a command like:\n\t#   > python -m pip install -U --index-url https://test.pypi.org/simple/ $(PACKAGE_NAME)==$(VERSION)\n\t#   👉 Run the following command:\n\t#   > make pipinstalltest VERSION=\"0.1.0\"\n\t#   👉 Specifying VERSION=\"#.#.#\" installs a specific version.\n\t#      If no version is specified, the latest version is installed from TestPyPI.\n\t@if [ $(VERSION) ]; then $(PIPINSTALL_PYPITEST) $(PACKAGE_NAME)==$(VERSION); else $(PIPINSTALL_PYPITEST) $(PACKAGE_NAME); fi;\n\n## Gendocs\n\n# .PHONY: gendocs\ngendocs:\n\t@echo \"🔥 Generate documentation with MkDocs ... ⏳\"\n\t# generate documentation\n\tmkdocs serve --dirtyreload\n\n## Postgendocs\n\n# .PHONY: postgendocs.doc\npostgendocs.doc:\n\t#echo \"Cleanup docs... ⏳\"\n\trm -rf docs/doc\n\n# .PHONY: postgendocs.local\npostgendocs.local: postgendocs.doc\n\n# .PHONY: postgendocs.remote\npostgendocs.remote: postgendocs.doc\n\n# .PHONY: gendocsall.local\ngendocsall.local: pregendocs.local gendocs postgendocs.local\n\n# .PHONY: gendocsall.remote\n# gendocsall.remote: pregendocs.remote gendocs postgendocs.remote\n# \t@ # Use mkdocs-publish-ghpages.yml action instead of this make command\n\n\n####------------------------------------------------------------####\n\n### Generate Output for Examples\n\nintro:\n\tpython examples/intro.py\n\nsquares:\n\tpython examples/squares.py\n\nhanoi:\n\tpython examples/hanoi.py\n\nescher_square:\n\tpython examples/escher_square_limit.py\n\nlattice:\n\tpython examples/lattice.py\n\nlenet:\n\tpython examples/lenet.py\n\nlogo:\n\tpython examples/logo.py\n\nhilbert:\n\tpython examples/hilbert.py\n\nkoch:\n\tpython examples/koch.py\n\ntensor:\n\tpython examples/tensor.py\n\nlatex:\n\tpython examples/latex.py\n\nhex_variation:\n\tpython examples/hex_variation.py\n\ntree:\n\tpython examples/tree.py\n\ntournament:\n\tpython examples/tournament-network.py\n\nparade:\n\tpython examples/rectangle_parade.py\n\narrows:\n\tpython examples/arrows.py\n\npath:\n\tpython examples/path.py\n\nimages: squares hanoi intro  hilbert koch tensor hex_variation tree tournament parade arrows path lenet escher_square logo\n\t@echo \"🎁 Generate all examples ... ⏳\"\n\nserve:\n\tpython -m http.server 8080 -d examples/output/\n\ndocsapi:\n\tpython docs/api/*.py\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/examples/output/logo-sm.png\" width=300></p>\n\nChalk is a declarative drawing library.\nThe API draws heavy inspiration from\nHaskell's [diagrams](https://diagrams.github.io/),\nScala's [doodle](https://github.com/creativescala/doodle/) and\nJeremy Gibbons's lecture notes on [Functional Programming for Domain−Specific Languages](http://www.cs.ox.ac.uk/publications/publication7583-abstract.html).\n\nThe documentation is available at [https://chalk-diagrams.github.io](https://chalk-diagrams.github.io).\n\n⚠️ The library is still very much work in progress and subject to change.\n\n## Installation\n\nThe library is available on PyPI as `chalk-diagrams` and can be installed with `pip`:\n\n```bash\npip install git+https://github.com/chalk-diagrams/chalk/\n```\n\nOn Debian (or Colab) you will need to install Cairo for [PyCairo](https://pycairo.readthedocs.io)\n\n```bash\nsudo apt-get install libcairo2-dev\n```\n\nIf you want to use the LaTeX extension, run:\n\n```bash\npip install chalk-diagrams[latex]\n```\n\nFor the LaTeX extension you might need to install `pdf2svg` and `texlive`;\non Debian these dependencies can be installed as follows:\n\n```bash\nsudo apt-get install pdf2svg texlive texlive-science texlive-latex-recommended texlive-latex-extra\n```\n\n**Installation with Conda**\n\nYou can install the library with **conda** from `conda-forge` channel.\n\n```powershell\nconda install -c conda-forge chalk-diagrams\n```\n\n## Overview\n\nBelow we provide a brief introduction of the main functionality of the library.\nThese examples are available in the `examples/intro.py` file.\n\nWe start by importing the [`colour`](https://github.com/vaab/colour) module and the `diagrams` functions:\n\n```python\nfrom colour import Color\nfrom chalk import *\n```\n\nWe also define some colors that will be shortly used:\n\n```python\npapaya = Color(\"#ff9700\")\nblue = Color(\"#005FDB\")\n```\n\nWe can easily create basic shapes (the functions `circle`, `square`, `triangle`) and style them with various attributes (the methods`fill_color`, `line_color`, `line_width`).\nFor example:\n\n```python\nd = circle(1).fill_color(papaya)\n```\n\n![circle](https://raw.githubusercontent.com/chalk-diagrams/chalk/master/examples/output/intro-01.png)\n\nThe diagram can be saved to an image using the `render` method:\n\n```python\nd.render(\"examples/output/intro-01.png\", height=64)\n```\n\nWe can glue together two diagrams using the combinators `atop` (or `+`), `beside` (or `|`), `above` (or `/`).\nFor example:\n\n```python\ncircle(0.5).fill_color(papaya) | square(1).fill_color(blue)\n```\n\nwhich is equivalent to\n\n```python\ncircle(0.5).fill_color(papaya).beside(square(1).fill_color(blue), unit_x)\n```\n\nThis code produces the following image:\n\n![atop](https://raw.githubusercontent.com/chalk-diagrams/chalk/master/examples/output/intro-02.png)\n\nWe also provide combinators for a list of diagrams:\n`hcat` for horizontal composition, `vcat` for vertical composition.\nFor example:\n\n```python\nhcat(circle(0.1 * i) for i in range(1, 6)).fill_color(blue)\n```\n![hcat](https://raw.githubusercontent.com/chalk-diagrams/chalk/master/examples/output/intro-03.png)\n\nWe can use Python functions to build more intricate diagrams:\n\n```python\ndef sierpinski(n: int, size: int) -> Diagram:\n    if n <= 1:\n        return triangle(size)\n    else:\n        smaller = sierpinski(n - 1, size / 2)\n        return smaller.above((smaller | smaller).center_xy())\n\nd = sierpinski(5, 4).fill_color(papaya)\n```\n\n![sierpinski](https://raw.githubusercontent.com/chalk-diagrams/chalk/master/examples/output/intro-04.png)\n\n### Gallery of examples\n\nFor more examples, please check the `examples` folder;\ntheir output is illustrated below:\n\n<table>\n<tr>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/squares.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/squares.py\">squares.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/logo.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/logo.py\">logo.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/escher-square-limit.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/escher_square_limit.py\">escher_square_limit.py</a></code></td>\n</tr>\n<tr>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/hilbert.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/hilbert.py\">hilbert.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/koch.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/koch.py\">koch.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/hex-variation.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/hex_variation.py\">hex-variation.py</a></code></td>\n</tr>\n<tr>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/lenet.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/lenet.py\">lenet.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/tensor.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/tensor.py\">tensor.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/hanoi.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/hanoi.py\">hanoi.py</a></code></td>\n</tr>\n<tr>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/tree.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/tree.py\">tree.py</a></code></td>\n<td align=\"center\"><img src=\"https://raw.githubusercontent.com/chalk-diagrams/chalk/master/doc/imgs/lattice.png\"><br><code><a href=\"https://github.com/chalk-diagrams/chalk/tree/master/examples/lattice.py\">lattice.py</a></code></td>\n</tr>\n<!--<tr>\n</tr>\n-->\n</table>\n\nThese scripts can be run as follows:\n\n```bash\npython examples/squares.py\n```\n\n## Authors\n\n- [Dan Oneață](http://doneata.bitbucket.io/)\n- [Alexander Rush](http://rush-nlp.com/)\n\nSpecial thanks to:\n- [Sugato Ray](https://github.com/sugatoray/), for his significant contributions and suggestions;\n- [Ionuț G. Stan](http://igstan.ro/), for providing many useful insights and comments.\n"
  },
  {
    "path": "chalk/__init__.py",
    "content": "import math\nimport sys\nfrom typing import TYPE_CHECKING, Iterable, List, Optional, Tuple, Union\n\nif sys.version_info >= (3, 8):\n    from importlib import metadata\nelse:\n    import importlib_metadata as metadata\n\nimport chalk.align as align\nfrom chalk.align import *  # noqa: F403\nfrom chalk.arrow import ArrowOpts, arrow_at, arrow_between, arrow_v\nfrom chalk.combinators import *  # noqa: F403\nfrom chalk.core import set_svg_draw_height, set_svg_height\nfrom chalk.envelope import Envelope\nfrom chalk.monoid import Maybe, MList, Monoid\nfrom chalk.shapes import *  # noqa: F403\nfrom chalk.style import Style\nfrom chalk.subdiagram import Name\nfrom chalk.trail import Trail\nfrom chalk.transform import (\n    P2,\n    V2,\n    Affine,\n    BoundingBox,\n    from_radians,\n    origin,\n    to_radians,\n    unit_x,\n    unit_y,\n)\nfrom chalk.types import Diagram\n\nif not TYPE_CHECKING:\n\n    # Set library name the same as on PyPI\n    # must be the same as setup.py:setup(name=?)\n    __libname__: str = \"chalk-diagrams\"  # custom dunder attribute\n    __version__: str = metadata.version(__libname__)\n"
  },
  {
    "path": "chalk/align.py",
    "content": "from chalk.transform import V2, Affine, origin, unit_x, unit_y\nfrom chalk.types import Diagram\n\n# Functions mirroring Diagrams.Align and Diagrams.2d.Align\n\n\ndef align_to(self: Diagram, v: V2) -> Diagram:\n    envelope = self.get_envelope()\n    t = Affine.translation(-envelope.envelope_v(v))\n    return self.apply_transform(t)\n\n\ndef snug(self: Diagram, v: V2) -> Diagram:\n    \"Align based on the trace.\"\n    trace = self.get_trace()\n    d = trace.trace_v(origin, v)\n    assert d is not None\n    t = Affine.translation(-d)\n    return self.apply_transform(t)\n\n\ndef center(self: Diagram) -> Diagram:\n    return self.center_xy()\n\n\n# 2D versions\n\n\ndef align_t(self: Diagram) -> Diagram:\n    return align_to(self, -unit_y)\n\n\ndef align_b(self: Diagram) -> Diagram:\n    return align_to(self, unit_y)\n\n\ndef align_r(self: Diagram) -> Diagram:\n    return align_to(self, unit_x)\n\n\ndef align_l(self: Diagram) -> Diagram:\n    return align_to(self, -unit_x)\n\n\ndef align_tl(self: Diagram) -> Diagram:\n    return align_l(align_t(self))\n\n\ndef align_br(self: Diagram) -> Diagram:\n    return align_r(align_b(self))\n\n\ndef align_tr(self: Diagram) -> Diagram:\n    return align_r(align_t(self))\n\n\ndef align_bl(self: Diagram) -> Diagram:\n    return align_l(align_b(self))\n\n\ndef center_xy(self: Diagram) -> Diagram:\n    envelope = self.get_envelope()\n    if envelope.is_empty:\n        return self\n    t = Affine.translation(-envelope.center)\n    return self.apply_transform(t)\n\n\ndef scale_uniform_to_x(self: Diagram, x: float) -> Diagram:\n    \"\"\"Apply uniform scaling along the x-axis.\n\n    Args:\n        self (Diagram): Diagram object.\n        x (float): Amount of scaling along the x-axis.\n\n    Returns:\n        Diagram: A diagram object.\n    \"\"\"\n    envelope = self.get_envelope()\n    if envelope.is_empty:\n        return self\n    α = x / envelope.width\n    return self.scale(α)\n\n\ndef scale_uniform_to_y(self: Diagram, y: float) -> Diagram:\n    \"\"\"Apply uniform scaling along the y-axis.\n\n    Args:\n        self (Diagram): Diagram object.\n        y (float): Amount of scaling along the y-axis.\n\n    Returns:\n        Diagram: A diagram object.\n    \"\"\"\n    envelope = self.get_envelope()\n    if envelope.is_empty:\n        return self\n    α = y / envelope.height\n    return self.scale(α)\n"
  },
  {
    "path": "chalk/arrow.py",
    "content": "from dataclasses import dataclass, field\nfrom typing import List, Optional\n\nfrom colour import Color\n\nfrom chalk.shapes import ArcSegment, ArrowHead, arc_seg, dart\nfrom chalk.style import Style\nfrom chalk.subdiagram import Name, Subdiagram\nfrom chalk.trail import Trail\nfrom chalk.transform import P2, V2, unit_x\nfrom chalk.types import Diagram\n\nblack = Color(\"black\")\n# arrow heads\n\n\n@dataclass\nclass ArrowOpts:\n    head_style: Style = field(default_factory=Style)\n    head_pad: float = 0.0\n    tail_pad: float = 0.0\n    head_arrow: Optional[Diagram] = None\n    shaft_style: Style = field(default_factory=Style)\n    trail: Optional[Trail] = None\n    arc_height: float = 0.0\n\n\n# Arrow connections.\n\n\ndef connect(\n    self: Diagram, name1: Name, name2: Name, style: ArrowOpts = ArrowOpts()\n) -> Diagram:\n    def f(subs: List[Subdiagram], dia: Diagram) -> Diagram:\n        sub1, sub2 = subs\n\n        ps = sub1.get_location()\n        pe = sub2.get_location()\n\n        return dia + arrow_between(ps, pe, style)\n\n    return self.with_names([name1, name2], f)\n\n\ndef connect_outside(\n    self: Diagram, name1: Name, name2: Name, style: ArrowOpts = ArrowOpts()\n) -> Diagram:\n    def f(subs: List[Subdiagram], dia: Diagram) -> Diagram:\n        sub1, sub2 = subs\n\n        loc1 = sub1.get_location()\n        loc2 = sub2.get_location()\n\n        tr1 = sub1.get_trace()\n        tr2 = sub2.get_trace()\n\n        v = loc2 - loc1\n        midpoint = loc1 + v / 2\n\n        ps = tr1.trace_p(midpoint, -v)\n        pe = tr2.trace_p(midpoint, v)\n\n        assert ps is not None, \"Cannot connect\"\n        assert pe is not None, \"Cannot connect\"\n\n        return dia + arrow_between(ps, pe, style)\n\n    return self.with_names([name1, name2], f)\n\n\ndef connect_perim(\n    self: Diagram,\n    name1: Name,\n    name2: Name,\n    v1: V2,\n    v2: V2,\n    style: ArrowOpts = ArrowOpts(),\n) -> Diagram:\n    def f(subs: List[Subdiagram], dia: Diagram) -> Diagram:\n        sub1, sub2 = subs\n\n        loc1 = sub1.get_location()\n        loc2 = sub2.get_location()\n\n        tr1 = sub1.get_trace()\n        tr2 = sub2.get_trace()\n\n        ps = tr1.max_trace_p(loc1, v1)\n        pe = tr2.max_trace_p(loc2, v2)\n\n        assert ps is not None, \"Cannot connect\"\n        assert pe is not None, \"Cannot connect\"\n\n        return dia + arrow_between(ps, pe, style)\n\n    return self.with_names([name1, name2], f)\n\n\n# Arrow primitives\n\n\ndef arrow(length: float, style: ArrowOpts = ArrowOpts()) -> Diagram:\n    from chalk.core import Primitive\n\n    if style.head_arrow is None:\n        arrow: Diagram = Primitive.from_shape(ArrowHead(dart()))\n    else:\n        arrow = style.head_arrow\n    arrow = arrow._style(style.head_style)\n    t = style.tail_pad\n    l_adj = length - style.head_pad - t\n    if style.trail is None:\n        segment = arc_seg(P2(l_adj, 0), style.arc_height)\n        shaft = segment.stroke()\n        if isinstance(segment.segments[-1], ArcSegment):\n            seg = segment.segments[-1]\n            tan = -(seg.q - seg.center.reflect_y()).perpendicular()  # type: ignore\n            φ = tan.angle\n            arrow = arrow.rotate(φ)\n            if style.arc_height < 0:\n                arrow = arrow.rotate(180)\n    else:\n        shaft = style.trail.stroke().scale_uniform_to_x(l_adj).fill_opacity(0)\n\n        if isinstance(style.trail.segments[-1], ArcSegment):\n            arrow = arrow.rotate(-style.trail.segments[-1].angle)\n\n    return shaft._style(style.shaft_style).translate_by(\n        t * unit_x\n    ) + arrow.translate_by((l_adj + t) * unit_x)\n\n\ndef arrow_v(vec: V2, style: ArrowOpts = ArrowOpts()) -> Diagram:\n    arr = arrow(vec.length, style)\n    return arr.rotate(-vec.angle)\n\n\ndef arrow_at(base: P2, vec: V2, style: ArrowOpts = ArrowOpts()) -> Diagram:\n    return arrow_v(vec, style).translate_by(base)\n\n\ndef arrow_between(\n    start: P2, end: P2, style: ArrowOpts = ArrowOpts()\n) -> Diagram:\n    return arrow_at(start, end - start, style)\n"
  },
  {
    "path": "chalk/backend/__init__.py",
    "content": ""
  },
  {
    "path": "chalk/backend/cairo.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any, Optional\n\nfrom chalk.monoid import MList\nfrom chalk.shapes import (\n    ArcSegment,\n    ArrowHead,\n    Image,\n    Latex,\n    Path,\n    Segment,\n    SegmentLike,\n    Spacer,\n    Text,\n    from_pil,\n)\nfrom chalk.style import Style\nfrom chalk.transform import P2, Affine, to_radians, unit_x, unit_y\nfrom chalk.types import Diagram\nfrom chalk.visitor import DiagramVisitor, ShapeVisitor\n\nif TYPE_CHECKING:\n    from chalk.core import ApplyName, ApplyStyle, ApplyTransform, Primitive\n\n\nIdent = Affine.identity()\nPyCairoContext = Any\nEMPTY_STYLE = Style.empty()\n\n\ndef tx_to_cairo(affine: Affine) -> Any:\n    import cairo\n\n    def convert(a, b, c, d, e, f):  # type: ignore\n        return cairo.Matrix(a, d, b, e, c, f)  # type: ignore\n\n    return convert(*affine[:6])  # type: ignore\n\n\nclass ToList(DiagramVisitor[MList[Any], Affine]):\n    \"\"\"Compiles a `Diagram` to a list of `Primitive`s. The transformation `t`\n    is accumulated upwards, from the tree's leaves.\n    \"\"\"\n\n    A_type = MList[Any]\n\n    def visit_primitive(\n        self, diagram: Primitive, t: Affine = Ident\n    ) -> MList[Primitive]:\n        return MList([diagram.apply_transform(t)])\n\n    def visit_apply_transform(\n        self, diagram: ApplyTransform, t: Affine = Ident\n    ) -> MList[Primitive]:\n        t_new = t * diagram.transform\n        return MList(\n            [\n                prim.apply_transform(t_new)\n                for prim in diagram.diagram.accept(self, t).data\n            ]\n        )\n\n    def visit_apply_style(\n        self, diagram: ApplyStyle, t: Affine = Ident\n    ) -> MList[Primitive]:\n        return MList(\n            [\n                prim.apply_style(diagram.style)\n                for prim in diagram.diagram.accept(self, t).data\n            ]\n        )\n\n    def visit_apply_name(\n        self, diagram: ApplyName, t: Affine = Ident\n    ) -> MList[Primitive]:\n        return MList([prim for prim in diagram.diagram.accept(self, t).data])\n\n\nclass ToCairoShape(ShapeVisitor[None]):\n\n    def render_segment(\n        self, seg: SegmentLike, ctx: PyCairoContext, p: P2\n    ) -> None:\n        q = seg.q + p\n        if isinstance(seg, Segment):\n            ctx.line_to(q.x, q.y)\n        elif isinstance(seg, ArcSegment):\n            end = seg.angle + seg.dangle\n            ctx.save()\n            matrix = tx_to_cairo(Affine.translation(p) * seg.t)\n            ctx.transform(matrix)\n            if seg.dangle < 0:\n                ctx.arc_negative(\n                    0.0, 0.0, 1.0, to_radians(seg.angle), to_radians(end)\n                )\n            else:\n                ctx.arc(0.0, 0.0, 1.0, to_radians(seg.angle), to_radians(end))\n            ctx.restore()\n\n    def visit_path(\n        self,\n        path: Path,\n        ctx: PyCairoContext = None,\n        style: Style = EMPTY_STYLE,\n    ) -> None:\n        if not path.loc_trails[0].trail.closed:\n            style.fill_opacity_ = 0\n        for loc_trail in path.loc_trails:\n            for i, (seg, p) in enumerate(loc_trail.located_segments()):\n                if i == 0:\n                    ctx.move_to(p.x, p.y)\n                self.render_segment(seg, ctx, p)\n            if loc_trail.trail.closed:\n                ctx.close_path()\n\n    def visit_latex(\n        self,\n        shape: Latex,\n        ctx: PyCairoContext = None,\n        style: Style = EMPTY_STYLE,\n    ) -> None:\n        raise NotImplementedError(\"Latex is not implemented\")\n\n    def visit_text(\n        self,\n        shape: Text,\n        ctx: PyCairoContext = None,\n        style: Style = EMPTY_STYLE,\n    ) -> None:\n        ctx.select_font_face(\"sans-serif\")\n        if shape.font_size is not None:\n            ctx.set_font_size(shape.font_size)\n        extents = ctx.text_extents(shape.text)\n\n        ctx.move_to(-(extents.width / 2), (extents.height / 2))\n        ctx.text_path(shape.text)\n\n    def visit_spacer(\n        self,\n        shape: Spacer,\n        ctx: PyCairoContext = None,\n        style: Style = EMPTY_STYLE,\n    ) -> None:\n        return\n\n    def visit_arrowhead(\n        self,\n        shape: ArrowHead,\n        ctx: PyCairoContext = None,\n        style: Style = EMPTY_STYLE,\n    ) -> None:\n        assert style.output_size\n        scale = 0.01 * (15 / 500) * style.output_size\n        render_cairo_prims(shape.arrow_shape.scale(scale), ctx, style)\n\n    def visit_image(\n        self,\n        shape: Image,\n        ctx: PyCairoContext = None,\n        style: Style = EMPTY_STYLE,\n    ) -> None:\n        surface = from_pil(shape.im)\n        ctx.set_source_surface(\n            surface, -(shape.width / 2), -(shape.height / 2)\n        )\n        ctx.paint()\n\n\ndef render_cairo_prims(\n    base: Diagram, ctx: PyCairoContext, style: Style\n) -> None:\n    base = base._style(style)\n    shape_renderer = ToCairoShape()\n    for prim in base.accept(ToList(), Ident):\n        # apply transformation\n        matrix = tx_to_cairo(prim.transform)\n        ctx.transform(matrix)\n        prim.shape.accept(shape_renderer, ctx=ctx, style=prim.style)\n\n        # undo transformation\n        matrix.invert()\n        ctx.transform(matrix)\n        prim.style.render(ctx)\n        ctx.stroke()\n\n\ndef render(\n    self: Diagram, path: str, height: int = 128, width: Optional[int] = None\n) -> None:\n    \"\"\"Render the diagram to a PNG file.\n\n    Args:\n        self (Diagram): Given ``Diagram`` instance.\n        path (str): Path of the .png file.\n        height (int, optional): Height of the rendered image.\n                                Defaults to 128.\n        width (Optional[int], optional): Width of the rendered image.\n                                         Defaults to None.\n    \"\"\"\n    import cairo\n\n    envelope = self.get_envelope()\n    assert envelope is not None\n\n    pad = 0.05\n\n    # infer width to preserve aspect ratio\n    width = width or int(height * envelope.width / envelope.height)\n\n    # determine scale to fit the largest axis in the target frame size\n    if envelope.width - width <= envelope.height - height:\n        α = height / ((1 + pad) * envelope.height)\n    else:\n        α = width / ((1 + pad) * envelope.width)\n\n    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)\n    ctx = cairo.Context(surface)\n\n    s = self.scale(α).center_xy().pad(1 + pad)\n    e = s.get_envelope()\n    assert e is not None\n    s = s.translate(e(-unit_x), e(-unit_y))\n    render_cairo_prims(s, ctx, Style.root(max(width, height)))\n    surface.write_to_png(path)\n"
  },
  {
    "path": "chalk/backend/svg.py",
    "content": "from __future__ import annotations\n\nimport xml.etree.ElementTree as ET\nfrom typing import TYPE_CHECKING, Optional\n\nimport svgwrite\nfrom svgwrite import Drawing\nfrom svgwrite.base import BaseElement\nfrom svgwrite.shapes import Rect\n\nfrom chalk import transform as tx\nfrom chalk.shapes import (\n    ArcSegment,\n    ArrowHead,\n    Image,\n    Latex,\n    Path,\n    Segment,\n    SegmentLike,\n    Spacer,\n    Text,\n)\nfrom chalk.style import Style\nfrom chalk.transform import P2, unit_x, unit_y\nfrom chalk.types import Diagram\nfrom chalk.visitor import DiagramVisitor, ShapeVisitor\n\nif TYPE_CHECKING:\n    from chalk.core import (\n        ApplyName,\n        ApplyStyle,\n        ApplyTransform,\n        Compose,\n        Empty,\n        Primitive,\n    )\n\n\nEMPTY_STYLE = Style.empty()\n\n\ndef tx_to_svg(affine: tx.Affine) -> str:\n    def convert(\n        a: float, b: float, c: float, d: float, e: float, f: float\n    ) -> str:\n        return f\"matrix({a}, {d}, {b}, {e}, {c}, {f})\"\n\n    return convert(*affine[:6])\n\n\nclass Raw(Rect):  # type: ignore\n    \"\"\"Shape class.\n\n    A fake SVG node for importing latex.\n    \"\"\"\n\n    def __init__(self, st: str):\n        ET.register_namespace(\"\", \"http://www.w3.org/2000/svg\")\n        self.xml = ET.fromstring(st)\n\n    def get_xml(self) -> ET.Element:\n        return self.xml\n\n\nclass ToSVG(DiagramVisitor[BaseElement, Style]):\n    A_type = BaseElement\n\n    def __init__(self, dwg: Drawing):\n        self.dwg = dwg\n        self.shape_renderer = ToSVGShape(dwg)\n\n    def visit_primitive(\n        self, diagram: Primitive, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        style_new = diagram.style.merge(style)\n        style_svg = style_new.to_svg()\n        transform = tx_to_svg(diagram.transform)\n        inner = diagram.shape.accept(self.shape_renderer, style=style_new)\n        if not style_svg and not transform:\n            return inner\n        else:\n            if not style_svg:\n                style_svg = \";\"\n            g = self.dwg.g(transform=transform, style=style_svg)\n            g.add(inner)\n            return g\n\n    def visit_empty(\n        self, diagram: Empty, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        return self.dwg.g()\n\n    def visit_compose(\n        self, diagram: Compose, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        g = self.dwg.g()\n\n        for d in diagram.diagrams:\n            g.add(d.accept(self, style))\n        return g\n\n    def visit_apply_transform(\n        self, diagram: ApplyTransform, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        g = self.dwg.g(transform=tx_to_svg(diagram.transform))\n        g.add(diagram.diagram.accept(self, style))\n        return g\n\n    def visit_apply_style(\n        self, diagram: ApplyStyle, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        return diagram.diagram.accept(self, diagram.style.merge(style))\n\n    def visit_apply_name(\n        self, diagram: ApplyName, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        g = self.dwg.g()\n        g.add(diagram.diagram.accept(self, style))\n        return g\n\n\nclass ToSVGShape(ShapeVisitor[BaseElement]):\n    def __init__(self, dwg: Drawing):\n        self.dwg = dwg\n\n    def render_segment(self, seg: SegmentLike, p: P2) -> str:\n        q = seg.q + p\n        if isinstance(seg, Segment):\n            return f\"L {q.x} {q.y}\"\n        elif isinstance(seg, ArcSegment):\n            \"https://www.w3.org/TR/SVG/implnote.html#ArcConversionCenterToEndpoint\"\n            f_A = 1 if abs(seg.dangle) > 180 else 0\n            det: float = seg.t.determinant  # type: ignore\n            f_S = 1 if det * seg.dangle > 0 else 0\n            return f\"A {seg.r_x} {seg.r_y} {seg.rot} {f_A} {f_S} {q.x} {q.y}\"\n\n    def visit_path(\n        self, path: Path, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        extra_style = \"\"\n        if not path.loc_trails[0].trail.closed:\n            extra_style = \"fill:none;\"\n        line = self.dwg.path(\n            style=\"vector-effect: non-scaling-stroke;\" + extra_style,\n        )\n        for loc_trail in path.loc_trails:\n            p = loc_trail.location\n            line.push(f\"M {p.x} {p.y}\")\n            for i, (seg, p) in enumerate(loc_trail.located_segments()):\n                line.push(self.render_segment(seg, p))\n            if loc_trail.trail.closed:\n                line.push(\"Z\")\n        return line\n\n    def visit_latex(\n        self, shape: Latex, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        dx, dy = -shape.width / 2, -shape.height / 2\n        g = self.dwg.g(transform=f\"scale(0.05) translate({dx} {dy})\")\n        g.add(Raw(shape.content))\n        return g\n\n    def visit_text(\n        self, shape: Text, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        dx = -(shape.get_bounding_box().width / 2)\n        return self.dwg.text(\n            shape.text,\n            transform=f\"translate({dx}, 0)\",\n            style=f\"\"\"text-align:center; text-anchor:middle; dominant-baseline:middle;\n                      font-family:sans-serif; font-weight: bold;\n                      font-size:{shape.font_size}px;\n                      vector-effect: non-scaling-stroke;\"\"\",\n        )\n\n        raise NotImplementedError(\"Text is not implemented\")\n\n    def visit_spacer(\n        self, shape: Spacer, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        return self.dwg.g()\n\n    def visit_arrowhead(\n        self, shape: ArrowHead, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        assert style.output_size\n        scale = 0.01 * (15 / 500) * style.output_size\n        return to_svg(shape.arrow_shape.scale(scale), self.dwg, style)\n\n    def visit_image(\n        self, shape: Image, style: Style = EMPTY_STYLE\n    ) -> BaseElement:\n        dx = -shape.width / 2\n        dy = -shape.height / 2\n        return self.dwg.image(\n            href=shape.url_path, transform=f\"translate({dx}, {dy})\"\n        )\n\n\ndef to_svg(self: Diagram, dwg: Drawing, style: Style) -> BaseElement:\n    return self.accept(ToSVG(dwg), style)\n\n\ndef render(\n    self: Diagram,\n    path: str,\n    height: int = 128,\n    width: Optional[int] = None,\n    draw_height: Optional[int] = None,\n) -> None:\n    \"\"\"Render the diagram to an SVG file.\n\n    Args:\n        self (Diagram): Given ``Diagram`` instance.\n        path (str): Path of the .svg file.\n        height (int, optional): Height of the rendered image.\n                                Defaults to 128.\n        width (Optional[int], optional): Width of the rendered image.\n                                         Defaults to None.\n        draw_height (Optional[int], optional): Override the height for\n                                               line width.\n\n    \"\"\"\n    pad = 0.05\n    envelope = self.get_envelope()\n\n    # infer width to preserve aspect ratio\n    assert envelope is not None\n    width = width or int(height * envelope.width / envelope.height)\n\n    # determine scale to fit the largest axis in the target frame size\n    if envelope.width - width <= envelope.height - height:\n        α = height / ((1 + pad) * envelope.height)\n    else:\n        α = width / ((1 + pad) * envelope.width)\n\n    dwg = svgwrite.Drawing(path, size=(width, height))\n\n    outer = dwg.g(style=\"fill:white;\")\n    # Arrow marker\n    marker = dwg.marker(\n        id=\"arrow\", refX=5.0, refY=1.7, size=(5, 3.5), orient=\"auto\"\n    )\n    marker.add(dwg.polygon([(0, 0), (5, 1.75), (0, 3.5)]))\n    dwg.defs.add(marker)\n\n    dwg.add(outer)\n    s = self.center_xy().pad(1 + pad).scale(α)\n    e = s.get_envelope()\n    assert e is not None\n    s = s.translate(e(-unit_x), e(-unit_y))\n    if draw_height is None:\n        draw_height = max(height, width)\n    style = Style.root(output_size=draw_height)\n    outer.add(to_svg(s, dwg, style))\n    dwg.save()\n"
  },
  {
    "path": "chalk/backend/tikz.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any, List\n\nfrom chalk import transform as tx\nfrom chalk.monoid import MList\nfrom chalk.shapes import (\n    ArcSegment,\n    ArrowHead,\n    Image,\n    Latex,\n    Path,\n    Segment,\n    SegmentLike,\n    Spacer,\n    Text,\n)\nfrom chalk.style import Style\nfrom chalk.transform import P2, origin\nfrom chalk.types import Diagram\nfrom chalk.visitor import DiagramVisitor, ShapeVisitor\n\nif TYPE_CHECKING:\n    from chalk.core import ApplyStyle, ApplyTransform, Primitive\n\n\nPyLatex = Any\nPyLatexElement = Any\n\nEMPTY_STYLE = Style.empty()\n\n\ndef tx_to_tikz(affine: tx.Affine) -> str:\n    def convert(\n        a: float, b: float, c: float, d: float, e: float, f: float\n    ) -> str:\n        return f\"{{{a}, {d}, {b}, {e}, ({c}, {f})}}\"\n\n    return convert(*affine[:6])\n\n\nclass ToTikZ(DiagramVisitor[MList[PyLatexElement], Style]):\n    A_type = MList[PyLatexElement]\n\n    def __init__(self, pylatex: PyLatex):\n        self.pylatex = pylatex\n        self.shape_renderer = ToTikZShape(pylatex)\n\n    def visit_primitive(\n        self, diagram: Primitive, style: Style = EMPTY_STYLE\n    ) -> MList[PyLatexElement]:\n        transform = tx_to_tikz(diagram.transform)\n        style_new = diagram.style.merge(style)\n        inner = diagram.shape.accept(self.shape_renderer, style=style_new)\n        if not style_new and not transform:\n            return MList([inner])\n        else:\n            options = {\"cm\": tx_to_tikz(diagram.transform)}\n            s = self.pylatex.TikZScope(\n                options=self.pylatex.TikZOptions(**options)\n            )\n            s.append(inner)\n            return MList([s])\n\n    def visit_apply_transform(\n        self, diagram: ApplyTransform, style: Style = EMPTY_STYLE\n    ) -> MList[PyLatexElement]:\n        options = {\"cm\": tx_to_tikz(diagram.transform)}\n        s = self.pylatex.TikZScope(options=self.pylatex.TikZOptions(**options))\n        for x in diagram.diagram.accept(self, style).data:\n            s.append(x)\n        return MList([s])\n\n    def visit_apply_style(\n        self, diagram: ApplyStyle, style: Style = EMPTY_STYLE\n    ) -> MList[PyLatexElement]:\n        style_new = diagram.style.merge(style)\n        return diagram.diagram.accept(self, style_new)\n\n\nclass ToTikZShape(ShapeVisitor[PyLatexElement]):\n    def __init__(self, pylatex: PyLatex):\n        self.pylatex = pylatex\n\n    def render_segment(\n        self, pts: PyLatexElement, seg: SegmentLike, p: P2\n    ) -> None:\n        q = seg.q + p\n        if isinstance(seg, Segment):\n            pts.append(\"--\")\n            pts.append(self.pylatex.TikZCoordinate(q.x, q.y))\n        elif isinstance(seg, ArcSegment):\n            start = (-seg.center).angle\n            end = (seg.q - seg.center).angle\n            det: float = seg.t.determinant  # type: ignore\n            if det * seg.dangle < 0 and end > start:\n                end = end - 360\n            if det * seg.dangle > 0 and end < start:\n                end = end + 360\n            end_ang = end - seg.rot\n            pts._arg_list.append(\n                self.pylatex.TikZUserPath(\n                    f\"\"\"{{[rotate={seg.rot}] arc [\n                           start angle={start-seg.rot}, end angle={end_ang},\n                           x radius={seg.r_x}, y radius={seg.r_y}]}}\"\"\"\n                )\n            )\n\n    def visit_path(\n        self, path: Path, style: Style = EMPTY_STYLE\n    ) -> PyLatexElement:\n        pts = self.pylatex.TikZPathList()\n        if not path.loc_trails[0].trail.closed:\n            style = style.fill_opacity(0)\n\n        for loc_trail in path.loc_trails:\n            for i, (seg, p) in enumerate(loc_trail.located_segments()):\n                if i == 0:\n                    pts.append(self.pylatex.TikZCoordinate(p.x, p.y))\n                self.render_segment(pts, seg, p)\n            if loc_trail.trail.closed:\n                pts.append(\"--\")\n                pts._arg_list.append(self.pylatex.TikZUserPath(\"cycle\"))\n        return self.pylatex.TikZDraw(\n            pts,\n            options=self.pylatex.TikZOptions(**style.to_tikz(self.pylatex)),\n        )\n\n    def visit_latex(\n        self, shape: Latex, style: Style = EMPTY_STYLE\n    ) -> PyLatexElement:\n        raise NotImplementedError(\"Latex is not implemented\")\n\n    def visit_text(\n        self, shape: Text, style: Style = EMPTY_STYLE\n    ) -> PyLatexElement:\n        opts = {}\n        opts[\"font\"] = \"\\\\small\\\\sffamily\"\n        opts[\"scale\"] = str(\n            3.5 * (1 if shape.font_size is None else shape.font_size)\n        )\n\n        styles = style.to_tikz(self.pylatex)\n        if styles.get(\"fill\", None) is not None:\n            opts[\"text\"] = styles[\"fill\"]\n        return self.pylatex.TikZNode(\n            text=shape.text,\n            # Scale parameters based on observations\n            options=self.pylatex.TikZOptions(**opts),\n        )\n\n    def visit_spacer(\n        self, shape: Spacer, style: Style = EMPTY_STYLE\n    ) -> PyLatexElement:\n        left = origin.x - shape.width / 2\n        top = origin.y - shape.height / 2\n        return self.pylatex.TikZPath(\n            [\n                self.pylatex.TikZCoordinate(left, top),\n                \"rectangle\",\n                self.pylatex.TikZCoordinate(\n                    left + shape.width, top + shape.height\n                ),\n            ]\n        )\n\n    def visit_arrowhead(\n        self, shape: ArrowHead, style: Style = EMPTY_STYLE\n    ) -> PyLatexElement:\n        assert style.output_size\n        scale = 0.01 * 3 * (15 / 500) * style.output_size\n        s = self.pylatex.TikZScope()\n        for inner in to_tikz(\n            shape.arrow_shape.scale(scale), self.pylatex, style\n        ):\n            s.append(inner)\n        return s\n\n    def visit_image(\n        self, shape: Image, style: Style = EMPTY_STYLE\n    ) -> PyLatexElement:\n        assert False, \"No tikz image renderer\"\n\n\ndef to_tikz(\n    self: Diagram, pylatex: PyLatex, style: Style\n) -> List[PyLatexElement]:\n    return self.accept(ToTikZ(pylatex), style).data\n\n\ndef render(self: Diagram, path: str, height: int = 128) -> None:\n    # Hack: Convert roughly from px to pt. Assume 300 dpi.\n    heightpt = height / 4.3\n    try:\n        import pylatex\n    except ImportError:\n        print(\"Render PDF requires pylatex installation.\")\n        return\n\n    pad = 0.05\n    envelope = self.get_envelope()\n    assert envelope is not None\n\n    # infer width to preserve aspect ratio\n    width = heightpt * (envelope.width / envelope.height)\n    # determine scale to fit the largest axis in the target frame size\n    if envelope.width - width <= envelope.height - heightpt:\n        α = heightpt / ((1 + pad) * envelope.height)\n    else:\n        α = width / ((1 + pad) * envelope.width)\n    x, _ = pad * heightpt, pad * width\n\n    # create document\n    doc = pylatex.Document(documentclass=\"standalone\")\n    # document_options= pylatex.TikZOptions(margin=f\"{{{x}pt {x}pt {y}pt {y}pt}}\"))\n    # add our sample drawings\n    diagram = self.scale(α).reflect_y().pad(1 + pad)\n    envelope = diagram.get_envelope()\n    assert envelope is not None\n    from chalk.core import Primitive\n\n    padding = Primitive.from_shape(\n        Spacer(envelope.width, envelope.height)\n    ).translate(envelope.center.x, envelope.center.y)\n    diagram = diagram + padding\n    with doc.create(pylatex.TikZ()) as pic:\n        for x in to_tikz(diagram, pylatex, Style.root(max(height, width))):\n            pic.append(x)\n    doc.generate_tex(path.replace(\".pdf\", \"\") + \".tex\")\n    doc.generate_pdf(path.replace(\".pdf\", \"\"), clean_tex=False)\n"
  },
  {
    "path": "chalk/combinators.py",
    "content": "from typing import Iterable, List, Optional, Tuple\n\nfrom chalk.envelope import Envelope\nfrom chalk.monoid import associative_reduce\nfrom chalk.shapes import Path, Spacer\nfrom chalk.transform import V2, Affine, origin, unit_x, unit_y\nfrom chalk.types import Diagram\n\n# Functions mirroring Diagrams.Combinators and Diagrams.2d.Combinators\n\n\ndef with_envelope(self: Diagram, other: Diagram) -> Diagram:\n    return self.compose(other.get_envelope())\n\n\n# with_trace, phantom,\n\n\ndef strut(width: float, height: float) -> Diagram:\n    from chalk.core import Primitive\n\n    return Primitive.from_shape(Spacer(width, height))\n\n\ndef pad(self: Diagram, extra: float) -> Diagram:\n    \"\"\"Scale outward directed padding for a diagram.\n\n    Be careful using this if your diagram is not centered.\n\n    Args:\n        self (Diagram): Diagram object.\n        extra (float): Amount of padding to add.\n\n    Returns:\n        Diagram: A diagram object.\n    \"\"\"\n    envelope = self.get_envelope()\n\n    def f(d: V2) -> float:\n        assert envelope is not None\n        return envelope(d) * extra\n\n    new_envelope = Envelope(f, envelope.is_empty)\n    return self.compose(new_envelope)\n\n\ndef frame(self: Diagram, extra: float) -> Diagram:\n    \"\"\"Add outward directed padding for a diagram.\n    This padding is applied uniformly on all sides.\n\n    Args:\n        self (Diagram): Diagram object.\n        extra (float): Amount of padding to add.\n\n    Returns:\n        Diagram: A diagram object.\n    \"\"\"\n    envelope = self.get_envelope()\n\n    def f(d: V2) -> float:\n        assert envelope is not None\n        return envelope(d) + extra\n\n    new_envelope = Envelope(f, envelope.is_empty)\n    return self.compose(new_envelope)\n\n\n# extrudeEnvelope, intrudeEnvelope\n\n\ndef atop(self: Diagram, other: Diagram) -> Diagram:\n    envelope1 = self.get_envelope()\n    envelope2 = other.get_envelope()\n    new_envelope = envelope1 + envelope2\n    return self.compose(new_envelope, other)\n\n\n# beneath\n\n\ndef above(self: Diagram, other: Diagram) -> Diagram:\n    return beside(self, other, unit_y)\n\n\n# appends\n\n\ndef beside(self: Diagram, other: Diagram, direction: V2) -> Diagram:\n    return atop(self, juxtapose(self, other, direction))\n\n\ndef place_at(\n    diagrams: Iterable[Diagram], points: List[Tuple[float, float]]\n) -> Diagram:\n    return concat(d.translate(x, y) for d, (x, y) in zip(diagrams, points))\n\n\ndef place_on_path(diagrams: Iterable[Diagram], path: Path) -> Diagram:\n    return concat(\n        d.translate(p.x, p.y) for d, p in zip(diagrams, path.points())\n    )\n\n\n# position, atPoints\ndef cat(\n    diagrams: Iterable[Diagram], v: V2, sep: Optional[float] = None\n) -> Diagram:\n\n    diagrams = iter(diagrams)\n    start = next(diagrams, None)\n    sep_dia = hstrut(sep).rotate(v.angle)\n    if start is None:\n        return empty()\n\n    def fn(a: Diagram, b: Diagram) -> Diagram:\n        return a.beside(sep_dia, v).beside(b, v)\n\n    return fn(start, associative_reduce(fn, diagrams, empty()))\n\n\ndef concat(diagrams: Iterable[Diagram]) -> Diagram:\n    \"\"\"\n    Concat diagrams atop of each other with atop.\n\n    Args:\n        diagrams (Iterable[Diagram]): Diagrams to concat.\n\n    Returns:\n        Diagram: New diagram\n\n    \"\"\"\n    from chalk.core import BaseDiagram\n\n    return BaseDiagram.concat(diagrams)  # type: ignore\n\n\ndef empty() -> Diagram:\n    \"Create an empty diagram\"\n    from chalk.core import BaseDiagram\n\n    return BaseDiagram.empty()\n\n\n# CompaseAligned.\n\n# 2D\n\n\ndef hstrut(width: Optional[float]) -> Diagram:\n    from chalk.core import Primitive\n\n    if width is None:\n        return empty()\n    return Primitive.from_shape(Spacer(width, 0))\n\n\ndef vstrut(height: Optional[float]) -> Diagram:\n    from chalk.core import Primitive\n\n    if height is None:\n        return empty()\n    return Primitive.from_shape(Spacer(0, height))\n\n\ndef hcat(diagrams: Iterable[Diagram], sep: Optional[float] = None) -> Diagram:\n    \"\"\"\n    Stack diagrams next to each other with `besides`.\n\n    Args:\n        diagrams (Iterable[Diagram]): Diagrams to stack.\n        sep (Optional[float]): Padding between diagrams.\n\n    Returns:\n        Diagram: New diagram\n\n    \"\"\"\n    return cat(diagrams, unit_x, sep)\n\n\ndef vcat(diagrams: Iterable[Diagram], sep: Optional[float] = None) -> Diagram:\n    \"\"\"\n    Stack diagrams above each other with `above`.\n\n    Args:\n        diagrams (Iterable[Diagram]): Diagrams to stack.\n        sep (Optional[float]): Padding between diagrams.\n\n    Returns:\n        Diagrams\n\n    \"\"\"\n    return cat(diagrams, unit_y, sep)\n\n\n# Extra\n\n\ndef above2(self: Diagram, other: Diagram) -> Diagram:\n    \"\"\"Given two diagrams ``a`` and ``b``, ``a.above2(b)``\n    places ``a`` on top of ``b``. This moves ``a`` down to\n    touch ``b``.\n\n    💡 ``a.above2(b)`` is equivalent to ``a // b``.\n\n    Args:\n        self (Diagram): Diagram object.\n        other (Diagram): Another diagram object.\n\n    Returns:\n        Diagram: A diagram object.\n    \"\"\"\n    return beside(other, self, -unit_y)\n\n\ndef juxtapose_snug(self: Diagram, other: Diagram, direction: V2) -> Diagram:\n    trace1 = self.get_trace()\n    trace2 = other.get_trace()\n    d1 = trace1.trace_v(origin, direction)\n    d2 = trace2.trace_v(origin, -direction)\n    assert d1 is not None and d2 is not None\n    d = d1 - d2\n    t = Affine.translation(d)\n    return other.apply_transform(t)\n\n\ndef beside_snug(self: Diagram, other: Diagram, direction: V2) -> Diagram:\n    return atop(self, juxtapose_snug(self, other, direction))\n\n\ndef juxtapose(self: Diagram, other: Diagram, direction: V2) -> Diagram:\n    \"\"\"Given two diagrams ``a`` and ``b``, ``a.juxtapose(b, v)``\n    places ``b`` to touch ``a`` along angle ve .\n\n    Args:\n        self (Diagram): Diagram object.\n        other (Diagram): Another diagram object.\n        direction (V2): (Normalized) vector angle to juxtapose\n\n    Returns:\n        Diagram: Repositioned ``b`` diagram\n    \"\"\"\n    envelope1 = self.get_envelope()\n    envelope2 = other.get_envelope()\n    d = envelope1.envelope_v(direction) - envelope2.envelope_v(-direction)\n    t = Affine.translation(d)\n    return other.apply_transform(t)\n\n\ndef at_center(self: Diagram, other: Diagram) -> Diagram:\n    \"\"\"Center two given diagrams.\n\n    💡 `a.at_center(b)` means center of ``a`` is translated\n    to the center of ``b``, and ``b`` sits on top of\n    ``a`` along the axis out of the plane of the image.\n\n    💡 In other words, ``b`` occludes ``a``.\n\n    Args:\n        self (Diagram): Diagram object.\n        other (Diagram): Another diagram object.\n\n    Returns:\n        Diagram: A diagram object.\n    \"\"\"\n    envelope1 = self.get_envelope()\n    envelope2 = other.get_envelope()\n    t = Affine.translation(envelope1.center)\n    new_envelope = envelope1 + (t * envelope2)\n    return self.compose(new_envelope, other.apply_transform(t))\n"
  },
  {
    "path": "chalk/core.py",
    "content": "from __future__ import annotations\n\nimport os\nimport tempfile\nfrom dataclasses import dataclass\nfrom typing import Any, List, Optional, TypeVar\n\nimport chalk.align\nimport chalk.arrow\nimport chalk.backend.cairo\nimport chalk.backend.svg\nimport chalk.backend.tikz\nimport chalk.combinators\nimport chalk.model\nimport chalk.subdiagram\nimport chalk.trace\nimport chalk.types\nfrom chalk import backend\nfrom chalk.envelope import Envelope\nfrom chalk.style import Style\nfrom chalk.subdiagram import Name\nfrom chalk.transform import Affine, unit_x\nfrom chalk.types import Diagram, Shape\nfrom chalk.utils import imgen\nfrom chalk.visitor import DiagramVisitor\n\nTrail = Any\nIdent = Affine.identity()\nA = TypeVar(\"A\", bound=chalk.monoid.Monoid)\n\nSVG_HEIGHT = 200\nSVG_DRAW_HEIGHT = None\n\n\ndef set_svg_height(height: int) -> None:\n    \"Globally set the svg preview height for notebooks.\"\n    global SVG_HEIGHT\n    SVG_HEIGHT = height\n\n\ndef set_svg_draw_height(height: int) -> None:\n    \"Globally set the svg preview height for notebooks.\"\n    global SVG_DRAW_HEIGHT\n    SVG_DRAW_HEIGHT = height\n\n\n@dataclass\nclass BaseDiagram(chalk.types.Diagram):\n    \"\"\"Diagram class.\"\"\"\n\n    # Monoid\n    __add__ = chalk.combinators.atop\n\n    @classmethod\n    def empty(cls) -> Diagram:  # type: ignore\n        return Empty()\n\n    # Tranformable\n    def apply_transform(self, t: Affine) -> Diagram:  # type: ignore\n        return ApplyTransform(t, self)\n\n    # Stylable\n    def apply_style(self, style: Style) -> Diagram:  # type: ignore\n        return ApplyStyle(style, self)\n\n    def _style(self, style: Style) -> Diagram:\n        return self.apply_style(style)\n\n    def compose(\n        self, envelope: Envelope, other: Optional[Diagram] = None\n    ) -> Diagram:\n        other = other if other is not None else Empty()\n        if isinstance(self, Compose) and isinstance(other, Compose):\n            return Compose(envelope, self.diagrams + other.diagrams)\n        elif isinstance(self, Compose):\n            return Compose(envelope, self.diagrams + [other])\n        elif isinstance(other, Compose):\n            return Compose(envelope, [self] + other.diagrams)\n        else:\n            return Compose(envelope, [self, other])\n\n    def named(self, name: Name) -> Diagram:\n        \"\"\"Add a name (or a sequence of names) to a diagram.\"\"\"\n        return ApplyName(name, self)\n\n    # Combinators\n    with_envelope = chalk.combinators.with_envelope\n    juxtapose = chalk.combinators.juxtapose\n    juxtapose_snug = chalk.combinators.juxtapose_snug\n    beside_snug = chalk.combinators.beside_snug\n    above = chalk.combinators.above\n    atop = chalk.combinators.atop\n    beside = chalk.combinators.beside\n    above = chalk.combinators.above\n\n    # Align\n    align = chalk.align.align_to\n    align_t = chalk.align.align_t\n    align_b = chalk.align.align_b\n    align_l = chalk.align.align_l\n    align_r = chalk.align.align_r\n    align_tr = chalk.align.align_tr\n    align_tl = chalk.align.align_tl\n    align_bl = chalk.align.align_bl\n    align_br = chalk.align.align_br\n    center_xy = chalk.align.center_xy\n    center = chalk.align.center\n    scale_uniform_to_y = chalk.align.scale_uniform_to_y\n    scale_uniform_to_x = chalk.align.scale_uniform_to_x\n\n    # Arrows\n    connect = chalk.arrow.connect\n    connect_outside = chalk.arrow.connect_outside\n    connect_perim = chalk.arrow.connect_perim\n\n    # Model\n    show_origin = chalk.model.show_origin\n    show_envelope = chalk.model.show_envelope\n    show_beside = chalk.model.show_beside\n    show_labels = chalk.model.show_labels\n\n    # Combinators\n    frame = chalk.combinators.frame\n    pad = chalk.combinators.pad\n\n    # Infix\n    def __or__(self, d: Diagram) -> Diagram:\n        return chalk.combinators.beside(self, d, unit_x)\n\n    __truediv__ = chalk.combinators.above\n    __floordiv__ = chalk.combinators.above2\n\n    def display(\n        self, height: int = 256, verbose: bool = True, **kwargs: Any\n    ) -> None:\n        \"\"\"Display the diagram using the default renderer.\n\n        Note: see ``chalk.utils.imgen`` for details on the keyword arguments.\n        \"\"\"\n        # update kwargs with defaults and user-specified values\n        kwargs.update({\"height\": height})\n        kwargs.update({\"verbose\": verbose})\n        kwargs.update({\"dirpath\": None})\n        kwargs.update({\"wait\": kwargs.get(\"wait\", 1)})\n        # render and display the diagram\n        imgen(self, **kwargs)\n\n    # Rendering\n    render = chalk.backend.cairo.render\n    render_png = chalk.backend.cairo.render\n    render_svg = chalk.backend.svg.render\n    render_pdf = chalk.backend.tikz.render\n\n    to_svg = backend.svg.to_svg\n    to_tikz = backend.tikz.to_tikz\n\n    def _repr_svg_(self) -> str:\n        global SVG_HEIGHT\n        f = tempfile.NamedTemporaryFile(delete=False)\n        self.render_svg(f.name, height=SVG_HEIGHT, draw_height=SVG_DRAW_HEIGHT)\n        f.close()\n        svg = open(f.name).read()\n        os.unlink(f.name)\n        return svg\n\n    def _repr_html_(self) -> str | tuple[str, Any]:\n        \"\"\"Returns a rich HTML representation of an object.\"\"\"\n        return self._repr_svg_()\n\n    # Getters\n    get_envelope = chalk.envelope.get_envelope\n    get_trace = chalk.trace.get_trace\n    get_subdiagram = chalk.subdiagram.get_subdiagram\n    get_sub_map = chalk.subdiagram.get_sub_map\n\n    with_names = chalk.subdiagram.with_names\n\n    def qualify(self, name: Name) -> Diagram:\n        \"\"\"Prefix names in the diagram by a given name or sequence of names.\"\"\"\n        return self.accept(Qualify(name), None)\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        raise NotImplementedError\n\n\n@dataclass\nclass Primitive(BaseDiagram):\n    \"\"\"Primitive class.\n\n    This is derived from a ``chalk.core.Diagram`` class.\n\n    [TODO]: explain what Primitive class is for.\n    \"\"\"\n\n    shape: Shape\n    style: Style\n    transform: Affine\n\n    @classmethod\n    def from_shape(cls, shape: Shape) -> Primitive:\n        \"\"\"Creates a primitive from a shape using the default style (only line\n        stroke, no fill) and the identity transformation.\n\n        Args:\n            shape (Shape): A shape object.\n\n        Returns:\n            Primitive: A diagram object.\n        \"\"\"\n        return cls(shape, Style.empty(), Ident)\n\n    def apply_transform(self, t: Affine) -> Primitive:\n        \"\"\"Applies a transform and returns a primitive.\n\n        Args:\n            t (Transform): A transform object.\n\n        Returns:\n            Primitive\n        \"\"\"\n        new_transform = t * self.transform\n        return Primitive(self.shape, self.style, new_transform)\n\n    def apply_style(self, other_style: Style) -> Primitive:\n        \"\"\"Applies a style and returns a primitive.\n\n        Args:\n            other_style (Style): A style object.\n\n        Returns:\n            Primitive\n        \"\"\"\n        return Primitive(\n            self.shape, self.style.merge(other_style), self.transform\n        )\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        return visitor.visit_primitive(self, args)\n\n\n@dataclass\nclass Empty(BaseDiagram):\n    \"\"\"An Empty diagram class.\"\"\"\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        return visitor.visit_empty(self, args)\n\n\n@dataclass\nclass Compose(BaseDiagram):\n    \"\"\"Compose class.\"\"\"\n\n    envelope: Envelope\n    diagrams: List[Diagram]\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        return visitor.visit_compose(self, args)\n\n\n@dataclass\nclass ApplyTransform(BaseDiagram):\n    \"\"\"ApplyTransform class.\"\"\"\n\n    transform: Affine\n    diagram: Diagram\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        return visitor.visit_apply_transform(self, args)\n\n\n@dataclass\nclass ApplyStyle(BaseDiagram):\n    \"\"\"ApplyStyle class.\"\"\"\n\n    style: Style\n    diagram: Diagram\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        return visitor.visit_apply_style(self, args)\n\n\n@dataclass\nclass ApplyName(BaseDiagram):\n    \"\"\"ApplyName class.\"\"\"\n\n    dname: Name\n    diagram: Diagram\n\n    def accept(self, visitor: DiagramVisitor[A, Any], args: Any) -> A:\n        return visitor.visit_apply_name(self, args)\n\n\nclass Qualify(DiagramVisitor[Diagram, None]):\n    A_type = Diagram\n\n    def __init__(self, name: Name):\n        self.name = name\n\n    def visit_primitive(self, diagram: Primitive, args: None) -> Diagram:\n        return diagram\n\n    def visit_compose(self, diagram: Compose, args: None) -> Diagram:\n        return Compose(\n            diagram.envelope, [d.accept(self, None) for d in diagram.diagrams]\n        )\n\n    def visit_apply_transform(\n        self, diagram: ApplyTransform, args: None\n    ) -> Diagram:\n        return ApplyTransform(\n            diagram.transform,\n            diagram.diagram.accept(self, None),\n        )\n\n    def visit_apply_style(self, diagram: ApplyStyle, args: None) -> Diagram:\n        return ApplyStyle(\n            diagram.style,\n            diagram.diagram.accept(self, None),\n        )\n\n    def visit_apply_name(self, diagram: ApplyName, args: None) -> Diagram:\n        return ApplyName(\n            self.name + diagram.dname, diagram.diagram.accept(self, None)\n        )\n"
  },
  {
    "path": "chalk/envelope.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Callable, Iterable, Tuple\n\nfrom chalk.monoid import Monoid\nfrom chalk.transform import (\n    P2,\n    V2,\n    Affine,\n    BoundingBox,\n    Transformable,\n    apply_affine,\n    origin,\n    remove_translation,\n    transpose_translation,\n    unit_x,\n    unit_y,\n)\nfrom chalk.visitor import DiagramVisitor\n\nif TYPE_CHECKING:\n    from chalk.core import ApplyTransform, Compose, Primitive\n    from chalk.types import Diagram\n\n\nSignedDistance = float\nIdent = Affine.identity()\n\n\nclass Envelope(Transformable, Monoid):\n    def __init__(\n        self, f: Callable[[V2], SignedDistance], is_empty: bool = False\n    ):\n        self.f = f\n        self.is_empty = is_empty\n\n    def __call__(self, direction: V2) -> SignedDistance:\n        assert not self.is_empty\n        return self.f(direction)\n\n    # Monoid\n    @staticmethod\n    def empty() -> Envelope:\n        return Envelope(lambda v: 0, is_empty=True)\n\n    def __add__(self, other: Envelope) -> Envelope:\n        if self.is_empty:\n            return other\n        if other.is_empty:\n            return self\n        return Envelope(\n            lambda direction: max(self(direction), other(direction))\n        )\n\n    @property\n    def center(self) -> P2:\n        if self.is_empty:\n            return origin\n        return P2(\n            (-self(-unit_x) + self(unit_x)) / 2,\n            (-self(-unit_y) + self(unit_y)) / 2,\n        )\n\n    @property\n    def width(self) -> float:\n        assert not self.is_empty\n        return self(unit_x) + self(-unit_x)\n\n    @property\n    def height(self) -> float:\n        assert not self.is_empty\n        return self(unit_y) + self(-unit_y)\n\n    def apply_transform(self, t: Affine) -> Envelope:\n        if self.is_empty:\n            return self\n        rt = remove_translation(t)\n        inv_t = ~rt\n        trans_t = transpose_translation(rt)\n        _, _, c, _, _, f = t[:6]\n        u: V2 = -V2(c, f)\n\n        def wrapped(v: V2) -> SignedDistance:\n            # Linear\n            vi = apply_affine(inv_t, v)\n            v_prim = apply_affine(trans_t, v).normalized()\n            inner: float = self(v_prim)\n            d: float = v_prim.dot(vi)\n            after_linear = inner / d\n\n            # Translation\n            diff: float = (u / (v.dot(v))).dot(v)\n            return after_linear - diff\n\n        return Envelope(wrapped)\n\n    def envelope_v(self, v: V2) -> V2:\n        if self.is_empty:\n            return V2(0, 0)\n        v = v.scaled_to(1)\n        d: float = self(v)\n        return v * d\n\n    @staticmethod\n    def from_bounding_box(box: BoundingBox) -> Envelope:\n        def wrapped(d: V2) -> SignedDistance:\n            v: float = apply_affine(\n                Affine.rotation(d.angle), box\n            ).bounding_box.max_point.x\n            return v / d.length\n\n        return Envelope(wrapped)\n\n    @staticmethod\n    def from_circle(radius: float) -> Envelope:\n        def wrapped(d: V2) -> SignedDistance:\n            return radius / d.length\n\n        return Envelope(wrapped)\n\n    def to_path(self, angle: int = 45) -> Iterable[P2]:\n        \"Draws an envelope by sampling every 10 degrees.\"\n        pts = []\n        for i in range(0, 361, angle):\n            v = V2.polar(i)\n            pts.append(self(v) * v)\n        return pts\n\n    def to_segments(self, angle: int = 45) -> Iterable[Tuple[P2, P2]]:\n        \"Draws an envelope by sampling every 10 degrees.\"\n        segments = []\n        for i in range(0, 361, angle):\n            v = V2.polar(i)\n            segments.append((origin, self(v) * v))\n        return segments\n\n\nclass GetEnvelope(DiagramVisitor[Envelope, Affine]):\n    A_type = Envelope\n\n    def visit_primitive(\n        self, diagram: Primitive, t: Affine = Ident\n    ) -> Envelope:\n        new_transform = t * diagram.transform\n        return diagram.shape.get_envelope().apply_transform(new_transform)\n\n    def visit_compose(self, diagram: Compose, t: Affine = Ident) -> Envelope:\n        return diagram.envelope.apply_transform(t)\n\n    def visit_apply_transform(\n        self, diagram: ApplyTransform, t: Affine = Ident\n    ) -> Envelope:\n        n = t * diagram.transform\n        return diagram.diagram.accept(self, n)\n\n\ndef get_envelope(self: Diagram, t: Affine = Ident) -> Envelope:\n    return self.accept(GetEnvelope(), t)\n"
  },
  {
    "path": "chalk/model.py",
    "content": "from colour import Color\n\nfrom chalk.combinators import concat\nfrom chalk.shapes import Path, circle, text\nfrom chalk.shapes.segment import seg\nfrom chalk.transform import V2, origin\nfrom chalk.types import Diagram\n\nRED = Color(\"red\")\nBLUE = Color(\"blue\")\n\n\ndef show_origin(self: Diagram) -> Diagram:\n    \"Add a red dot at the origin of a diagram for debugging.\"\n\n    envelope = self.get_envelope()\n    if envelope.is_empty:\n        return self\n    origin_size = max(0.1, min(envelope.height, envelope.width) / 50)\n    origin = circle(origin_size).line_color(RED)\n    return self + origin\n\n\ndef show_envelope(\n    self: Diagram, phantom: bool = False, angle: int = 45\n) -> Diagram:\n    \"\"\"Add red envelope to diagram for debugging.\n\n    Args:\n        self (Diagram) : Diagram\n        phantom (bool): Don't include debugging in the envelope\n        angle (int): Angle increment to show debugging lines.\n\n    Returns:\n        Diagram\n    \"\"\"\n\n    self.show_origin()\n    envelope = self.get_envelope()\n    if envelope.is_empty:\n        return self\n    outer: Diagram = (\n        Path.from_points(list(envelope.to_path(angle)))\n        .stroke()\n        .fill_opacity(0)\n        .line_color(RED)\n    )\n    outer += (\n        concat([seg(y).stroke() for (x, y) in envelope.to_segments(angle)])\n        .line_color(RED)\n        .dashing([0.01, 0.01], 0)\n    )\n\n    new = self + outer\n    if phantom:\n        new = new.with_envelope(self)\n    return new\n\n\ndef show_beside(self: Diagram, other: Diagram, direction: V2) -> Diagram:\n    \"Add blue normal line to show placement of combination.\"\n\n    envelope1 = self.get_envelope()\n    envelope2 = other.get_envelope()\n    v1 = envelope1.envelope_v(direction)\n    one: Diagram = (\n        Path.from_points([origin, v1])\n        .stroke()\n        .line_color(RED)\n        .dashing([0.01, 0.01], 0)\n        .line_width(0.01)\n    )\n    v2 = envelope2.envelope_v(-direction)\n    two: Diagram = (\n        Path.from_points([origin, v2])\n        .stroke()\n        .line_color(RED)\n        .dashing([0.01, 0.01], 0)\n        .line_width(0.01)\n    )\n    split: Diagram = (\n        Path.from_points(\n            [\n                v1 + direction.perpendicular(),\n                v1 - direction.perpendicular(),\n            ]\n        )\n        .stroke()\n        .line_color(BLUE)\n        .line_width(0.02)\n    )\n    one = (self.show_origin() + one + split).with_envelope(self)\n    two = (other.show_origin() + two).with_envelope(other)\n    return one.beside(two, direction)\n\n\ndef show_labels(self: Diagram, font_size: int = 1) -> Diagram:\n    \"\"\"Shows the labels of all named subdiagrams of a diagram at their\n    corresponding origin.\"\"\"\n    for name, subs in self.get_sub_map().items():\n        for sub in subs:\n            n = str(name)\n            p = sub.get_location()\n            self = self + text(n, font_size).fill_color(RED).translate_by(p)\n    return self\n"
  },
  {
    "path": "chalk/monoid.py",
    "content": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import (\n    Callable,\n    Generic,\n    Iterable,\n    Iterator,\n    List,\n    Optional,\n    TypeVar,\n)\n\nfrom typing_extensions import Self\n\no = TypeVar(\"o\")\n\n\ndef associative_reduce(\n    fn: Callable[[o, o], o], iter: Iterable[o], initial: o\n) -> o:\n    \"Reduce for associative operations.\"\n    ls = list(iter)\n    if len(ls) == 0:\n        return initial\n    if len(ls) == 1:\n        return ls[0]\n    off = len(ls) % 2\n    v = associative_reduce(\n        fn, [fn(ls[i], ls[i + 1]) for i in range(0, len(ls) - off, 2)], initial\n    )\n    if off:\n        v = fn(v, ls[-1])\n    return v\n\n\nclass Monoid:\n    @classmethod\n    def empty(cls) -> Self:\n        raise NotImplementedError()\n\n    def __add__(self, other: Self) -> Self:\n        raise NotImplementedError()\n\n    @classmethod\n    def concat(cls, elems: Iterable[Self]) -> Self:\n        return associative_reduce(cls.__add__, elems, cls.empty())\n\n\nA = TypeVar(\"A\")\n\n\n@dataclass\nclass Maybe(Generic[A], Monoid):\n    data: Optional[A]\n\n    @classmethod\n    def empty(cls) -> Maybe[A]:\n        return Maybe(None)\n\n    def __add__(self, other: Maybe[A]) -> Maybe[A]:\n        if self.data is None:\n            return other\n        return self\n\n\n@dataclass\nclass MList(Generic[A], Monoid):\n    data: List[A]\n\n    @classmethod\n    def empty(cls) -> MList[A]:\n        return MList([])\n\n    def __add__(self, other: MList[A]) -> MList[A]:\n        return MList(self.data + other.data)\n\n    def __iter__(self) -> Iterator[A]:\n        return self.data.__iter__()\n"
  },
  {
    "path": "chalk/py.typed",
    "content": ""
  },
  {
    "path": "chalk/shapes/__init__.py",
    "content": "from typing import Optional, Tuple, Union\n\nfrom chalk.shapes.arc import ArcSegment, arc_seg, arc_seg_angle  # noqa: F401\nfrom chalk.shapes.arrowheads import ArrowHead, dart  # noqa: F401\nfrom chalk.shapes.image import Image, from_pil, image  # noqa: F401\nfrom chalk.shapes.latex import Latex, latex  # noqa: F401\nfrom chalk.shapes.path import Path, make_path  # noqa: F401\nfrom chalk.shapes.segment import Segment, seg  # noqa: F401\nfrom chalk.shapes.shape import Shape, Spacer  # noqa: F401\nfrom chalk.shapes.text import Text, text  # noqa: F401\nfrom chalk.trail import SegmentLike, Trail  # noqa: F401\nfrom chalk.transform import P2, V2\nfrom chalk.types import Diagram\n\n# Functions mirroring Diagrams.2d.Shapes\n\n\ndef hrule(length: float) -> Diagram:\n    return Trail.hrule(length).stroke().center_xy()\n\n\ndef vrule(length: float) -> Diagram:\n    return Trail.vrule(length).stroke().center_xy()\n\n\n# def polygon(sides: int, radius: float, rotation: float = 0) -> Diagram:\n#     \"\"\"\n#     Draw a polygon.\n\n#     Args:\n#        sides (int): Number of sides.\n#        radius (float): Internal radius.\n#        rotation: (int): Rotation in degrees\n\n#     Returns:\n#        Diagram\n#     \"\"\"\n#     return Trail.polygon(sides, radius, to_radians(rotation)).stroke()\n\n\ndef regular_polygon(sides: int, side_length: float) -> Diagram:\n    \"\"\"Draws a regular polygon with given number of sides and given side\n    length. The polygon is oriented with one edge parallel to the x-axis.\"\"\"\n    return Trail.regular_polygon(sides, side_length).centered().stroke()\n\n\ndef triangle(width: float) -> Diagram:\n    \"\"\"Draws an equilateral triangle with the side length specified by\n    the ``width`` argument. The origin is the traingle's centroid.\"\"\"\n    return regular_polygon(3, width)\n\n\ndef rectangle(\n    width: float, height: float, radius: Optional[float] = None\n) -> Diagram:\n    \"\"\"\n    Draws a rectangle.\n\n    Args:\n        width (float): Width\n        height (float): Height\n        radius (Optional[float]): Radius for rounded corners.\n\n    Returns:\n        Diagrams\n    \"\"\"\n    if radius is None:\n        return Trail.rectangle(width, height).stroke().center_xy()\n    else:\n        return (\n            Trail.rounded_rectangle(width, height, radius).stroke().center_xy()\n        )\n\n\ndef square(side: float) -> Diagram:\n    \"\"\"Draws a square with the specified side length. The origin is the\n    center of the square.\"\"\"\n    return rectangle(side, side)\n\n\ndef circle(radius: float) -> Diagram:\n    \"Draws a circle with the specified ``radius``.\"\n    return Trail.circle().stroke().center_xy().scale(radius)\n\n\ndef arc(radius: float, angle0: float, angle1: float) -> Diagram:\n    \"\"\"\n    Draws an arc.\n\n    Args:\n      radius (float): Circle radius.\n      angle0 (float): Starting cutoff in degrees.\n      angle1 (float): Finishing cutoff in degrees.\n\n    Returns:\n      Diagram\n\n    \"\"\"\n    return (\n        ArcSegment(angle0, angle1 - angle0)\n        .at(V2.polar(angle0, 1))\n        .stroke()\n        .scale(radius)\n    )\n\n\ndef arc_between(\n    point1: Union[P2, Tuple[float, float]],\n    point2: Union[P2, Tuple[float, float]],\n    height: float,\n) -> Diagram:\n    \"\"\"Makes an arc starting at point1 and ending at point2, with the midpoint\n    at a distance of abs(height) away from the straight line from point1 to\n    point2. A positive value of height results in an arc to the left of the\n    line from point1 to point2; a negative value yields one to the right.\n    The implementation is based on the the function arcBetween from Haskell's\n    diagrams:\n    https://hackage.haskell.org/package/diagrams-lib-1.4.5.1/docs/src/Diagrams.TwoD.Arc.html#arcBetween\n    \"\"\"\n    p = P2(*point1)\n    q = P2(*point2)\n    return arc_seg(q - p, height).at(p).stroke()\n\n\nignore = [Optional]\n\n__all__ = [\n    \"Segment\",\n    \"seg\",\n    \"Shape\",\n    \"Spacer\",\n    \"Text\",\n    \"text\",\n    \"SegmentLike\",\n    \"Trail\",\n    \"P2\",\n    \"V2\",\n    \"Diagram\",\n    \"hrule\",\n    \"vrule\",\n    \"regular_polygon\",\n    \"triangle\",\n    \"rectangle\",\n    \"square\",\n    \"circle\",\n    \"arc\",\n    \"arc_between\",\n    \"Latex\",\n    \"Trail\",\n    \"Path\",\n    \"Image\",\n    \"ArrowHead\",\n    \"arc_seg\",\n    \"dart\",\n    \"ArcSegment\",\n    \"from_pil\",\n    \"make_path\",\n    \"arc_seg_angle\",\n]\n"
  },
  {
    "path": "chalk/shapes/arc.py",
    "content": "\"\"\"\nContains arithmetic for arc calculations.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport math\nfrom dataclasses import dataclass\nfrom typing import TYPE_CHECKING, List, Union\n\nfrom planar.py import Ray\n\nimport chalk.transform as tx\nfrom chalk.envelope import Envelope\nfrom chalk.shapes.segment import LocatedSegment, ray_circle_intersection\nfrom chalk.trace import Trace\nfrom chalk.transform import P2, V2, from_radians, unit_x, unit_y\nfrom chalk.types import Enveloped, Traceable, TrailLike\n\nif TYPE_CHECKING:\n    from chalk.trail import Trail\n\nIdent = tx.Affine.identity()\nORIGIN = P2(0, 0)\n\nDegrees = float\n\n\ndef is_in_mod_360(x: Degrees, a: Degrees, b: Degrees) -> bool:\n    \"\"\"Checks if x ∈ [a, b] mod 360. See the following link for an\n    explanation:\n    https://fgiesen.wordpress.com/2015/09/24/intervals-in-modular-arithmetic/\n    \"\"\"\n    return (x - a) % 360 <= (b - a) % 360\n\n\n@dataclass\nclass LocatedArcSegment(Traceable, Enveloped, tx.Transformable):\n    \"A ellipse arc represented with the cetner parameterization\"\n    angle: float\n    dangle: float\n\n    # Ellipse is closed under affine.\n    t: tx.Affine = tx.Affine.identity()\n\n    # Parts to be careful about translation-invariance\n    @property\n    def p(self) -> P2:\n        \"Real start\"\n        return tx.apply_p2_affine(self.t, P2.polar(self.angle, 1))\n\n    @property\n    def q(self) -> P2:\n        \"Real end\"\n        return tx.apply_p2_affine(\n            self.t, P2.polar(self.angle + self.dangle, 1)\n        )\n\n    @property\n    def q_angle(self) -> float:\n        return (self.q - self.center).angle\n\n    @property\n    def center(self) -> P2:\n        \"Real center\"\n        return tx.apply_p2_affine(self.t, P2(0, 0))\n\n    @property\n    def r_x(self) -> float:\n        t2 = tx.remove_translation(self.t)\n        return tx.apply_p2_affine(t2, unit_x).length\n\n    @property\n    def r_y(self) -> float:\n        t2 = tx.remove_translation(self.t)\n        return tx.apply_p2_affine(t2, unit_y).length\n\n    @property\n    def rot(self) -> float:\n        t2 = tx.remove_translation(self.t)\n        return tx.apply_p2_affine(t2, unit_x).angle\n\n    def get_trace(self, t: tx.Affine = Ident) -> Trace:\n        \"Trace is done as simple arc and transformed\"\n        angle0_deg = self.angle\n        angle1_deg = self.angle + self.dangle\n\n        def f(p: P2, v: V2) -> List[float]:\n            ray = Ray(p, v)\n            return sorted(\n                [\n                    d / v.length\n                    for d in ray_circle_intersection(ray, 1)\n                    if is_in_mod_360(\n                        ((d * v) + P2.polar(self.angle, 1)).angle,\n                        min(angle0_deg, angle1_deg),\n                        max(angle0_deg, angle1_deg),\n                    )\n                ]\n            )\n\n        return Trace(f).apply_transform(self.t)\n\n    def get_envelope(self, t: tx.Affine = Ident) -> Envelope:\n        \"Trace is done as simple arc and transformed\"\n        angle0_deg = self.angle\n        angle1_deg = self.angle + self.dangle\n\n        v1 = V2.polar(angle0_deg, 1)\n        v2 = V2.polar(angle1_deg, 1)\n\n        def wrapped(d: V2) -> float:\n            is_circle = abs(angle0_deg - angle1_deg) >= 360\n            if is_circle or is_in_mod_360(\n                d.angle,\n                min(angle0_deg, angle1_deg),\n                max(angle0_deg, angle1_deg),\n            ):\n                # Case 1: P2 at arc\n                return 1 / d.length\n            else:\n                # Case 2: P2 outside of arc\n                x: float = max(d.dot(v1), d.dot(v2))\n                return x\n\n        return Envelope(wrapped).apply_transform(self.t)\n\n    @staticmethod\n    def arc_between(\n        p: P2, q: P2, height: float\n    ) -> Union[LocatedSegment, LocatedArcSegment]:\n\n        h = abs(height)\n        if h < 1e-3:\n            return LocatedSegment(q - p, p)\n        d = (q - p).length\n        # Determine the arc's angle θ and its radius r\n        θ = math.acos((d**2 - 4.0 * h**2) / (d**2 + 4.0 * h**2))\n        r = d / (2 * math.sin(θ))\n\n        if height > 0:\n            # bend left\n            φ = +math.pi / 2\n            dy = r - h\n            flip = 1\n        else:\n            # bend right\n            φ = -math.pi / 2\n            dy = h - r\n            flip = -1\n\n        diff = q - p\n        ret = (\n            ArcSegment(flip * -from_radians(θ), flip * 2 * from_radians(θ))\n            .scale(r)\n            .rotate_rad(φ)\n            .translate(d / 2, dy)\n        )\n\n        ret = ret.rotate(-diff.angle).translate_by(p)\n        return ret\n\n\n@dataclass\nclass ArcSegment(LocatedArcSegment, TrailLike):\n    \"A translation invariant version of Arc\"\n\n    def __post_init__(self) -> None:\n        self.t = tx.Affine.translation(-self.p) * self.t\n        assert self.p.x == 0, self.p\n        assert self.p.y == 0, self.p\n\n    def apply_transform(self, t: tx.Affine) -> ArcSegment:\n        t = tx.remove_translation(t)\n        return ArcSegment(self.angle, self.dangle, t * self.t)\n\n    def to_trail(self) -> Trail:\n        from chalk.trail import Trail\n\n        return Trail([self])\n\n    @staticmethod\n    def arc_between_trail(q: P2, height: float) -> Trail:\n        segment = LocatedArcSegment.arc_between(P2(0, 0), q, height)\n        if isinstance(segment, LocatedArcSegment):\n            return ArcSegment(\n                segment.angle, segment.dangle, segment.t\n            ).to_trail()\n        else:\n            return segment.to_trail()\n\n    def reverse(self) -> ArcSegment:\n        angle = self.angle + self.dangle\n        dangle = -self.dangle\n        return ArcSegment(angle, dangle).apply_transform(self.t)\n\n\ndef arc_seg(q: V2, height: float) -> Trail:\n    return ArcSegment.arc_between_trail(q, height)\n\n\ndef arc_seg_angle(angle: float, dangle: float) -> Trail:\n    return ArcSegment(angle, dangle).to_trail()\n\n\n# def arc_between(p:P2, q: P2, height: float) -> Path:\n#     return ArcSegment.arc_between_trail(q - p, height).at(p)\n"
  },
  {
    "path": "chalk/shapes/arrowheads.py",
    "content": "from dataclasses import dataclass\nfrom typing import Any\n\nfrom colour import Color\n\nfrom chalk.shapes.path import Path\nfrom chalk.shapes.shape import Shape\nfrom chalk.transform import P2, BoundingBox, origin\nfrom chalk.types import Diagram\nfrom chalk.visitor import A, ShapeVisitor\n\nblack = Color(\"black\")\n\n\ndef tri() -> Diagram:\n    return (\n        Path.from_list_of_tuples(\n            [(1.0, 0), (0.0, -1.0), (-1.0, 0), (1.0, 0)], closed=True\n        )\n        .stroke()\n        .rotate_by(-0.25)\n        .fill_color(Color(\"black\"))\n        .center_xy()\n        .align_r()\n        .line_width(0)\n    )\n\n\ndef dart(cut: float = 0.2) -> Diagram:\n    return (\n        Path.from_list_of_tuples(\n            [\n                (0, -cut),\n                (1.0, cut),\n                (0.0, -1.0 - cut),\n                (-1.0, +cut),\n                (0, -cut),\n            ],\n            closed=True,\n        )\n        .stroke()\n        .rotate_by(-0.25)\n        .fill_color(Color(\"black\"))\n        .center_xy()\n        .align_r()\n        .line_width(0)\n    )\n\n\n@dataclass\nclass ArrowHead(Shape):\n    \"\"\"Arrow Head.\"\"\"\n\n    arrow_shape: Diagram\n\n    def get_bounding_box(self) -> BoundingBox:\n        # Arrow head don't have a bounding box since we can't accurately know\n        # the size until rendering\n        eps = 1e-4\n        self.bb = BoundingBox([origin, origin + P2(eps, eps)])\n        return self.bb\n\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A:\n        return visitor.visit_arrowhead(self, **kwargs)\n"
  },
  {
    "path": "chalk/shapes/image.py",
    "content": "from dataclasses import dataclass\nfrom io import BytesIO\nfrom typing import Any, Optional\n\nimport PIL\nfrom PIL import Image as Im\n\nfrom chalk.shapes.shape import Shape\nfrom chalk.transform import P2, BoundingBox, origin\nfrom chalk.types import Diagram\n\n\ndef from_pil(\n    im: Im.Image,\n    alpha: float = 1.0,\n) -> Any:\n    import cairo\n\n    format: cairo.Format = cairo.FORMAT_ARGB32\n    if \"A\" not in im.getbands():\n        im.putalpha(int(alpha * 256.0))  # type: ignore\n    arr = bytearray(im.tobytes(\"raw\", \"BGRa\"))\n    surface = cairo.ImageSurface.create_for_data(\n        arr, format, im.width, im.height  # type: ignore\n    )\n    return surface\n\n\n@dataclass\nclass Image(Shape):\n    \"\"\"Image class.\"\"\"\n\n    local_path: str\n    url_path: Optional[str]\n\n    def __post_init__(self) -> None:\n        if self.local_path.endswith(\"svg\"):\n            import cairosvg\n\n            out = BytesIO()\n            cairosvg.svg2png(url=self.local_path, write_to=out)\n        else:\n            out = open(self.local_path, \"rb\")  # type:ignore\n\n        self.im = PIL.Image.open(out)\n        self.height = self.im.height\n        self.width = self.im.width\n\n    def get_bounding_box(self) -> BoundingBox:\n        left = origin.x - self.width / 2\n        top = origin.y - self.height / 2\n        tl = P2(left, top)\n        br = P2(left + self.width, top + self.height)\n        return BoundingBox([tl, br])\n\n\ndef image(local_path: str, url_path: Optional[str]) -> Diagram:\n    from chalk.core import Primitive\n\n    return Primitive.from_shape(Image(local_path, url_path))\n"
  },
  {
    "path": "chalk/shapes/latex.py",
    "content": "from dataclasses import dataclass\nfrom typing import Any\n\nfrom chalk.shapes.shape import Shape\nfrom chalk.transform import P2, BoundingBox, origin\nfrom chalk.types import Diagram\nfrom chalk.visitor import A, ShapeVisitor\n\n\n@dataclass\nclass Latex(Shape):\n    \"\"\"Latex class.\"\"\"\n\n    text: str\n\n    def __post_init__(self) -> None:\n        # Need to install latextools for this to run.\n        import latextools\n\n        # Border ensures no cropping.\n        latex_eq = latextools.render_snippet(\n            f\"{self.text}\",\n            commands=[latextools.cmd.all_math],\n            config=latextools.DocumentConfig(\n                \"standalone\", {\"crop=true,border=0.1cm\"}\n            ),\n        )\n        self.eq = latex_eq.as_svg()\n        eq_lines = self.eq.content.split(\"\\n\")\n        c = \"<g>\\n\" + \"\\n\".join(eq_lines[2:-2]) + \"\\n</g>\"\n\n        # Undo scaling done by latextools\n        # https://github.com/cduck/latextools/blob/caa15da02d88e5a4c82eb06f8fadbe48abd7ad2f/latextools/convert.py#L131\n        self.width = self.eq.width * 3 / 4\n        self.height = self.eq.height * 3 / 4\n        self.content = c\n\n        # From latextools Ensures no clash between multiple math statements\n        id_prefix = f\"embed-{hash(self.content)}-\"\n        self.content = (\n            self.content.replace('id=\"', f'id=\"{id_prefix}')\n            .replace('=\"url(#', f'=\"url(#{id_prefix}')\n            .replace('xlink:href=\"#', f'href=\"#{id_prefix}')\n        )\n\n    def get_bounding_box(self) -> BoundingBox:\n        eps = 1e-4\n        self.bb = BoundingBox([origin, origin + P2(eps, eps)])\n        return self.bb\n\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A:\n        return visitor.visit_latex(self, **kwargs)\n\n\ndef latex(t: str) -> Diagram:\n    from chalk.core import Primitive\n\n    return Primitive.from_shape(Latex(t))\n"
  },
  {
    "path": "chalk/shapes/path.py",
    "content": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import Any, Iterable, List, Tuple\n\nfrom chalk import transform as tx\nfrom chalk.envelope import Envelope\nfrom chalk.shapes.shape import Shape\nfrom chalk.trace import Trace\nfrom chalk.trail import Located, Trail\nfrom chalk.transform import P2, Transformable\nfrom chalk.types import Diagram, Enveloped, Traceable\nfrom chalk.visitor import A, ShapeVisitor\n\n\ndef make_path(\n    segments: List[Tuple[float, float]], closed: bool = False\n) -> Diagram:\n    return Path.from_list_of_tuples(segments, closed).stroke()\n\n\n@dataclass\nclass Path(Shape, Enveloped, Traceable, Transformable):\n    \"\"\"Path class.\"\"\"\n\n    loc_trails: List[Located]\n\n    # Monoid - compose\n    @staticmethod\n    def empty() -> Path:\n        return Path([])\n\n    def __add__(self, other: Path) -> Path:\n        return Path(self.loc_trails + other.loc_trails)\n\n    def apply_transform(self, t: tx.Affine) -> Path:\n        return Path(\n            [loc_trail.apply_transform(t) for loc_trail in self.loc_trails]\n        )\n\n    def points(self) -> Iterable[P2]:\n        for loc_trails in self.loc_trails:\n            for pt in loc_trails.points():\n                yield pt\n\n    def get_envelope(self) -> Envelope:\n        return Envelope.concat((loc.get_envelope() for loc in self.loc_trails))\n\n    def get_trace(self) -> Trace:\n        return Trace.concat((loc.get_trace() for loc in self.loc_trails))\n\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A:\n        return visitor.visit_path(self, **kwargs)\n\n    # Constructors\n    @staticmethod\n    def from_points(points: List[P2], closed: bool = False) -> Path:\n        if not points:\n            return Path.empty()\n        start = points[0]\n        trail = Trail.from_offsets(\n            [pt2 - pt1 for pt1, pt2 in zip(points, points[1:])], closed\n        )\n        return Path([trail.at(start)])\n\n    @staticmethod\n    def from_point(point: P2) -> Path:\n        return Path.from_points([point])\n\n    @staticmethod\n    def from_pairs(segs: List[Tuple[P2, P2]], closed: bool = False) -> Path:\n        if not segs:\n            return Path.empty()\n        ls = [segs[0][0]]\n        for seg in segs:\n            assert seg[0] == ls[-1]\n            ls.append(seg[1])\n        return Path.from_points(ls, closed)\n\n    @staticmethod\n    def from_list_of_tuples(\n        coords: List[Tuple[float, float]], closed: bool = False\n    ) -> Path:\n        points = list([P2(x, y) for x, y in coords])\n        return Path.from_points(points, closed)\n"
  },
  {
    "path": "chalk/shapes/segment.py",
    "content": "from __future__ import annotations\n\nimport math\nfrom dataclasses import dataclass\nfrom typing import TYPE_CHECKING, Any, List, Optional, Tuple\n\nfrom planar.py import Ray\n\nimport chalk.transform as tx\nfrom chalk.envelope import Envelope\nfrom chalk.trace import Trace\nfrom chalk.transform import P2, V2\nfrom chalk.types import Enveloped, Traceable, TrailLike\n\nif TYPE_CHECKING:\n    from chalk.trail import Trail\n\nSignedDistance = float\n\nIdent = tx.Affine.identity()\nORIGIN = P2(0, 0)\n\n\n@dataclass\nclass LocatedSegment(Traceable, Enveloped, tx.Transformable, TrailLike):\n    offset: V2\n    origin: P2 = ORIGIN\n\n    @property\n    def p(self) -> P2:\n        return self.origin\n\n    @staticmethod\n    def from_points(p: P2, q: P2) -> LocatedSegment:\n        return LocatedSegment(q - p, p)\n\n    def apply_transform(self, t: tx.Affine) -> LocatedSegment:\n        return LocatedSegment(\n            tx.apply_affine(t, self.offset), tx.apply_affine(t, self.origin)\n        )\n\n    @property\n    def q(self) -> P2:\n        return self.p + self.offset\n\n    def get_trace(self, t: tx.Affine = Ident) -> Trace:\n        def f(point: P2, direction: V2) -> List[float]:\n            ray = Ray(point, direction)\n            inter = sorted(line_segment(ray, self))\n            return [r / direction.length for r in inter]\n\n        return Trace(f)\n\n    def get_envelope(self, t: tx.Affine = Ident) -> Envelope:\n        def f(d: V2) -> SignedDistance:\n            x: float = max(d.dot(self.q), d.dot(self.p))\n            return x\n\n        return Envelope(f)\n\n    @property\n    def length(self) -> Any:\n        return self.offset.length\n\n    def to_ray(self) -> \"Ray\":\n        return Ray(self.p, self.q - self.p)\n\n    def to_trail(self) -> Trail:\n        from chalk.trail import Trail\n\n        return Trail([Segment(self.offset)])\n\n\n@dataclass\nclass Segment(LocatedSegment, TrailLike):\n    @property\n    def p(self) -> P2:\n        return ORIGIN\n\n    def apply_transform(self, t: tx.Affine) -> Segment:\n        return Segment(tx.apply_affine(tx.remove_translation(t), self.offset))\n\n    def reverse(self):  # type: ignore\n        return self.scale(-1)\n\n\ndef seg(offset: V2) -> Trail:\n    return Segment(offset).to_trail()\n\n\ndef ray_ray_intersection(\n    ray1: Ray, ray2: Ray\n) -> Optional[Tuple[float, float]]:\n    \"\"\"Given two rays\n\n    ray₁ = λ t . p₁ + t v₁\n    ray₂ = λ t . p₂ + t v₂\n\n    the function returns the parameters t₁ and t₂ at which the two rays meet,\n    that is:\n\n    ray₁ t₁ = ray₂ t₂\n\n    \"\"\"\n    u = ray2.anchor - ray1.anchor\n    x1 = ray1.direction.cross(ray2.direction)\n    x2 = u.cross(ray1.direction)\n    x3 = u.cross(ray2.direction)\n    if x1 == 0 and x2 != 0:\n        # parallel\n        return None\n    elif x1 == 0 and x2 == 0:\n        return 0.0, 0.0\n    else:\n        # intersecting or collinear\n        return x3 / x1, x2 / x1\n\n\ndef line_segment(ray: Ray, segment: LocatedSegment) -> List[float]:\n    \"\"\"Given a ray and a segment, return the parameter `t` for which the ray\n    meets the segment, that is:\n\n    ray t₁ = segment.to_ray t₂, with t₂ ∈ [0, segment.length]\n\n    Note: We need to consider the segment's length separately since `Ray`\n    normalizes the direction to unit and hences looses this information. The\n    length is important to determine whether the intersection point falls\n    within the given segment.\n\n    See also: https://github.com/danoneata/chalk/issues/91\n\n    \"\"\"\n    ray_s = segment.to_ray()\n    t = ray_ray_intersection(ray, ray_s)\n    if not t:\n        return []\n    else:\n        t1, t2 = t\n        # the intersection point is given by any of the two expressions:\n        # ray.anchor   + t1 * ray.direction\n        # ray_s.anchor + t2 * ray_s.direction\n        if 0 <= t2 <= segment.length:\n            # intersection point is in segment\n            return [t1]\n        else:\n            # intersection point outside\n            return []\n\n\ndef ray_circle_intersection(ray: Ray, circle_radius: float) -> List[float]:\n    \"\"\"Given a ray and a circle centered at the origin, return the parameter t\n    where the ray meets the circle, that is:\n\n    ray t = circle θ\n\n    The above equation is solved as follows:\n\n    x + t v_x = r sin θ\n    y + t v_y = r cos θ\n\n    By squaring the equations and adding them we get\n\n    (x + t v_x)² + (y + t v_y)² = r²,\n\n    which is equivalent to the following equation:\n\n    (v_x² + v_y²) t² + 2 (x v_x + y v_y) t + (x² + y² - r²) = 0\n\n    This is a quadratic equation, whose solutions are well known.\n\n    \"\"\"\n    p = ray.anchor\n\n    a = ray.direction.length2\n    b = 2 * (p.dot(ray.direction))\n    c = p.length2 - circle_radius**2\n\n    Δ = b**2 - 4 * a * c\n    eps = 1e-6  # rounding error tolerance\n\n    if Δ < -eps:\n        # no intersection\n        return []\n    elif -eps <= Δ < eps:\n        # tangent\n        return [-b / (2 * a)]\n    else:\n        # the ray intersects at two points\n        return [\n            (-b - math.sqrt(Δ)) / (2 * a),\n            (-b + math.sqrt(Δ)) / (2 * a),\n        ]\n"
  },
  {
    "path": "chalk/shapes/shape.py",
    "content": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import Any\n\nfrom chalk.envelope import Envelope\nfrom chalk.trace import Trace\nfrom chalk.trail import Trail\nfrom chalk.transform import P2, BoundingBox, origin\nfrom chalk.types import Diagram\nfrom chalk.visitor import A, ShapeVisitor\n\n\n@dataclass\nclass Shape:\n    \"\"\"Shape class.\"\"\"\n\n    def get_bounding_box(self) -> BoundingBox:\n        raise NotImplementedError\n\n    def get_envelope(self) -> Envelope:\n        return Envelope.from_bounding_box(self.get_bounding_box())\n\n    def get_trace(self) -> Trace:\n        box = self.get_bounding_box()\n        return (\n            Trail.rectangle(box.width, box.height)\n            .stroke()\n            .center_xy()\n            .get_trace()\n        )\n\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A:\n        raise NotImplementedError\n\n    def stroke(self) -> Diagram:\n        \"\"\"Returns a primitive (shape) with strokes\n\n        Returns:\n            Diagram: A diagram.\n        \"\"\"\n        from chalk.core import Primitive\n\n        return Primitive.from_shape(self)\n\n\n@dataclass\nclass Spacer(Shape):\n    \"\"\"Spacer class.\"\"\"\n\n    width: float\n    height: float\n\n    def get_bounding_box(self) -> BoundingBox:\n        left = origin.x - self.width / 2\n        top = origin.y - self.height / 2\n        tl = P2(left, top)\n        br = P2(left + self.width, top + self.height)\n        return BoundingBox([tl, br])\n\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A:\n        return visitor.visit_spacer(self, **kwargs)\n"
  },
  {
    "path": "chalk/shapes/text.py",
    "content": "from dataclasses import dataclass\nfrom typing import Any, Optional\n\nfrom chalk.shapes.shape import Shape\nfrom chalk.transform import P2, BoundingBox, origin\nfrom chalk.types import Diagram\nfrom chalk.visitor import A, ShapeVisitor\n\n\n@dataclass\nclass Text(Shape):\n    \"\"\"Text class.\"\"\"\n\n    text: str\n    font_size: Optional[float]\n\n    def get_bounding_box(self) -> BoundingBox:\n        # Text doesn't have a bounding box since we can't accurately know\n        # its size for all backends.\n        eps = 1e-4\n        self.bb = BoundingBox([origin, origin + P2(eps, eps)])\n        return self.bb\n\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A:\n        return visitor.visit_text(self, **kwargs)\n\n\ndef text(t: str, size: Optional[float]) -> Diagram:\n    \"\"\"\n    Draw some text.\n\n    Args:\n       t (str): The text string.\n       size (Optional[float]): Size of the text.\n\n    Returns:\n       Diagram\n\n    \"\"\"\n    from chalk.core import Primitive\n\n    return Primitive.from_shape(Text(t, font_size=size))\n"
  },
  {
    "path": "chalk/style.py",
    "content": "from __future__ import annotations\n\nfrom dataclasses import dataclass, fields\nfrom enum import Enum, auto\nfrom typing import Any, Dict, List, Optional, Tuple\n\nfrom colour import Color\nfrom typing_extensions import Self\n\nPyCairoContext = Any\nPyLatex = Any\n\n\nclass Stylable:\n    def line_width(self, width: float) -> Self:\n        return self.apply_style(\n            Style(line_width_=(WidthType.NORMALIZED, width))\n        )\n\n    def line_width_local(self, width: float) -> Self:\n        return self.apply_style(Style(line_width_=(WidthType.LOCAL, width)))\n\n    def line_color(self, color: Color) -> Self:\n        return self.apply_style(Style(line_color_=color))\n\n    def fill_color(self, color: Color) -> Self:\n        return self.apply_style(Style(fill_color_=color))\n\n    def fill_opacity(self, opacity: float) -> Self:\n        return self.apply_style(Style(fill_opacity_=opacity))\n\n    def dashing(self, dashing_strokes: List[float], offset: float) -> Self:\n        return self.apply_style(Style(dashing_=(dashing_strokes, offset)))\n\n    def apply_style(self: Self, style: Style) -> Self:\n        raise NotImplementedError(\"Abstract\")\n\n\ndef m(a: Optional[Any], b: Optional[Any]) -> Optional[Any]:\n    return a if a is not None else b\n\n\nclass WidthType(Enum):\n    LOCAL = auto()\n    NORMALIZED = auto()\n\n\nLC = Color(\"black\")\nLW = 0.1\n\n\n@dataclass\nclass Style(Stylable):\n    \"\"\"Style class.\"\"\"\n\n    line_width_: Optional[Tuple[WidthType, float]] = None\n    line_color_: Optional[Color] = None\n    fill_color_: Optional[Color] = None\n    fill_opacity_: Optional[float] = None\n    dashing_: Optional[Tuple[List[float], float]] = None\n    output_size: Optional[float] = None\n\n    @classmethod\n    def empty(cls) -> Style:\n        return cls()\n\n    @classmethod\n    def root(cls, output_size: float) -> Style:\n        return cls(output_size=output_size)\n\n    def apply_style(self, other: Style) -> Style:\n        return self.merge(other)\n\n    def merge(self, other: Style) -> Style:\n        \"\"\"Merges two styles and returns the merged style.\n\n        Args:\n            other (Style): Another style object.\n\n        Returns:\n            Style: A style object.\n        \"\"\"\n        return Style(\n            *(\n                m(getattr(other, dim.name), getattr(self, dim.name))\n                for dim in fields(self)\n            )\n        )\n\n    def render(self, ctx: PyCairoContext) -> None:\n        \"\"\"Renders the style object.\n\n        Args:\n            ctx (PyCairoContext): A context.\n        \"\"\"\n        if self.fill_color_:\n            if self.fill_opacity_ is None:\n                op = 1.0\n            else:\n                op = self.fill_opacity_\n\n            ctx.set_source_rgba(*self.fill_color_.rgb, op)\n            ctx.fill_preserve()\n\n        # set default values if they are not provided\n        if self.line_color_ is None:\n            lc = LC\n        else:\n            lc = self.line_color_\n        # Set by observation\n        assert self.output_size is not None\n        normalizer = self.output_size * (15 / 500)\n        if self.line_width_ is None:\n            lw = LW * normalizer\n        else:\n            lwt, lw = self.line_width_\n            if lwt == WidthType.NORMALIZED:\n                lw = lw * normalizer\n\n            elif lwt == WidthType.LOCAL:\n                lw = lw\n        ctx.set_source_rgb(*lc.rgb)\n        ctx.set_line_width(lw)\n\n        if self.dashing_ is not None:\n            ctx.set_dash(self.dashing_[0], self.dashing_[1])\n\n    def to_svg(self) -> str:\n        \"\"\"Converts to SVG.\n\n        Returns:\n            str: A string notation of the SVG.\n        \"\"\"\n\n        style = \"\"\n        if self.fill_color_ is not None:\n            style += f\"fill: {self.fill_color_.hex_l};\"\n        if self.line_color_ is not None:\n            style += f\"stroke: {self.line_color_.hex_l};\"\n        else:\n            style += \"stroke: black;\"\n\n        # Set by observation\n        assert self.output_size is not None\n        normalizer = self.output_size * (15 / 500)\n        if self.line_width_ is not None:\n            lwt, lw = self.line_width_\n            if lwt == WidthType.NORMALIZED:\n                lw = lw * normalizer\n            elif lwt == WidthType.LOCAL:\n                lw = lw\n        else:\n            lw = LW * normalizer\n\n        style += f\"stroke-width: {lw};\"\n\n        if self.fill_opacity_ is not None:\n            style += f\"fill-opacity: {self.fill_opacity_};\"\n        if self.dashing_ is not None:\n            style += (\n                f\"stroke-dasharray: {' '.join(map(str, self.dashing_[0]))};\"\n            )\n\n        return style\n\n    def to_tikz(self, pylatex: PyLatex) -> Dict[str, str]:\n        \"\"\"Converts to dictionary of tikz options.\"\"\"\n        style = {}\n\n        def tikz_color(color: Color) -> str:\n            r, g, b = color.rgb\n            return f\"{{rgb,1:red,{r}; green,{g}; blue,{b}}}\"\n\n        if self.fill_color_ is not None:\n            style[\"fill\"] = tikz_color(self.fill_color_)\n        if self.line_color_ is not None:\n            style[\"draw\"] = tikz_color(self.line_color_)\n        # This constant was set based on observing TikZ output\n        assert self.output_size is not None\n        normalizer = self.output_size * (175 / 500)\n\n        if self.line_width_ is not None:\n            lwt, lw = self.line_width_\n            if lwt == WidthType.NORMALIZED:\n                lw = lw * normalizer\n            elif lwt == WidthType.LOCAL:\n                lw = lw\n        else:\n            lw = normalizer * LW\n        style[\"line width\"] = f\"{lw}pt\"\n        if self.fill_opacity_ is not None:\n            style[\"fill opacity\"] = f\"{self.fill_opacity_}\"\n        if self.dashing_ is not None:\n            style[\"dash pattern\"] = (\n                f\"{{on {self.dashing_[0][0]}pt off {self.dashing_[0][0]}pt}}\"\n            )\n\n        return style\n"
  },
  {
    "path": "chalk/subdiagram.py",
    "content": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple\n\nfrom chalk.envelope import Envelope\nfrom chalk.monoid import Maybe, Monoid\nfrom chalk.trace import Trace\nfrom chalk.transform import P2, V2, Affine, apply_p2_affine, origin\nfrom chalk.types import Diagram\nfrom chalk.visitor import DiagramVisitor\n\nif TYPE_CHECKING:\n    from chalk.core import ApplyName, ApplyTransform, Compose\n\n\nIdent = Affine.identity()\nAtomicName = Any\n\n\n@dataclass\nclass Name:\n    atomic_names: Tuple[AtomicName, ...]\n\n    def __init__(self, atomic_name: AtomicName):\n        self.atomic_names = (atomic_name,)\n\n    def __hash__(self) -> int:\n        return hash(self.atomic_names)\n\n    def __str__(self) -> str:\n        return \"·\".join(map(str, self.atomic_names))\n\n    def __add__(self, other: Name) -> Name:\n        new_name = Name(None)\n        new_name.atomic_names = self.atomic_names + other.atomic_names\n        return new_name\n\n    def qualify(self, name: Name) -> Name:\n        return name + self\n\n\n@dataclass\nclass Subdiagram(Monoid):\n    diagram: Diagram\n    transform: Affine\n    # style: Style\n\n    def get_location(self) -> P2:\n        return apply_p2_affine(self.transform, origin)\n\n    def get_envelope(self) -> Envelope:\n        return self.diagram.get_envelope().apply_transform(self.transform)\n\n    def get_trace(self) -> Trace:\n        return self.diagram.get_trace().apply_transform(self.transform)\n\n    def boundary_from(self, v: V2) -> P2:\n        \"\"\"Returns the furthest point on the boundary of the subdiagram,\n        starting from the local origin of the subdiagram and going in the\n        direction of the given vector `v`.\n        \"\"\"\n        o = self.get_location()\n        p = self.get_trace().trace_p(o, -v)\n        if not p:\n            return origin\n        else:\n            return p\n\n\nclass GetSubdiagram(DiagramVisitor[Maybe[Subdiagram], Affine]):\n    A_type = Maybe[Subdiagram]\n\n    def __init__(self, name: Name, t: Affine = Ident):\n        self.name = name\n\n    def visit_compose(\n        self,\n        diagram: Compose,\n        t: Affine = Ident,\n    ) -> Maybe[Subdiagram]:\n        for d in diagram.diagrams:\n            bb = d.accept(self, t)\n            if bb.data is not None:\n                return bb\n        return Maybe.empty()\n\n    def visit_apply_transform(\n        self,\n        diagram: ApplyTransform,\n        t: Affine = Ident,\n    ) -> Maybe[Subdiagram]:\n        return diagram.diagram.accept(self, t * diagram.transform)\n\n    def visit_apply_name(\n        self,\n        diagram: ApplyName,\n        t: Affine = Ident,\n    ) -> Maybe[Subdiagram]:\n        if self.name == diagram.dname:\n            return Maybe(Subdiagram(diagram.diagram, t))\n        else:\n            return diagram.diagram.accept(self, t)\n\n\ndef get_subdiagram(self: Diagram, name: Name) -> Optional[Subdiagram]:\n    return self.accept(GetSubdiagram(name), Ident).data\n\n\ndef with_names(\n    self: Diagram,\n    names: List[Name],\n    f: Callable[[List[Subdiagram], Diagram], Diagram],\n) -> Diagram:\n    # NOTE Instead of performing a pass of the AST for each `name` in `names`,\n    # it might be more efficient to retrieve all named subdiagrams using the\n    # `get_sub_map` function and then filter the subdiagrams specified by\n    # `names`.\n    subs = [self.get_subdiagram(name) for name in names]\n    if any(sub is None for sub in subs):\n        # return self\n        raise LookupError(\"One of the names is missing from the diagram\")\n    else:\n        # NOTE Unfortunately, mypy is not narrowing the type when using the\n        # `any` or `all` functions.\n        # https://github.com/python/mypy/issues/13069\n        # Hopefully this bug will be fixed at some point in the future.\n        return f(subs, self)  # type: ignore\n\n\n@dataclass\nclass SubMap(Monoid):\n    data: Dict[Name, List[Subdiagram]]\n\n    def __add__(self, other: SubMap) -> SubMap:\n        d1 = self.data\n        d2 = other.data\n        return SubMap(\n            {k: d1.get(k, []) + d2.get(k, []) for k in set(d1) | set(d2)}\n        )\n\n    @classmethod\n    def empty(cls) -> SubMap:\n        return SubMap({})\n\n\nclass GetSubMap(DiagramVisitor[SubMap, Affine]):\n    A_type = SubMap\n\n    def visit_apply_transform(\n        self,\n        diagram: ApplyTransform,\n        t: Affine = Ident,\n    ) -> SubMap:\n        return diagram.diagram.accept(self, t * diagram.transform)\n\n    def visit_apply_name(\n        self,\n        diagram: ApplyName,\n        t: Affine = Ident,\n    ) -> SubMap:\n        d1 = SubMap({diagram.dname: [Subdiagram(diagram.diagram, t)]})\n        d2 = diagram.diagram.accept(self, t)\n        return d1 + d2\n\n\ndef get_sub_map(\n    self: Diagram, t: Affine = Ident\n) -> Dict[Name, List[Subdiagram]]:\n    \"\"\"Retrieves all named subdiagrams in the given diagram and accumulates\n    them in a dictionary (map) indexed by their name.\n    \"\"\"\n    return self.accept(GetSubMap(), t).data\n"
  },
  {
    "path": "chalk/trace.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Callable, List, Optional\n\nfrom chalk.monoid import Monoid\nfrom chalk.transform import (\n    P2,\n    V2,\n    Affine,\n    Transformable,\n    apply_affine,\n    remove_translation,\n)\nfrom chalk.visitor import DiagramVisitor\n\nif TYPE_CHECKING:\n    from chalk.core import ApplyTransform, Primitive\n    from chalk.types import Diagram\n\n\nSignedDistance = float\nIdent = Affine.identity()\n\n\nclass Trace(Monoid, Transformable):\n    def __init__(self, f: Callable[[P2, V2], List[SignedDistance]]) -> None:\n        self.f = f\n\n    def __call__(self, point: P2, direction: V2) -> List[SignedDistance]:\n        return self.f(point, direction)\n\n    # Monoid\n    @classmethod\n    def empty(cls) -> Trace:\n        return cls(lambda point, direction: [])\n\n    def __add__(self, other: Trace) -> Trace:\n        return Trace(\n            lambda point, direction: self(point, direction)\n            + other(point, direction)\n        )\n\n    # Transformable\n    def apply_transform(self, t: Affine) -> Trace:\n        def wrapped(p: P2, d: V2) -> List[SignedDistance]:\n            t1 = ~t\n            return self(\n                apply_affine(t1, p), apply_affine(remove_translation(t1), d)\n            )\n\n        return Trace(wrapped)\n\n    def trace_v(self, p: P2, v: V2) -> Optional[V2]:\n        v = v.scaled_to(1)\n        dists = self(p, v)\n        if dists:\n            s, *_ = sorted(dists)\n            return s * v\n        else:\n            return None\n\n    def trace_p(self, p: P2, v: V2) -> Optional[P2]:\n        u = self.trace_v(p, v)\n        return p + u if u else None\n\n    def max_trace_v(self, p: P2, v: V2) -> Optional[V2]:\n        return self.trace_v(p, -v)\n\n    def max_trace_p(self, p: P2, v: V2) -> Optional[P2]:\n        u = self.max_trace_v(p, v)\n        return p + u if u else None\n\n\nclass GetTrace(DiagramVisitor[Trace, Affine]):\n    A_type = Trace\n\n    def visit_primitive(self, diagram: Primitive, t: Affine = Ident) -> Trace:\n        new_transform = t * diagram.transform\n        return diagram.shape.get_trace().apply_transform(new_transform)\n\n    def visit_apply_transform(\n        self, diagram: ApplyTransform, t: Affine = Ident\n    ) -> Trace:\n        return diagram.diagram.accept(self, t * diagram.transform)\n\n\ndef get_trace(self: Diagram, t: Affine = Ident) -> Trace:\n    return self.accept(GetTrace(), t)\n"
  },
  {
    "path": "chalk/trail.py",
    "content": "from __future__ import annotations\n\nimport math\nfrom dataclasses import dataclass\nfrom typing import TYPE_CHECKING, Iterable, List, Tuple, Union\n\nfrom chalk.envelope import Envelope\nfrom chalk.monoid import Monoid\nfrom chalk.shapes.arc import ArcSegment, arc_seg, arc_seg_angle\nfrom chalk.shapes.segment import Segment, seg\nfrom chalk.trace import Trace\nfrom chalk.transform import (\n    P2,\n    V2,\n    Affine,\n    Transformable,\n    apply_affine,\n    remove_translation,\n    unit_x,\n    unit_y,\n)\nfrom chalk.types import Diagram, Enveloped, Traceable, TrailLike\n\nif TYPE_CHECKING:\n    from chalk.shapes.path import Path\n\nSegmentLike = Union[Segment, ArcSegment]\n\n\n@dataclass\nclass Located(Enveloped, Traceable, Transformable):\n    trail: Trail\n    location: P2\n\n    def located_segments(self) -> Iterable[Tuple[SegmentLike, P2]]:\n        return zip(self.trail.segments, self.points())\n\n    def points(self) -> Iterable[P2]:\n        return (pt + self.location for pt in self.trail.points())\n\n    def get_envelope(self) -> Envelope:\n        return Envelope.concat(\n            segment.get_envelope().translate_by(location)\n            for segment, location in self.located_segments()\n        )\n\n    def get_trace(self) -> Trace:\n        return Trace.concat(\n            segment.get_trace().translate_by(location)\n            for segment, location in self.located_segments()\n        )\n\n    def stroke(self) -> Diagram:\n        return self.to_path().stroke()\n\n    def apply_transform(self, t: Affine) -> Located:\n        return Located(\n            apply_affine(t, self.trail), apply_affine(t, self.location)\n        )\n\n    def to_path(self) -> Path:\n        from chalk.shapes.path import Path\n\n        return Path([self])\n\n\n@dataclass\nclass Trail(Monoid, Transformable, TrailLike):\n    segments: List[SegmentLike]\n    closed: bool = False\n\n    # Monoid\n    @staticmethod\n    def empty() -> Trail:\n        return Trail([], False)\n\n    def __add__(self, other: Trail) -> Trail:\n        assert not (self.closed or other.closed), \"Cannot add closed trails\"\n        return Trail(self.segments + other.segments, False)\n\n    # Transformable\n    def apply_transform(self, t: Affine) -> Trail:\n        t = remove_translation(t)\n        return Trail(\n            [seg.apply_transform(t) for seg in self.segments], self.closed\n        )\n\n    # Trail-like\n    def to_trail(self) -> Trail:\n        return self\n\n    def close(self) -> Trail:\n        return Trail(self.segments, True)\n\n    def points(self) -> Iterable[P2]:\n        cur = P2(0, 0)\n        pts = [cur]\n        for segment in self.segments:\n            cur += segment.q\n            pts.append(cur)\n        return pts\n\n    def at(self, p: P2) -> Located:\n        return Located(self, p)\n\n    def reverse(self) -> Trail:\n        return Trail(\n            [seg.reverse() for seg in reversed(self.segments)],\n            self.closed,\n        )\n\n    def centered(self) -> Located:\n        return self.at(-sum(self.points(), P2(0, 0)) / len(self.segments))\n\n    # Misc. Constructor\n    @staticmethod\n    def from_offsets(offsets: List[V2], closed: bool = False) -> Trail:\n        return Trail([Segment(off) for off in offsets], closed)\n\n    @staticmethod\n    def hrule(length: float) -> Trail:\n        return seg(length * unit_x)\n\n    @staticmethod\n    def vrule(length: float) -> Trail:\n        return seg(length * unit_y)\n\n    @staticmethod\n    def rectangle(width: float, height: float) -> Trail:\n        t = seg(unit_x * width) + seg(unit_y * height)\n        return (t + t.rotate_by(0.5)).close()\n\n    @staticmethod\n    def rounded_rectangle(width: float, height: float, radius: float) -> Trail:\n        r = radius\n        edge1 = math.sqrt(2 * r * r) / 2\n        edge3 = math.sqrt(r * r - edge1 * edge1)\n        corner = arc_seg(V2(r, r), -(r - edge3))\n        b = [height - r, width - r, height - r, width - r]\n        trail = Trail.concat(\n            (seg(b[i] * unit_y) + corner).rotate_by(i / 4) for i in range(4)\n        ) + seg(0.01 * unit_y)\n        return trail.close()\n\n    @staticmethod\n    def circle(radius: float = 1.0, clockwise: bool = True) -> Trail:\n        sides = 4\n        dangle = -90\n        rotate_by = 1\n        if not clockwise:\n            dangle = 90\n            rotate_by *= -1\n        return (\n            Trail.concat(\n                [\n                    arc_seg_angle(0, dangle).rotate_by(rotate_by * i / sides)\n                    for i in range(sides)\n                ]\n            )\n            .close()\n            .scale(radius)\n        )\n\n    @staticmethod\n    def regular_polygon(sides: int, side_length: float) -> Trail:\n        edge = Trail.hrule(side_length)\n        return Trail.concat(\n            edge.rotate_by(i / sides) for i in range(sides)\n        ).close()\n"
  },
  {
    "path": "chalk/transform.py",
    "content": "import math\nfrom typing import Any\n\nfrom planar import Affine as Affine\nfrom planar import BoundingBox, Point, Polygon, Ray, Vec2, Vec2Array\nfrom typing_extensions import Self\n\n\ndef from_radians(θ: float) -> float:\n    t = (θ / math.pi) * 180\n    return t\n\n\ndef to_radians(θ: float) -> float:\n    t = (θ / 180) * math.pi\n    return t\n\n\ndef remove_translation(aff: Affine) -> Affine:\n    a, b, c, d, e, f = aff[:6]\n    return Affine(a, b, 0, d, e, 0)\n\n\ndef remove_linear(aff: Affine) -> Affine:\n    a, b, c, d, e, f = aff[:6]\n    return Affine(1, 0, c, 0, 1, f)\n\n\ndef transpose_translation(aff: Affine) -> Affine:\n    a, b, c, d, e, f = aff[:6]\n    return Affine(a, d, 0, b, e, 0)\n\n\ndef apply_affine(aff: Affine, x: Any) -> Any:\n    return aff * x\n\n\nclass Transformable:\n    \"\"\"Transformable class.\"\"\"\n\n    def apply_transform(self, t: Affine) -> Self:  # type: ignore[empty-body]\n        pass\n\n    def __rmul__(self, t: Affine) -> Self:\n        return self._app(t)\n\n    def _app(self, t: Affine) -> Self:\n        return self.apply_transform(t)\n\n    def scale(self, α: float) -> Self:\n        return self._app(Affine.scale(Vec2(α, α)))\n\n    def scale_x(self, α: float) -> Self:\n        return self._app(Affine.scale(Vec2(α, 1)))\n\n    def scale_y(self, α: float) -> Self:\n        return self._app(Affine.scale(Vec2(1, α)))\n\n    def rotate(self, θ: float) -> Self:\n        \"Rotate by θ degrees counterclockwise\"\n        return self._app(Affine.rotation(θ))\n\n    def rotate_rad(self, θ: float) -> Self:\n        \"Rotate by θ radians counterclockwise\"\n        return self._app(Affine.rotation(from_radians(θ)))\n\n    def rotate_by(self, turns: float) -> Self:\n        \"Rotate by fractions of a circle (turn).\"\n        θ = 2 * math.pi * turns\n        return self._app(Affine.rotation(from_radians(θ)))\n\n    def reflect_x(self) -> Self:\n        return self._app(Affine.scale(Vec2(-1, +1)))\n\n    def reflect_y(self) -> Self:\n        return self._app(Affine.scale(Vec2(+1, -1)))\n\n    def shear_y(self, λ: float) -> Self:\n        return self._app(Affine(1.0, 0.0, 0.0, λ, 1.0, 0.0))\n\n    def shear_x(self, λ: float) -> Self:\n        return self._app(Affine(1.0, λ, 0.0, 0.0, 1.0, 0.0))\n\n    def translate(self, dx: float, dy: float) -> Self:\n        return self._app(Affine.translation(Vec2(dx, dy)))\n\n    def translate_by(self, vector) -> Self:  # type: ignore\n        return self._app(Affine.translation(vector))\n\n\n# Below here are a collection of hacks to ensure that planar objects\n# behave like the rest of the Chalk library. We do this by monkey\n# patching in methods to Vec2 and by fixing a bug in the Affine\n# transformation. This is not great, but necessary to keep the\n# Object oriented api of Chalk.\n\nVec2._app = lambda x, y: y * x  # type: ignore\nVec2.shear_x = Transformable.shear_x  # type: ignore\nVec2.shear_y = Transformable.shear_y  # type: ignore\nVec2.scale = Transformable.scale  # type: ignore\nVec2.scale_x = Transformable.scale_x  # type: ignore\nVec2.scale_y = Transformable.scale_y  # type: ignore\nVec2.rotate = Transformable.rotate  # type: ignore\nVec2.rotate_by = Transformable.rotate_by  # type: ignore\nVec2.reflect_x = Transformable.reflect_x  # type: ignore\nVec2.reflect_y = Transformable.reflect_y  # type: ignore\nV2 = Vec2\n\n\nVec2.translate = Transformable.translate  # type: ignore\nVec2.translate_by = Transformable.translate_by  # type: ignore\nP2 = Point\n\norigin = P2(0, 0)\nunit_x = V2(1, 0)\nunit_y = V2(0, 1)\n\n\ndef apply_p2_affine(aff: Affine, x: Point) -> Point:\n    y: Point = aff * x\n    return y\n\n\ndef affine(affine: Affine, other: Any) -> Any:\n    sa, sb, sc, sd, se, sf, _, _, _ = affine[:]\n    if isinstance(other, Affine):\n        oa, ob, oc, od, oe, of, _, _, _ = other\n        return tuple.__new__(\n            Affine,\n            (\n                sa * oa + sb * od,\n                sa * ob + sb * oe,\n                sa * oc + sb * of + sc,\n                sd * oa + se * od,\n                sd * ob + se * oe,\n                sd * oc + se * of + sf,\n                0.0,\n                0.0,\n                1.0,\n            ),\n        )\n\n    elif hasattr(other, \"from_points\"):\n        # Point/vector array\n        points = getattr(other, \"points\", other)\n        try:\n            return other.from_points(\n                Point(px * sa + py * sb + sc, px * sd + py * se + sf)\n                for px, py in points\n            )\n        except TypeError:\n            return NotImplemented\n    else:\n        try:\n            vx, vy = other\n        except Exception:\n            return NotImplemented\n        return Vec2(vx * sa + vy * sb + sc, vx * sd + vy * se + sf)\n\n\nAffine.__mul__ = affine  # type: ignore\nAffine.remove_translation = remove_translation  # type: ignore\nAffine.remove_linear = remove_linear  # type: ignore\nAffine.transpose_translation = transpose_translation  # type: ignore\n\n# Explicit rexport\n\n__all__ = [\"BoundingBox\", \"Polygon\", \"Vec2Array\", \"Ray\"]\nAffine\n"
  },
  {
    "path": "chalk/types.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Protocol\n\nimport chalk.transform as tx\nfrom chalk.envelope import Envelope\nfrom chalk.monoid import Monoid\nfrom chalk.style import Stylable, Style\nfrom chalk.trace import Trace\nfrom chalk.transform import P2, V2\n\nif TYPE_CHECKING:\n    from chalk.path import Path\n    from chalk.subdiagram import Name, Subdiagram\n    from chalk.trail import Located, Trail\n    from chalk.visitor import A, DiagramVisitor, ShapeVisitor\n\nIdent = tx.Affine.identity()\n\n\nclass Enveloped(Protocol):\n    def get_envelope(self) -> Envelope: ...\n\n\nclass Traceable(Protocol):\n    def get_trace(self) -> Trace: ...\n\n\nclass Shape(Enveloped, Traceable, Protocol):\n    def accept(self, visitor: ShapeVisitor[A], **kwargs: Any) -> A: ...\n\n\nclass TrailLike(Protocol):\n    def to_trail(self) -> Trail: ...\n\n    def to_path(self, location: P2 = P2(0, 0)) -> Path:\n        return self.at(location).to_path()\n\n    def at(self, location: P2) -> Located:\n        return self.to_trail().at(location)\n\n    def stroke(self) -> Diagram:\n        return self.at(P2(0, 0)).stroke()\n\n\nclass Diagram(Enveloped, Traceable, Stylable, tx.Transformable, Monoid):\n    def apply_transform(self, t: tx.Affine) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def __add__(self: Diagram, other: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def __or__(self, d: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def __truediv__(self, d: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def __floordiv__(self, d: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def juxtapose_snug(  # type: ignore[empty-body]\n        self: Diagram, other: Diagram, direction: V2\n    ) -> Diagram: ...\n\n    def beside_snug(  # type: ignore[empty-body]\n        self: Diagram, other: Diagram, direction: V2\n    ) -> Diagram: ...\n\n    def juxtapose(  # type: ignore[empty-body]\n        self: Diagram, other: Diagram, direction: V2\n    ) -> Diagram: ...\n\n    def atop(self: Diagram, other: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def above(self: Diagram, other: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def beside(  # type: ignore[empty-body]\n        self: Diagram, other: Diagram, direction: V2\n    ) -> Diagram: ...\n\n    def frame(self, extra: float) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def pad(self, extra: float) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def scale_uniform_to_x(self, x: float) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def scale_uniform_to_y(self, y: float) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align(self: Diagram, v: V2) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_t(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_b(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_l(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_r(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_tl(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_tr(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_bl(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def align_br(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def snug(self: Diagram, v: V2) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def center_xy(self: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def get_subdiagram(self, name: Name) -> Optional[Subdiagram]: ...\n\n    def get_sub_map(  # type: ignore[empty-body]\n        self, t: tx.Affine = Ident\n    ) -> Dict[Name, List[Subdiagram]]: ...\n\n    def with_names(  # type: ignore[empty-body]\n        self,\n        names: List[Name],\n        f: Callable[[List[Subdiagram], Diagram], Diagram],\n    ) -> Diagram: ...\n\n    def _style(self, style: Style) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def with_envelope(self, other: Diagram) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def show_origin(self) -> Diagram:  # type: ignore[empty-body]\n        ...\n\n    def show_envelope(  # type: ignore[empty-body]\n        self, phantom: bool = False, angle: int = 45\n    ) -> Diagram: ...\n\n    def compose(  # type: ignore[empty-body]\n        self, envelope: Envelope, other: Optional[Diagram] = None\n    ) -> Diagram: ...\n\n    def to_list(  # type: ignore[empty-body]\n        self, t: tx.Affine = Ident\n    ) -> List[Diagram]: ...\n\n    def accept(  # type: ignore[empty-body]\n        self, visitor: DiagramVisitor[A, Any], args: Any\n    ) -> A: ...\n"
  },
  {
    "path": "chalk/utils.py",
    "content": "\"\"\"\nThe ``chalk.utils`` module is meant to provide various\nutility-oriented functionalities.\n\nImporting:\n\n    ```python\n    # method-1\n    from chalk import utils as U\n\n    # method-2\n    import chalk.utils\n\n    # method-3\n    from chalk.utils import <some_function>\n\n    ```\n\"\"\"\n\nimport os\nimport sys\nimport tempfile\nimport time\nfrom typing import Any, Optional, Tuple, TypeVar, Union\n\nfrom colour import Color\nfrom PIL import Image as PILImage\n\n_HERE = os.path.dirname(__file__)\n\ntry:\n    from loguru import logger\n\n    logger.remove()\n    logger.add(\n        sys.stdout,\n        colorize=True,\n        format=\"<light-red>{time:HH:mm:ss}</light-red> <level>{message}</level>\",  # noqa: E501\n        level=\"INFO\",\n    )  # noqa: E124\n    prnt_success = logger.success\n    prnt_warning = logger.warning\nexcept ImportError:\n    prnt_success = print  # type: ignore\n    prnt_warning = print  # type: ignore\n\nDiagram = TypeVar(\"Diagram\")\n\n\ndef show(filepath: str) -> None:\n    \"\"\"Show image from filepath.\n\n    Args:\n        filepath (str): Filepath of the image.\n                        example: \"examples/output/intro-01-a.png\"\n\n    Usage:\n\n        ```python\n        from chalk.utils import show\n        show(\"examples/output/intro-01.png\")\n        ```\n    \"\"\"\n    PILImage.open(filepath).show()\n\n\ndef imgen(\n    d: Diagram,\n    temporary: bool = True,\n    dirpath: Optional[str] = \"examples/output\",\n    prefix: str = \"trial_\",\n    suffix: str = \"_image.png\",\n    height: int = 64,\n    wait: int = 5,\n    verbose: bool = True,\n) -> None:\n    \"\"\"Render a ``chalk`` diagram and visualize.\n\n    Args:\n        d (Diagram): A chalk diagram object (``chalk.Diagram``).\n        temporary (bool, optional): Whether to use a temporary file or not.\n                Defaults to True.\n        dirpath (Optional[str], optional): Directory to save the temporary\n                file in. If does not exist, creates a temporary directory\n                and destroys it afterwards. Defaults to \"examples/output\".\n        prefix (str, optional): Prefix for the generated image file.\n                Defaults to \"trial_\".\n        suffix (str, optional): Suffix for the generated image file.\n                Defaults to \"_image.png\".\n        height (int, optional): Height of the diagram, rendered as an image.\n                Defaults to 64.\n        wait (int, optional): The time (in seconds) to wait until destroying\n                the temporary image file. Defaults to 5.\n        verbose (bool): Set verbosity. Defaults to True.\n\n    Raises:\n        NotImplementedError: For non temporary file (``temporary=False``),\n                             raises an error, as it has not been\n                             implemented yet.\n\n    Usage:\n\n        ```python\n        from colour import Color\n        from chalk import circle\n        from chalk.utils import imgen\n\n        papaya = Color(\"#ff9700\")\n        d = circle(0.5).fill_color(papaya)\n\n        # Minimal example\n        imgen(d, temporary=True)\n\n        # Temporary file is created in current directory\n        imgen(d, temporary=True, dirpath=None)\n\n        # Folder path must exist; otherwise temporary folder is used\n        imgen(d, temporary=True, dirpath=\"examples/output\")\n\n        # Display and delete the temporary file after 10 seconds\n        imgen(d, temporary=True, wait=10)\n        ```\n    \"\"\"\n    make_tempdir = False\n    dp = None\n    if temporary:\n        if (dirpath is not None) and (not os.path.isdir(dirpath)):\n            make_tempdir = True\n            dp = tempfile.TemporaryDirectory(\n                dir=\".\", prefix=prefix, suffix=suffix\n            )\n            dirpath = dp.name\n        with tempfile.NamedTemporaryFile(\n            dir=dirpath, prefix=prefix, suffix=suffix\n        ) as fp:\n            if verbose:\n                prnt_success(\n                    f\" ✅ 1. Created temporary file: \\n\\t\\t{os.path.relpath(fp.name)}\"  # noqa: E501\n                )  # noqa: E501\n            d.render(fp.name, height=height)  # type: ignore\n            if verbose:\n                prnt_success(\" ✅ 2. Saved rendered image to temporary file.\")\n            fp.seek(0)\n            if verbose:\n                prnt_success(\" ✅ 3. Displaying image from temporary file.\")\n            show(fp.name)\n            time.sleep(wait)\n\n        if verbose:\n            prnt_success(\" ✅ 4. Removed temporary image file!\")\n\n        if make_tempdir and dp:\n            # Cleanup temporary directory\n            dp.cleanup()\n    else:\n        raise NotImplementedError(\n            \"Only temporary file creation + load + display is supported.\"\n        )\n\n\ndef create_sample_diagram(\n    option: Optional[str] = \"a|b\",\n) -> Union[Diagram, Tuple[Diagram, Diagram]]:\n    \"\"\"Creates a sample diagram.\n\n    Args:\n        option (Optional[str], optional): A string denoting what\n            kind of sample diagram(s) to return.\n            💡 Defaults to ``\"a|b\"``.\n\n    Choose ``option`` from for the following.\n\n    Click to expand:\n\n        |   Option    |      Meaning      |     Output      |\n        |:-----------:|:------------------|:---------------:|\n        | ``\"a+b\"``   | ``a.atop(b)``     | Single Diagram  |\n        | ``\"b+a\"``   | ``b.atop(a)``     | Single Diagram  |\n        | ``\"a|b\"``   | ``a.beside(b)``   | Single Diagram  |\n        | ``\"b|a\"``   | ``b.beside(a)``   | Single Diagram  |\n        | ``\"a/b\"``   | ``a.above(b)``    | Single Diagram  |\n        | ``\"b/a\"``   | ``b.above(a)``    | Single Diagram  |\n        | ``\"a//b\"``  | ``a.above2(b)``   | Single Diagram  |\n        | ``\"b//a\"``  | ``b.above2(a)``   | Single Diagram  |\n        | ``\"a,b\"``   | ``(a, b)``        | Two Diagrams    |\n\n    Returns:\n        Diagram: Returns a sample diagram.\n\n    Usage:\n\n        ```python\n        from chalk.utils import create_sample_diagram\n\n        # create a diagram composed of two diagrams: a|b\n        d = create_sample_diagram(option=\"a|b\")\n\n        # create a diagram composed of two diagrams: b|a\n        d = create_sample_diagram(option=\"b|a\")\n\n        # create a diagram composed of two diagrams: a+b\n        d = create_sample_diagram(option=\"a+b\")\n\n        # create a diagram composed of two diagrams: a/b\n        d = create_sample_diagram(option=\"a/b\")\n\n        # create a diagram composed of two diagrams: a//b\n        d = create_sample_diagram(option=\"a//b\")\n\n        # create two diagrams: (a,b)\n        a, b = create_sample_diagram(option=\"a,b\")\n        ```\n    \"\"\"\n    from chalk import circle, square\n\n    papaya = Color(\"#ff9700\")\n    blue = Color(\"#005FDB\")\n    a = circle(0.5).fill_color(papaya)\n    b = square(1).fill_color(blue)\n\n    if option is None:\n        d = a | b  # a|b\n    else:\n        option = \"\".join(option.split())\n        # handle specific cases\n        if option == \"a+b\":\n            d = a + b  # a.atop(b)\n        if option == \"b+a\":\n            d = b + a  # b.atop(a)\n        elif option == \"a|b\":\n            d = a | b  # a.beside(b)\n        elif option == \"a|b\":\n            d = b | a  # b.beside(a)\n        elif option == \"a/b\":\n            d = a / b  # a.above(b)\n        elif option == \"b/a\":\n            d = b / a  # b.above(a)\n        elif option == \"a//b\":\n            d = a // b  # a.above2(b)\n        elif option == \"b//a\":\n            d = b // a  # b.above2(a)\n        elif option == \"a,b\":\n            d = (a, b)  # type: ignore\n        elif option == \"b,a\":\n            d = (b, a)  # type: ignore\n    return d  # type: ignore\n\n\ndef create_double_diagrams() -> Tuple[Diagram, Diagram]:\n    \"\"\"Creates a pair of sample diagrams (a circle and a square).\n\n    Returns:\n        Diagram: Returns a sample diagram.\n    \"\"\"\n    a, b = create_sample_diagram(option=\"a,b\")  # type: ignore\n\n    return (a, b)\n\n\ndef quick_probe(\n    d: Optional[Diagram] = None,\n    dirpath: Optional[str] = None,\n    verbose: bool = True,\n    **kwargs: Any,\n) -> None:\n    \"\"\"Render diagram and generate an image tempfile (``.png``)\n\n    This utility is made to quickly create a sample diagram and display it,\n    without saving any permanent image file on disk. If a diagram is not\n    provided, a sample diagram is generated. If a diagram is provided, it\n    is displayed.\n\n    Args:\n        d (Optional[Diagram], optional): A chalk diagram object\n                (``chalk.Diagram``). Defaults to None.\n        dirpath (Optional[str], optional): Directory to save the temporary\n                file in. For example, you could use \"examples/output\" with\n                respect to the location of running a script.\n                Defaults to None.\n        verbose (bool, optional): Set verbosity. Defaults to True.\n        **kwargs (Any, optional): See the keyword arguments of\n                                  [``imgen()``][chalk.utils.imgen].\n\n    Usage:\n\n        ```python\n        from chalk.utils import quick_probe\n        quick_probe(verbose=True, wait=2)\n        ```\n    \"\"\"\n    # if verbose:\n    #     prnt_warning(f\"{chalk.__name__} version: v{chalk.__version__}\")\n    if d is None:\n        d = create_sample_diagram()  # type: ignore\n    if dirpath is None:\n        dirpath = os.path.join(_HERE, \"../examples/output\")\n    # render diagram and generate an image tempfile (.png)\n    imgen(d, dirpath=dirpath, verbose=verbose, **kwargs)\n\n\nif __name__ == \"__main__\":\n\n    # determine initial directory\n    root = os.path.abspath(os.curdir)\n    # update sys-path\n    sys.path.append(_HERE)\n    os.chdir(_HERE)  # change directory\n    quick_probe(verbose=True)  # generate diagram\n    os.chdir(root)  # switch back to initial directory\n"
  },
  {
    "path": "chalk/visitor.py",
    "content": "from __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Generic, TypeVar\n\nif TYPE_CHECKING:\n    from chalk.ArrowHead import ArrowHead\n    from chalk.core import (\n        ApplyName,\n        ApplyStyle,\n        ApplyTransform,\n        Compose,\n        Empty,\n        Primitive,\n    )\n    from chalk.monoid import Monoid\n    from chalk.Path import Path\n    from chalk.shapes import Image, Latex, Spacer, Text\n\n    A = TypeVar(\"A\", bound=Monoid)\nelse:\n    A = TypeVar(\"A\")\n\nB = TypeVar(\"B\")\n\n\nclass DiagramVisitor(Generic[A, B]):\n    A_type: type[A]\n\n    def visit_primitive(self, diagram: Primitive, arg: B) -> A:\n        \"Primitive defaults to empty\"\n        return self.A_type.empty()\n\n    def visit_empty(self, diagram: Empty, arg: B) -> A:\n        \"Empty defaults to empty\"\n        return self.A_type.empty()\n\n    def visit_compose(self, diagram: Compose, arg: B) -> A:\n        \"Compose defaults to monoid over children\"\n        return self.A_type.concat(\n            [d.accept(self, arg) for d in diagram.diagrams]\n        )\n\n    def visit_apply_transform(self, diagram: ApplyTransform, arg: B) -> A:\n        \"Defaults to pass over\"\n        return diagram.diagram.accept(self, arg)\n\n    def visit_apply_style(self, diagram: ApplyStyle, arg: B) -> A:\n        \"Defaults to pass over\"\n        return diagram.diagram.accept(self, arg)\n\n    def visit_apply_name(self, diagram: ApplyName, arg: B) -> A:\n        \"Defaults to pass over\"\n        return diagram.diagram.accept(self, arg)\n\n\nC = TypeVar(\"C\")\n\n\nclass ShapeVisitor(Generic[C]):\n    def visit_path(self, shape: Path) -> C:\n        raise NotImplementedError\n\n    def visit_latex(self, shape: Latex) -> C:\n        raise NotImplementedError\n\n    def visit_text(self, shape: Text) -> C:\n        raise NotImplementedError\n\n    def visit_spacer(self, shape: Spacer) -> C:\n        raise NotImplementedError\n\n    def visit_arrowhead(self, shape: ArrowHead) -> C:\n        raise NotImplementedError\n\n    def visit_image(self, shape: Image) -> C:\n        raise NotImplementedError\n"
  },
  {
    "path": "doc/crop-images.sh",
    "content": "for img in squares hanoi escher-square-limit lenet logo hilbert koch tensor hex-variation tree lattice; do\n    # convert examples/output/${img}.png -gravity center -crop 200x200+0+0 +repage doc/imgs/${img}.png\n    convert -define png:size=200x200 examples/output/${img}.png  -thumbnail 200x200^ -gravity center -extent 200x200  doc/imgs/${img}.png\ndone\n"
  },
  {
    "path": "docs/about/index.md",
    "content": "# About\n\n{{ config.site_description }}\n\n<sup>Copyright (c) 2022 {{ config.site_author }}.</sup>\n"
  },
  {
    "path": "docs/about/license.md",
    "content": "# License\n\n--8<-- \"LICENSE\"\n"
  },
  {
    "path": "docs/api/alignment.py",
    "content": "# + tags=[\"hide_inp\"]\nfrom chalk.core import BaseDiagram\nfrom chalk import *\n\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n# -\n\n\n# Each diagram has an origin and an envelope.\n# Manipulating the position of the diagram with respect to its origin and envelope allows for precise control of the layout.\n# Note that the Chalk API is immutable and always returns a new ``Diagram`` object.\n\n# ### Diagram.show_origin\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.show_origin)\n# -\n\n#\n\ntriangle(1).show_origin()\n\n# ### Diagram.show_envelope\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.show_envelope)\n# -\n\nrectangle(1, 1).show_envelope()\n\n\ntriangle(1).show_envelope()\n\n\nrectangle(1, 1).show_beside(triangle(1), unit_x)\n\n\n(rectangle(1, 1) | triangle(1)).pad(1.4)\n\n\narc(1, 0, math.pi).show_origin().show_envelope(angle=10)\n\n# ### Diagram.align_*\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.align_t)\n# -\n\n#\n\ntriangle(1).align_t().show_envelope()\n\n\ntriangle(1).align_t().show_beside(rectangle(1, 1).align_b(), unit_x)\n\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.align_r)\n# -\n\n#\n\ntriangle(1).align_r().show_envelope().show_origin()\n\n# ### Diagram.center_xy\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.center_xy)\n# -\n\n#\n\ntriangle(1).center_xy().show_envelope().show_origin()\n\n\n# ### Diagram.pad_*\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.pad)\n# -\n\n\n#\n\ntriangle(1).pad(1.5).show_envelope().show_origin()\n\n\n# ### Diagram.with_envelope\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.with_envelope)\n# -\n\n\n#\n\nfrom colour import Color\n\n(rectangle(1, 1) + triangle(0.5)) | rectangle(1, 1)\n\n\n(rectangle(1, 1) + triangle(0.5)).with_envelope(triangle(0.5)) | rectangle(1, 1).fill_color(Color(\"red\"))\n"
  },
  {
    "path": "docs/api/combinators.py",
    "content": "# + tags=[\"hide_inp\"]\nimport math\nfrom planar import Vec2\nfrom chalk import *\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n# -\n\n\n# Complex diagrams can be created by combining simpler diagrams\n# through placement combinators. These place diagrams above, atop or\n# besides other diagrams. Relative location is determined by the envelope\n# and origins of the diagrams.\n\n# ### beside\n\n# + tags=[\"hide_inp\"]\nhelp(beside)\n# -\n\n#\n\ndiagram = triangle(1).beside(square(1), unit_x)\ndiagram\n\n#\n\ntriangle(1).show_beside(square(1), unit_x)\n\ntriangle(1).show_beside(triangle(1).rotate_by(1 / 6), Vec2.polar(-45))\n\n#\n\ntriangle(1).show_beside(triangle(1).rotate_by(1 / 6), Vec2.polar(-30))\n\n\n# ### above\n\n# + tags=[\"hide_inp\"]\nhelp(above)\n# -\n\n#\n\ndiagram = triangle(1) / square(1)\ndiagram\n\n#\n\ndiagram.show_envelope().show_origin()\n\n\n# ### atop\n\n# + tags=[\"hide_inp\"]\nhelp(atop)\n# -\n\n# Example 1 - Atop at origin\n\ndiagram = square(1) + triangle(1)\ndiagram\n\n#\n\ndiagram.show_envelope().show_origin()\n\n# Example 2 - Align then atop origin\n\ns = square(1).align_r().align_b().show_origin()\nt = triangle(1).align_l().align_t().show_origin()\ns\n\n#\n\nt\n\n#\n\ns + t\n\n\n\n# ### vcat\n\n# + tags=[\"hide_inp\"]\nhelp(vcat)\n# -\n\n#\n\nvcat([triangle(1), square(1), triangle(1)], 0.2)\n\n# ### concat\n\n# + tags=[\"hide_inp\"]\nhelp(concat)\n# -\n\n#\n\nconcat([triangle(1), square(1), triangle(1)])\n\n\n# ### hcat\n\n# + tags=[\"hide_inp\"]\nhelp(hcat)\n# -\n\n#\n\nhcat([triangle(1), square(1), triangle(1)], 0.2)\n\n# ### place_on_path\n\n# + tags=[\"hide_inp\"]\nhelp(place_on_path)\n# -\n\nplace_on_path(\n    [circle(0.25) for _ in range(6)],\n    Trail.regular_polygon(6, 1).to_path(),\n)\n"
  },
  {
    "path": "docs/api/internals.md",
    "content": "---\ntitle: Chalk internals\nsummary: A look inside Chalk\ndate: 2022-08-21\n---\n\nThis document presents information on the internal implementation of the Chalk library.\nThis information should not be needed by the casual user of the library, but it can certainly be useful to developers and also provides a record of the major design decisions.\nWhile much of the functionality of the Chalk library resembles the [Haskell `diagrams` library](https://diagrams.github.io/),\nthere are important distinctions in the implementation due to the differences of the two languages (Python and Haskell).\n\n## Core data types\n\nChalk is an embedded domain specific language (EDSL).\nThe two extremes of language design are shallow and deep EDSLs.\nLoosely put, a shallow EDSL specifies the language through a set of functions,\nwhile a deep EDSL specifies the syntax of the language using a data type (the abstract syntax tree; AST), which is then interpreted using given evaluator functions.\nChalk uses a hybrid approach which defines an intermediate core data structure (in our case, the `Diagram` type) and a suite of functions that operate on this type.\n(For more information on the concepts of deep and shallow EDSLs, see [the paper of Gibbons and Wu from ICFP'14](http://www.cs.ox.ac.uk/jeremy.gibbons/publications/embedding.pdf)).\n\nThe `Diagram` type (implemented as `BaseDiagram` in `chalk/core.py`) can be thought as an [algebraic data type](https://en.wikipedia.org/wiki/Algebraic_data_type) with the following six variants:\n`Empty`, `Primitive`, `Compose`, `ApplyTransform`, `ApplyStyle`, `ApplyName`.\nEach of the variants may hold additional information, as follows:\n\n```python\nclass Empty(BaseDiagram):\n    pass\n\nclass Primitive(BaseDiagram):\n    shape: Shape\n    style: Style\n    transform: Affine\n\nclass Compose(BaseDiagram):\n    envelope: Envelope\n    diagram1: Diagram\n    diagram2: Diagram\n\nclass ApplyTransform(BaseDiagram):\n    transform: Affine\n    diagram: Diagram\n\nclass ApplyStyle(BaseDiagram):\n    style: Style\n    diagram: Diagram\n\nclass ApplyName(BaseDiagram):\n    dname: str\n    diagram: Diagram\n```\n\nInstances of `BaseDiagram` can be constructed and modified using the functions provided by the library.\nFor example,\n```python\ncircle(1)\n```\ngenerates the following AST:\n```python\nPrimitive(shape=Path(...), style=Style(...), transform=Affine(...))\n```\nand\n```python\ncircle(1) | circle(1)\n```\ngenerates the following AST:\n```python\nCompose(\n    envelope=...,\n    diagram1=Primitive(shape=Path(...), style=Style(...), transform=Affine(...)),\n    diagram2=Primitive(shape=Path(...), style=Style(...), transform=Affine(...)),\n)\n```\nA `Diagram` AST can be interpreted in multiple ways, arguably the most obvious being through the rendering functions (see the `chalk/backend` submodule);\nother interpretations are flattening the `Diagram` AST to a list of `Primitive`s or extracting the `Envelope` or `Trace` of a `Diagram`.\nAll these functions are implemented using the [visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern#Python_example), which is the object-oriented correspondent of pattern matching and folding encountered in functional programming.\n(Jeremy Gibbons provides a nice exposition on the relationship between object-oriented design patterns and their functional counterparts in his paper [Design Patterns as Higher-Order Datatype-Generic Programs](http://www.cs.ox.ac.uk/jeremy.gibbons/publications/hodgp.pdf)).\n\n## Support for functional and object-oriented use\n\nInternally the library is implemented in a functional style, through functions that operate on the `Diagram` AST.\nFor example, for composition we have a combinator function `beside` (in `chalk/combinators.py`) that takes two `Diagram`s and returns a new `Diagram` corresponding to their composition.\nThe main benefit of using functions is that the library can be easily split into self-contained submodules, each pertaining to a certain type of functionality (shapes, alignment, transformations, and so on).\nIn contrast, using an object-oriented style would bundle the entire functionality as methods inside the class\nand would only allow to separate the variants (e.g., `Empty`, `Primitive`, `Compose`) across files (see the [\"expression problem\"](https://en.wikipedia.org/wiki/Expression_problem) for the trade-offs between the functional and object-oriented style).\n\nHowever, allowing to write code in an object-oriented style (using dot notation) provides an arguably more convenient and idiomatic style in Python.\nFor this reason, we attach all the functions as methods in the `chalk/core.py` file; for example:\n```python\nclass BaseDiagram:\n    beside = chalk.combinators.beside\n```\nThis implementation allows writing\n```python\ncircle(1).beside(circle(1), unit_x)\n```\nand even more succinctly\n```python\ncircle(1) | circle(1)\n```\nas we define [\"dunder\" methods](https://docs.python.org/3/reference/datamodel.html#special-method-names) for common combinators (`__or__` in this case).\n\nThe challenge of this implementation decision is that it complicates the [type checking](https://mypy.readthedocs.io/en/stable/index.html#) due to circular imports.\nWe solve this problem by introducing a `Diagram` [protocol](https://mypy.readthedocs.io/en/stable/protocols.html) (in `chalk/types.py`), which specifies type signatures for all the methods that are to be implemented later on.\nThe `Diagram` type is used throughout the code for type hinting, for example:\n```python\ndef juxtapose(self: Diagram, other: Diagram, direction: V2) -> Diagram:\n    # We can use `get_envelope` here since the `Diagram` protocol promises\n    # that such a method will be implemented.\n```\nThe concrete implementation of the `Diagram` protocol is provided by the `BaseDiagram` in `chalk/core.py`.\n\n## Trail-like data types\n\nApart from the main `Diagram` data type, there are several other related types (`Trail`, `Located`, `Path`) that encode a trail-like drawing and can be \"lifted\" to a `Diagram` using the `stroke` method.\nThese trail-like structures encode different types of information and, as a consequence, have different combination semantics:\n\n- `Trail` corresponds to a list of vectors (translation-invariant offsets, which can be either straight or bendy—implemented as arcs).\nA `Trail` is `Transformable` (by transforming each of the vectors), but since vectors are translation-invariant, applying `translate` leaves a `Trail` unchanged.\nThe monoid composition corresponds to the list monoid: it _extends_ the first trail with the second one.\nA `Trail` can be closed which means that it is a loop and it will be able to hold a color when filled in.\n- `Located` is a `Trail` paired with a `location` origin point.\nA `Trail` can be turned into `Located` using the `at` method which specifies the origin location.\n`Located` instances do not form a monoid.\n- `Path` is a list of `Located` instances.\nHaving more than one `Located` instance is important, since it allows to easily draw objects with holes in them (such as rings).\nThe monoid composition also corresponds to the list monoid, but the effects is different from `Trail`: it _overlays_ the `Located` subpaths.\n\nBelow we present an example (inspired from the `diagrams` library) that showcases the distinction of the combination semantics (the `concat` function) for the `Diagram`, `Path`, `Trail` types.\n\n```python\nfrom colour import Color\nfrom toolz import iterate, take\nfrom chalk import *\n\nred = Color(\"red\")\nt = Trail.regular_polygon(3, 1)\nt_loc = t.centered()\n\n# Diagram\ndia1 = concat(take(3, iterate(lambda d: d.rotate_by(1 / 9), t_loc.stroke()))).fill_color(red)\n\n# Path\ndia2 = Path.concat(take(3, iterate(lambda d: d.rotate_by(1 / 9), t_loc.to_path()))).stroke().fill_color(red)\n\n# Trail\ndia3 = Trail.concat(take(3, iterate(lambda d: d.rotate_by(1 / 9), t))).stroke().center_xy()\n\nhcat([dia1, dia2, dia3], sep=0.2)\n```\n\n<img src=\"https://user-images.githubusercontent.com/819256/184515950-b3ce4245-19ee-4357-bc3a-f32f993b04ef.png\">\n"
  },
  {
    "path": "docs/api/names.py",
    "content": "# + tags=[\"hide_inp\"]\nfrom colour import Color\nfrom chalk.core import BaseDiagram\nfrom chalk import *\n\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n\nset_svg_height(100)\n# -\n\n# Chalk supports basic methods for complex connected layouts and diagrams.\n# Individual elements can be assigned names, and then be referenced in their subdiagram locations.\n# As we will see, names are particularly useful for connecting different parts of the diagram with arrows.\n# Our implementation follows the API of the Haskell [diagrams](https://diagrams.github.io/doc/manual.html#named-subdiagrams) library,\n# but named nodes are also common in TikZ.\n#\n# ### Diagram.named\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.named)\n# -\n\ndiagram = circle(0.5).named(\"x\") | square(1)\ndiagram\n\n# ### Diagram.get_subdiagram\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.get_subdiagram)\n# -\n\n# A `Subdiagram` is a `Diagram` paired with its enclosing context (a `Transformation` for the moment; but `Style` should also be added at some point).\n# It has the following methods:\n# - `get_envelope`, which returns the corresponding `Envelope`\n# - `get_trace`, which returns the corresponding `Trace`\n# - `get_location`, which returns the local origin of the `Subdiagram`\n# - `boundary_from`, which return the furthest point on the boundary of the `Subdiagram`, starting from the local origin of the `Subdigram` and going in the direction of a given vector.\n\n#\n\ndiagram = circle(0.5).named(\"x\") | square(1)\nsub = diagram.get_subdiagram(\"x\")\ndiagram + circle(0.2).translate_by(sub.get_location())\n\n# ### Diagram.with_names\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.with_names)\n# -\n\nroot = circle(1).named(\"root\")\nleaves = hcat([circle(1).named(c) for c in \"abcde\"], sep=0.5).center()\n\ndef connect(subs, nodes):\n    root, leaf = subs\n    pp = tuple(root.boundary_from(unit_y))\n    pc = tuple(leaf.boundary_from(-unit_y))\n    return nodes + make_path([pp, pc])\n\nnodes = root / vstrut(2) / leaves\n\nfor c in \"abcde\":\n    nodes = nodes.with_names([\"root\", c], connect)\nnodes\n\n# ### Diagram.qualify\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.qualify)\n# -\n\nred = Color(\"red\")\n\ndef attach(subs, dia):\n    sub1, sub2 = subs\n    p1 = tuple(sub1.get_location())\n    p2 = tuple(sub2.get_location())\n    return dia + make_path([p1, p2]).line_color(red)\n\n\ndef squares():\n    s = square(1)\n    return (\n        (s.named(\"NW\") | s.named(\"NE\")) /\n        (s.named(\"SW\") | s.named(\"SE\")))\n\n\ndia = hcat([squares().qualify(str(i)) for i in range(5)], sep=0.5)\npairs = [\n    ((\"0\", \"NE\"), (\"2\", \"SW\")),\n    ((\"1\", \"SE\"), (\"4\", \"NE\")),\n    ((\"3\", \"NW\"), (\"3\", \"SE\")),\n    ((\"0\", \"SE\"), (\"1\", \"NW\")),\n]\n\ndia\n\nfor pair in pairs:\n    dia = dia.with_names(pair, attach)\n\ndia\n\n# ### Diagram.show_labels\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.show_labels)\n# -\n\ndia.show_labels(font_size=0.2)\n\n# ### Diagram.connect\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.connect)\n# -\n\ndiagram = circle(0.5).named(\"x\") | hstrut(1) | square(1).named(\"y\")\ndiagram.connect(\"x\", \"y\")\n\n# ### Diagram.connect_outside\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.connect_outside)\n# -\n\ndiagram = circle(0.5).named(\"x\") | hstrut(1) | square(1).named(\"y\")\ndiagram.connect_outside(\"x\", \"y\")\n"
  },
  {
    "path": "docs/api/rendering.py",
    "content": "# + tags=[\"hide\"]\nimport math\n\nfrom chalk.core import BaseDiagram\nfrom chalk import *\n\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n# -\n\n# Chalk supports three back-ends (Cairo, SVG, TikZ),\n# which allow the created `Diagram`s to be rendered as PNG, SVG, PDF files, respectively.\n# The three corresponding methods for rendering are: `render`, `render_svg`, `render_pdf`;\n# these are documented below.\n#\n# ### Diagram.render\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.render)\n# -\n\ncircle(1).render(\"circle.png\")\nfrom IPython.display import Image\nImage(\"circle.png\")\n\n# ### Diagram.render_svg\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.render_svg)\n# -\n\n# ### Diagram.render_pdf\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.render_pdf)\n# -\n\n# ### ``Diagram``s in IPython notebooks\n\n# When a ``Diagram`` is used in an IPython notebook, it is automatically displayed as an SVG.\n# To adjust the height of the generated image, one can use the `set_svg_height` function:\n\n# + tags=[\"hide_inp\"]\nhelp(set_svg_height)\n# -\n\n# This function is particularly useful for showing tall drawings:\n\nset_svg_height(500)\nvcat([circle(1) for _ in range(5)])\n"
  },
  {
    "path": "docs/api/shapes.py",
    "content": "# + tags=[\"hide\"]\nfrom chalk.core import BaseDiagram\nfrom chalk import *\n\n\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n\n# -\n\n# Elementary diagrams can be created using shapes: polygons, circle-like shapes, text and paths.\n\n# ## Polygons\n\n# ### Triangle\n\n# + tags=[\"hide_inp\"]\nhelp(triangle)\n# -\n\ntriangle(1)\n\n# ### Square and rectangle\n\n# + tags=[\"hide_inp\"]\nhelp(square)\n# -\n\nsquare(1)\n\n# + tags=[\"hide_inp\"]\nhelp(rectangle)\n# -\n\nrectangle(3, 1, 0.0)\n\n# ### Regular polygon\n\n# + tags=[\"hide_inp\"]\nhelp(regular_polygon)\n# -\n\nhcat(\n    [\n        regular_polygon(5, 1),\n        regular_polygon(6, 1),\n        regular_polygon(7, 1),\n    ],\n    sep=0.5,\n)\n\n# ## Circle-like shapes\n\n# ### Circle\n\n# + tags=[\"hide_inp\"]\nhelp(circle)\n# -\n\ncircle(1)\n\n# ### Arc\n\n# Arcs can be specified either using angles (see ``arc``) or points (see ``arc_between``).\n\n# + tags=[\"hide_inp\"]\nhelp(arc)\n# -\n\nquarter = 90\narc(1, 0, quarter)\n\narc(1, 0, quarter) + arc(1, 2 * quarter, 3 * quarter)\n\n# + tags=[\"hide_inp\"]\nhelp(arc_between)\n# -\n\narc_between((0, 0), (1, 0), 1)\n\n# ## Text\n\n# + tags=[\"hide_inp\"]\nhelp(text)\n# -\n\n# Note that unlike other shapes, ``text`` has an empty envelope, so we need to explicitly specify it in order to get a non-empty rendering.\n\ntext(\"hello\", 1).with_envelope(rectangle(2.5, 1))\n\n# ## Paths\n\n# + tags=[\"hide_inp\"]\nhelp(make_path)\n# -\n\nmake_path([(0, 0), (0, 1), (1, 1), (1, 2)])\n"
  },
  {
    "path": "docs/api/style.py",
    "content": "# + tags=[\"hide_inp\"]\nfrom chalk.core import BaseDiagram\nfrom chalk import *\n\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n# -\n\n\n# Diagrams can be styled using standard vector graphic style\n# primitives. Colors use the Python [colour](https://github.com/vaab/colour) library.\n\nfrom colour import Color\nblue = Color(\"blue\")\norange = Color(\"orange\")\n\n# ### Diagram.fill_color\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.fill_color)\n# -\n\n#\n\ntriangle(1).fill_color(blue)\n\n# ### Diagram.fill_opacity\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.fill_opacity)\n# -\n\n#\n\ntriangle(1).fill_color(blue).fill_opacity(0.2)\n\n\n# ### Diagram.line_color\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.line_color)\n# -\n\n#\n\ntriangle(1).line_color(blue)\n\n# ### Diagram.line_width\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.line_width)\n# -\n\n#\n\ntriangle(1).line_width(0.05)\n\n\n# ### Diagram.dashing\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.dashing)\n# -\n\n#\n\ntriangle(1).dashing([0.2, 0.1], 0) \n\n\n# ### Advanced Example\n\n\n# Example: Outer styles override inner styles\n\n(triangle(1).fill_color(orange) | square(2)).fill_color(blue)\n"
  },
  {
    "path": "docs/api/trails.py",
    "content": "# + tags=[\"hide_inp\"]\nimport math\nfrom chalk import *\nfrom planar import Vec2Array\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n# -\n\n# Chalk also includes a ``Trail`` primitive which allows for creating new ``Diagram``s and more complex shapes.\n# ``Trail``s are specified by position-invariant offsets and can be rendered as ``Path``s.\n# See the [Koch](../examples/koch/) example for a use case. \n\n# ### Constructors\n\n# ``Trail``s are a sequence of vectors and can be constructed using the ``from_offsets`` method:\n\ntrail = Trail.from_offsets([V2(1, 0), V2(1, 1), V2(0, 1)])\n\n# ### Converting to ``Diagram``\n\n# In order to render, `Trail`s have to be turned into ``Diagram``s, which can be achieved using the `stroke` method.\n\n# + tags=[\"hide_inp\"]\nhelp(Trail.stroke)\n# -\n\ntrail.stroke()\n\n# ### Transformations\n\n# `Trail`s can be transformed using the usual geometric transformations, which are also applied to the ``Diagram`` object.\n# For example, ``Trail``s can be rotated:\n\n# + tags=[\"hide_inp\"]\nhelp(Trail.rotate_by)\n# -\n\ntrail2 = trail.rotate_by(0.2)\ntrail2.stroke()\n\n# However, since `Trail`s are translation invariant, applying the `translate` method leaves the `Trail` instance unchanged.\n\n# ### Composition\n\n# ``Trail``s form a monoid with addition given by list concatenation and identity given by the empty list.\n# Intuitively, adding two ``Trails`` appends the two sequences of offsets.\n\n# + tags=[\"hide_inp\"]\nhelp(Trail.__add__)\n# -\n\n(trail + trail2).stroke()\n"
  },
  {
    "path": "docs/api/transformations.py",
    "content": "# + tags=[\"hide_inp\"]\nfrom chalk.core import BaseDiagram\nfrom chalk import *\n\ndef help(f):\n    import pydoc\n    from IPython.display import HTML\n    return HTML(pydoc.HTMLDoc().docroutine(f))\n# -\n\n\n# Any Diagram (or other object in Chalk) can be transformed by affine transformation.\n# These produce a new diagram in the standard manner.\n\n# ### scale\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.scale)\n# -\n\n#\n\ntriangle(1) | triangle(1).scale(2)\n\n# Transformations apply to the whole diagram.\n\n(triangle(1) | triangle(1)).scale(2)\n\n\n# ### translate\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.translate)\n# -\n\n#\n\ntriangle(1).translate(1, 1).show_envelope().show_origin()\n\n#\n\ntriangle(1) + triangle(1).translate(1, 1)\n\n# ### shear_x\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.shear_x)\n# -\n\n#\n\nsquare(1).shear_x(0.25).show_envelope()\n\n#\n\nsquare(1) | square(1).shear_x(0.25)\n\n# ### rotate\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.rotate)\n# -\n\n#\n\ntriangle(1) | triangle(1).rotate(90)\n\n# ### rotate_by\n\n# + tags=[\"hide_inp\"]\nhelp(BaseDiagram.rotate_by)\n# -\n\n#\n\ntriangle(1) | triangle(1).rotate_by(0.2)\n"
  },
  {
    "path": "docs/api/utils.md",
    "content": "---\ntitle: chalk.utils\n---\n# **`{{ title }}`**\n\n👍\n\n::: {{ title }}\n"
  },
  {
    "path": "docs/assets/css-js/as-fastapi/chat.js",
    "content": "((window.gitter = {}).chat = {}).options = {\n    room: 'tiangolo/fastapi'\n};\n"
  },
  {
    "path": "docs/assets/css-js/as-fastapi/custom.css",
    "content": "/* source: https: //github.com/tiangolo/fastapi/blob/1876ebc77949a9a254909ec61ea0c09365169ec2/docs/en/docs/css/custom.css */\n\n.termynal-comment {\n    color: #4a968f;\n    font-style: italic;\n    display: block;\n}\n\n.termy [data-termynal] {\n    white-space: pre-wrap;\n}\n\na.external-link::after {\n    /* \\00A0 is a non-breaking space\n        to make the mark be on the same line as the link\n    */\n    content: \"\\00A0[↪]\";\n}\n\na.internal-link::after {\n    /* \\00A0 is a non-breaking space\n        to make the mark be on the same line as the link\n    */\n    content: \"\\00A0↪\";\n}\n\n.shadow {\n    box-shadow: 5px 5px 10px #999;\n}\n\n/* Give space to lower icons so Gitter chat doesn't get on top of them */\n.md-footer-meta {\n    padding-bottom: 2em;\n}\n\n.user-list {\n    display: flex;\n    flex-wrap: wrap;\n    margin-bottom: 2rem;\n}\n\n.user-list-center {\n    justify-content: space-evenly;\n}\n\n.user {\n    margin: 1em;\n    min-width: 7em;\n}\n\n.user .avatar-wrapper {\n    width: 80px;\n    height: 80px;\n    margin: 10px auto;\n    overflow: hidden;\n    border-radius: 50%;\n    position: relative;\n}\n\n.user .avatar-wrapper img {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n}\n\n.user .title {\n    text-align: center;\n}\n\n.user .count {\n    font-size: 80%;\n    text-align: center;\n}\n\na.announce-link:link,\na.announce-link:visited {\n    color: #fff;\n}\n\na.announce-link:hover {\n    color: var(--md-accent-fg-color);\n}\n\n.announce-wrapper {\n    display: flex;\n    justify-content: space-between;\n    flex-wrap: wrap;\n    align-items: center;\n}\n\n.announce-wrapper div.item {\n    display: none;\n}\n\n.announce-wrapper .sponsor-badge {\n    display: block;\n    position: absolute;\n    top: -5px;\n    right: 0;\n    font-size: 0.5rem;\n    color: #999;\n    background-color: #666;\n    border-radius: 10px;\n    padding: 0 10px;\n    z-index: 10;\n}\n\n.announce-wrapper .sponsor-image {\n    display: block;\n    border-radius: 20px;\n}\n\n.announce-wrapper>div {\n    min-height: 40px;\n    display: flex;\n    align-items: center;\n}\n\n.twitter {\n    color: #00acee;\n}\n"
  },
  {
    "path": "docs/assets/css-js/as-fastapi/custom.js",
    "content": "// source: https://github.com/tiangolo/fastapi/blob/1876ebc77949a9a254909ec61ea0c09365169ec2/docs/en/docs/js/custom.js\n\nconst div = document.querySelector('.github-topic-projects')\n\nasync function getDataBatch(page) {\n    const response = await fetch(`https://api.github.com/search/repositories?q=topic:fastapi&per_page=100&page=${page}`, { headers: { Accept: 'application/vnd.github.mercy-preview+json' } })\n    const data = await response.json()\n    return data\n}\n\nasync function getData() {\n    let page = 1\n    let data = []\n    let dataBatch = await getDataBatch(page)\n    data = data.concat(dataBatch.items)\n    const totalCount = dataBatch.total_count\n    while (data.length < totalCount) {\n        page += 1\n        dataBatch = await getDataBatch(page)\n        data = data.concat(dataBatch.items)\n    }\n    return data\n}\n\nfunction setupTermynal() {\n    document.querySelectorAll(\".use-termynal\").forEach(node => {\n        node.style.display = \"block\";\n        new Termynal(node, {\n            lineDelay: 500\n        });\n    });\n    const progressLiteralStart = \"---> 100%\";\n    const promptLiteralStart = \"$ \";\n    const customPromptLiteralStart = \"# \";\n    const termynalActivateClass = \"termy\";\n    let termynals = [];\n\n    function createTermynals() {\n        document\n            .querySelectorAll(`.${termynalActivateClass} .highlight`)\n            .forEach(node => {\n                const text = node.textContent;\n                const lines = text.split(\"\\n\");\n                const useLines = [];\n                let buffer = [];\n                function saveBuffer() {\n                    if (buffer.length) {\n                        let isBlankSpace = true;\n                        buffer.forEach(line => {\n                            if (line) {\n                                isBlankSpace = false;\n                            }\n                        });\n                        dataValue = {};\n                        if (isBlankSpace) {\n                            dataValue[\"delay\"] = 0;\n                        }\n                        if (buffer[buffer.length - 1] === \"\") {\n                            // A last single <br> won't have effect\n                            // so put an additional one\n                            buffer.push(\"\");\n                        }\n                        const bufferValue = buffer.join(\"<br>\");\n                        dataValue[\"value\"] = bufferValue;\n                        useLines.push(dataValue);\n                        buffer = [];\n                    }\n                }\n                for (let line of lines) {\n                    if (line === progressLiteralStart) {\n                        saveBuffer();\n                        useLines.push({\n                            type: \"progress\"\n                        });\n                    } else if (line.startsWith(promptLiteralStart)) {\n                        saveBuffer();\n                        const value = line.replace(promptLiteralStart, \"\").trimEnd();\n                        useLines.push({\n                            type: \"input\",\n                            value: value\n                        });\n                    } else if (line.startsWith(\"// \")) {\n                        saveBuffer();\n                        const value = \"💬 \" + line.replace(\"// \", \"\").trimEnd();\n                        useLines.push({\n                            value: value,\n                            class: \"termynal-comment\",\n                            delay: 0\n                        });\n                    } else if (line.startsWith(customPromptLiteralStart)) {\n                        saveBuffer();\n                        const promptStart = line.indexOf(promptLiteralStart);\n                        if (promptStart === -1) {\n                            console.error(\"Custom prompt found but no end delimiter\", line)\n                        }\n                        const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, \"\")\n                        let value = line.slice(promptStart + promptLiteralStart.length);\n                        useLines.push({\n                            type: \"input\",\n                            value: value,\n                            prompt: prompt\n                        });\n                    } else {\n                        buffer.push(line);\n                    }\n                }\n                saveBuffer();\n                const div = document.createElement(\"div\");\n                node.replaceWith(div);\n                const termynal = new Termynal(div, {\n                    lineData: useLines,\n                    noInit: true,\n                    lineDelay: 500\n                });\n                termynals.push(termynal);\n            });\n    }\n\n    function loadVisibleTermynals() {\n        termynals = termynals.filter(termynal => {\n            if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) {\n                termynal.init();\n                return false;\n            }\n            return true;\n        });\n    }\n    window.addEventListener(\"scroll\", loadVisibleTermynals);\n    createTermynals();\n    loadVisibleTermynals();\n}\n\nfunction shuffle(array) {\n    var currentIndex = array.length, temporaryValue, randomIndex;\n    while (0 !== currentIndex) {\n        randomIndex = Math.floor(Math.random() * currentIndex);\n        currentIndex -= 1;\n        temporaryValue = array[currentIndex];\n        array[currentIndex] = array[randomIndex];\n        array[randomIndex] = temporaryValue;\n    }\n    return array;\n}\n\nasync function showRandomAnnouncement(groupId, timeInterval) {\n    const announceFastAPI = document.getElementById(groupId);\n    if (announceFastAPI) {\n        let children = [].slice.call(announceFastAPI.children);\n        children = shuffle(children)\n        let index = 0\n        const announceRandom = () => {\n            children.forEach((el, i) => { el.style.display = \"none\" });\n            children[index].style.display = \"block\"\n            index = (index + 1) % children.length\n        }\n        announceRandom()\n        setInterval(announceRandom, timeInterval\n        )\n    }\n}\n\nasync function main() {\n    if (div) {\n        data = await getData()\n        div.innerHTML = '<ul></ul>'\n        const ul = document.querySelector('.github-topic-projects ul')\n        data.forEach(v => {\n            if (v.full_name === 'tiangolo/fastapi') {\n                return\n            }\n            const li = document.createElement('li')\n            li.innerHTML = `<a href=\"${v.html_url}\" target=\"_blank\">★ ${v.stargazers_count} - ${v.full_name}</a> by <a href=\"${v.owner.html_url}\" target=\"_blank\">@${v.owner.login}</a>`\n            ul.append(li)\n        })\n    }\n\n    setupTermynal();\n    showRandomAnnouncement('announce-left', 5000)\n    showRandomAnnouncement('announce-right', 10000)\n}\n\nmain()\n"
  },
  {
    "path": "docs/assets/css-js/as-fastapi/termynal.css",
    "content": "/**\n * termynal.js\n *\n * @author Ines Montani <ines@ines.io>\n * @version 0.0.1\n * @license MIT\n */\n\n:root {\n    --color-bg: #252a33;\n    --color-text: #eee;\n    --color-text-subtle: #a2a2a2;\n}\n\n[data-termynal] {\n    width: 750px;\n    max-width: 100%;\n    background: var(--color-bg);\n    color: var(--color-text);\n    /* font-size: 18px; */\n    font-size: 15px;\n    /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */\n    font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;\n    border-radius: 4px;\n    padding: 75px 45px 35px;\n    position: relative;\n    -webkit-box-sizing: border-box;\n    box-sizing: border-box;\n}\n\n[data-termynal]:before {\n    content: '';\n    position: absolute;\n    top: 15px;\n    left: 15px;\n    display: inline-block;\n    width: 15px;\n    height: 15px;\n    border-radius: 50%;\n    /* A little hack to display the window buttons in one pseudo element. */\n    background: #d9515d;\n    -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;\n    box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;\n}\n\n[data-termynal]:after {\n    content: 'bash';\n    position: absolute;\n    color: var(--color-text-subtle);\n    top: 5px;\n    left: 0;\n    width: 100%;\n    text-align: center;\n}\n\na[data-terminal-control] {\n    text-align: right;\n    display: block;\n    color: #aebbff;\n}\n\n[data-ty] {\n    display: block;\n    line-height: 2;\n}\n\n[data-ty]:before {\n    /* Set up defaults and ensure empty lines are displayed. */\n    content: '';\n    display: inline-block;\n    vertical-align: middle;\n}\n\n[data-ty=\"input\"]:before,\n[data-ty-prompt]:before {\n    margin-right: 0.75em;\n    color: var(--color-text-subtle);\n}\n\n[data-ty=\"input\"]:before {\n    content: '$';\n}\n\n[data-ty][data-ty-prompt]:before {\n    content: attr(data-ty-prompt);\n}\n\n[data-ty-cursor]:after {\n    content: attr(data-ty-cursor);\n    font-family: monospace;\n    margin-left: 0.5em;\n    -webkit-animation: blink 1s infinite;\n    animation: blink 1s infinite;\n}\n\n\n/* Cursor animation */\n\n@-webkit-keyframes blink {\n    50% {\n        opacity: 0;\n    }\n}\n\n@keyframes blink {\n    50% {\n        opacity: 0;\n    }\n}\n"
  },
  {
    "path": "docs/assets/css-js/as-fastapi/termynal.js",
    "content": "/**\n * termynal.js\n * A lightweight, modern and extensible animated terminal window, using\n * async/await.\n *\n * @author Ines Montani <ines@ines.io>\n * @version 0.0.1\n * @license MIT\n */\n\n'use strict';\n\n/** Generate a terminal widget. */\nclass Termynal {\n    /**\n     * Construct the widget's settings.\n     * @param {(string|Node)=} container - Query selector or container element.\n     * @param {Object=} options - Custom settings.\n     * @param {string} options.prefix - Prefix to use for data attributes.\n     * @param {number} options.startDelay - Delay before animation, in ms.\n     * @param {number} options.typeDelay - Delay between each typed character, in ms.\n     * @param {number} options.lineDelay - Delay between each line, in ms.\n     * @param {number} options.progressLength - Number of characters displayed as progress bar.\n     * @param {string} options.progressChar – Character to use for progress bar, defaults to █.\n     * @param {number} options.progressPercent - Max percent of progress.\n     * @param {string} options.cursor – Character to use for cursor, defaults to ▋.\n     * @param {Object[]} lineData - Dynamically loaded line data objects.\n     * @param {boolean} options.noInit - Don't initialise the animation.\n     */\n    constructor(container = '#termynal', options = {}) {\n        this.container = (typeof container === 'string') ? document.querySelector(container) : container;\n        this.pfx = `data-${options.prefix || 'ty'}`;\n        this.originalStartDelay = this.startDelay = options.startDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600;\n        this.originalTypeDelay = this.typeDelay = options.typeDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90;\n        this.originalLineDelay = this.lineDelay = options.lineDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500;\n        this.progressLength = options.progressLength\n            || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40;\n        this.progressChar = options.progressChar\n            || this.container.getAttribute(`${this.pfx}-progressChar`) || '█';\n        this.progressPercent = options.progressPercent\n            || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100;\n        this.cursor = options.cursor\n            || this.container.getAttribute(`${this.pfx}-cursor`) || '▋';\n        this.lineData = this.lineDataToElements(options.lineData || []);\n        this.loadLines()\n        if (!options.noInit) this.init()\n    }\n\n    loadLines() {\n        // Load all the lines and create the container so that the size is fixed\n        // Otherwise it would be changing and the user viewport would be constantly\n        // moving as she/he scrolls\n        const finish = this.generateFinish()\n        finish.style.visibility = 'hidden'\n        this.container.appendChild(finish)\n        // Appends dynamically loaded lines to existing line elements.\n        this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData);\n        for (let line of this.lines) {\n            line.style.visibility = 'hidden'\n            this.container.appendChild(line)\n        }\n        const restart = this.generateRestart()\n        restart.style.visibility = 'hidden'\n        this.container.appendChild(restart)\n        this.container.setAttribute('data-termynal', '');\n    }\n\n    /**\n     * Initialise the widget, get lines, clear container and start animation.\n     */\n    init() {\n        /**\n         * Calculates width and height of Termynal container.\n         * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.\n         */\n        const containerStyle = getComputedStyle(this.container);\n        this.container.style.width = containerStyle.width !== '0px' ?\n            containerStyle.width : undefined;\n        this.container.style.minHeight = containerStyle.height !== '0px' ?\n            containerStyle.height : undefined;\n\n        this.container.setAttribute('data-termynal', '');\n        this.container.innerHTML = '';\n        for (let line of this.lines) {\n            line.style.visibility = 'visible'\n        }\n        this.start();\n    }\n\n    /**\n     * Start the animation and rener the lines depending on their data attributes.\n     */\n    async start() {\n        this.addFinish()\n        await this._wait(this.startDelay);\n\n        for (let line of this.lines) {\n            const type = line.getAttribute(this.pfx);\n            const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay;\n\n            if (type == 'input') {\n                line.setAttribute(`${this.pfx}-cursor`, this.cursor);\n                await this.type(line);\n                await this._wait(delay);\n            }\n\n            else if (type == 'progress') {\n                await this.progress(line);\n                await this._wait(delay);\n            }\n\n            else {\n                this.container.appendChild(line);\n                await this._wait(delay);\n            }\n\n            line.removeAttribute(`${this.pfx}-cursor`);\n        }\n        this.addRestart()\n        this.finishElement.style.visibility = 'hidden'\n        this.lineDelay = this.originalLineDelay\n        this.typeDelay = this.originalTypeDelay\n        this.startDelay = this.originalStartDelay\n    }\n\n    generateRestart() {\n        const restart = document.createElement('a')\n        restart.onclick = (e) => {\n            e.preventDefault()\n            this.container.innerHTML = ''\n            this.init()\n        }\n        restart.href = '#'\n        restart.setAttribute('data-terminal-control', '')\n        restart.innerHTML = \"restart ↻\"\n        return restart\n    }\n\n    generateFinish() {\n        const finish = document.createElement('a')\n        finish.onclick = (e) => {\n            e.preventDefault()\n            this.lineDelay = 0\n            this.typeDelay = 0\n            this.startDelay = 0\n        }\n        finish.href = '#'\n        finish.setAttribute('data-terminal-control', '')\n        finish.innerHTML = \"fast →\"\n        this.finishElement = finish\n        return finish\n    }\n\n    addRestart() {\n        const restart = this.generateRestart()\n        this.container.appendChild(restart)\n    }\n\n    addFinish() {\n        const finish = this.generateFinish()\n        this.container.appendChild(finish)\n    }\n\n    /**\n     * Animate a typed line.\n     * @param {Node} line - The line element to render.\n     */\n    async type(line) {\n        const chars = [...line.textContent];\n        line.textContent = '';\n        this.container.appendChild(line);\n\n        for (let char of chars) {\n            const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay;\n            await this._wait(delay);\n            line.textContent += char;\n        }\n    }\n\n    /**\n     * Animate a progress bar.\n     * @param {Node} line - The line element to render.\n     */\n    async progress(line) {\n        const progressLength = line.getAttribute(`${this.pfx}-progressLength`)\n            || this.progressLength;\n        const progressChar = line.getAttribute(`${this.pfx}-progressChar`)\n            || this.progressChar;\n        const chars = progressChar.repeat(progressLength);\n        const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`)\n            || this.progressPercent;\n        line.textContent = '';\n        this.container.appendChild(line);\n\n        for (let i = 1; i < chars.length + 1; i++) {\n            await this._wait(this.typeDelay);\n            const percent = Math.round(i / chars.length * 100);\n            line.textContent = `${chars.slice(0, i)} ${percent}%`;\n            if (percent > progressPercent) {\n                break;\n            }\n        }\n    }\n\n    /**\n     * Helper function for animation delays, called with `await`.\n     * @param {number} time - Timeout, in ms.\n     */\n    _wait(time) {\n        return new Promise(resolve => setTimeout(resolve, time));\n    }\n\n    /**\n     * Converts line data objects into line elements.\n     *\n     * @param {Object[]} lineData - Dynamically loaded lines.\n     * @param {Object} line - Line data object.\n     * @returns {Element[]} - Array of line elements.\n     */\n    lineDataToElements(lineData) {\n        return lineData.map(line => {\n            let div = document.createElement('div');\n            div.innerHTML = `<span ${this._attributes(line)}>${line.value || ''}</span>`;\n\n            return div.firstElementChild;\n        });\n    }\n\n    /**\n     * Helper function for generating attributes string.\n     *\n     * @param {Object} line - Line data object.\n     * @returns {string} - String of attributes.\n     */\n    _attributes(line) {\n        let attrs = '';\n        for (let prop in line) {\n            // Custom add class\n            if (prop === 'class') {\n                attrs += ` class=${line[prop]} `\n                continue\n            }\n            if (prop === 'type') {\n                attrs += `${this.pfx}=\"${line[prop]}\" `\n            } else if (prop !== 'value') {\n                attrs += `${this.pfx}-${prop}=\"${line[prop]}\" `\n            }\n        }\n\n        return attrs;\n    }\n}\n\n/**\n* HTML API: If current script has container(s) specified, initialise Termynal.\n*/\nif (document.currentScript.hasAttribute('data-termynal-container')) {\n    const containers = document.currentScript.getAttribute('data-termynal-container');\n    containers.split('|')\n        .forEach(container => new Termynal(container))\n}\n"
  },
  {
    "path": "docs/assets/css-js/general/css/progressbar.css",
    "content": "/*\n * Source: https://facelessuser.github.io/pymdown-extensions/extensions/progressbar/\n *\n * Note: Look under \"CSS Setup\".\n *\n */\n\n.progress-label {\n  position: absolute;\n  text-align: center;\n  font-weight: 700;\n  width: 100%;\n  margin: 0;\n  line-height: 1.2rem;\n  white-space: nowrap;\n  overflow: hidden;\n}\n\n.progress-bar {\n  height: 1.2rem;\n  float: left;\n  background-color: #2979ff;\n}\n\n.progress {\n  display: block;\n  width: 100%;\n  margin: 0.5rem 0;\n  height: 1.2rem;\n  background-color: #eeeeee;\n  position: relative;\n}\n\n.progress.thin {\n  margin-top: 0.9rem;\n  height: 0.4rem;\n}\n\n.progress.thin .progress-label {\n  margin-top: -0.4rem;\n}\n\n.progress.thin .progress-bar {\n  height: 0.4rem;\n}\n\n/* ColorPalette Source: https://colorbrewer2.org/#type=diverging&scheme=RdYlGn&n=11 */\n\n.progress-0plus .progress-bar {\n  background-color: #a50026;\n}\n\n.progress-10plus .progress-bar {\n  background-color: #d73027;\n}\n\n.progress-20plus .progress-bar {\n  background-color: #f46d43;\n}\n\n.progress-30plus .progress-bar {\n  background-color: #fdae61;\n}\n\n.progress-40plus .progress-bar {\n  background-color: #fee08b;\n}\n\n.progress-50plus .progress-bar {\n  background-color: #ffffbf;\n}\n\n.progress-60plus .progress-bar {\n  background-color: #d9ef8b;\n}\n\n.progress-70plus .progress-bar {\n  background-color: #a6d96a;\n}\n\n.progress-80plus .progress-bar {\n  background-color: #66bd63;\n}\n\n.progress-90plus .progress-bar {\n  background-color: #1a9850;\n}\n\n.progress-100plus .progress-bar {\n  background-color: #006837;\n}\n\n"
  },
  {
    "path": "docs/assets/css-js/general/css/social-color-stryle.css",
    "content": "/* # cspell: disable */\n\n/* Icon Fill Color Styles\n *\n * Source: https://squidfunk.github.io/mkdocs-material/reference/icons-emojis/#with-colors\n *\n */\n\n.devdotto {\n    color: #0A0A0A;\n    src: \"https://simpleicons.org/icons/devdotto.svg\";\n}\n\n.facebook {\n    color: #4267B2;\n    src: \"https://simpleicons.org/icons/facebook.svg\";\n}\n\n.github {\n    color: #181717;\n    src: \"https://simpleicons.org/icons/github.svg\";\n    /* icon: \"https://simpleicons.org/icons/github.svg\"; */\n}\n\n.linkedin {\n    color: #0A66C2;\n    src: \"https://simpleicons.org/icons/linkedin.svg\";\n    /* icon: \"https://simpleicons.org/icons/linkedin.svg\"; */\n}\n\n.medium {\n    color: #00AB6C;\n    src: \"https://simpleicons.org/icons/medium.svg\";\n    /* icon: \"https://simpleicons.org/icons/medium.svg\"; */\n}\n\n.stackoverflow {\n    color: #F58025;\n    src: \"https://simpleicons.org/icons/stackoverflow.svg\";\n    /* icon: \"https://simpleicons.org/icons/stackoverflow.svg\"; */\n}\n\n.twitter {\n    color: #1DA1F2;\n    src: \"https://simpleicons.org/icons/twitter.svg\";\n    /* icon: \"https://simpleicons.org/icons/twitter.svg\"; */\n}\n\n.youtube {\n    color: #FF0000;\n    src: \"https://simpleicons.org/icons/youtube.svg\";\n    /* icon: \"https://simpleicons.org/icons/youtube.svg\"; */\n}\n"
  },
  {
    "path": "docs/assets/css-js/general/js/highlight-config.js",
    "content": "// source: https://squidfunk.github.io/mkdocs-material/reference/code-blocks/#highlight\ndocument$.subscribe(() => {\n    hljs.highlightAll()\n})\n"
  },
  {
    "path": "docs/assets/css-js/general/js/material-extra/extra-uml.js",
    "content": "/*\n * Source: https://github.com/facelessuser/pymdown-extensions/blob/main/docs/src/js/extra-uml.js\n */\n\nimport uml from \"./uml\"\n\n(() => {\n    const onReady = function (fn) {\n        document.addEventListener(\"DOMContentLoaded\", fn)\n        document.addEventListener(\"DOMContentSwitch\", fn)\n    }\n\n    const observer = new MutationObserver(mutations => {\n        mutations.forEach(mutation => {\n            if (mutation.type === \"attributes\") {\n                let scheme = mutation.target.getAttribute(\"data-md-color-scheme\")\n                if (!scheme) {\n                    scheme = \"default\"\n                }\n                localStorage.setItem(\"data-md-color-scheme\", scheme)\n                if (typeof mermaid !== \"undefined\") {\n                    uml(\"mermaid\")\n                }\n            }\n        })\n    })\n\n    onReady(() => {\n        observer.observe(document.querySelector(\"body\"), { attributeFilter: [\"data-md-color-scheme\"] })\n\n        if (typeof mermaid !== \"undefined\") {\n            uml(\"mermaid\")\n        }\n    })\n})()\n"
  },
  {
    "path": "docs/assets/css-js/general/js/material-extra/material-extra-theme.js",
    "content": "/*\n * Source: https://github.com/facelessuser/pymdown-extensions/blob/main/docs/src/js/material-extra-theme.js\n */\n\n(() => {\n\n    const preferToggle = function (e) {\n        if (localStorage.getItem(\"data-md-prefers-color-scheme\") === \"true\") {\n            document.querySelector(\"body\").setAttribute(\"data-md-color-scheme\", (e.matches) ? \"dracula\" : \"default\")\n        }\n    }\n\n    const setupTheme = function (body) {\n        const preferSupported = window.matchMedia(\"(prefers-color-scheme)\").media !== \"not all\"\n        let scheme = localStorage.getItem(\"data-md-color-scheme\")\n        let prefers = localStorage.getItem(\"data-md-prefers-color-scheme\")\n\n        if (!scheme) {\n            scheme = \"dracula\"\n        }\n        if (!prefers) {\n            prefers = \"false\"\n        }\n\n        if (prefers === \"true\" && preferSupported) {\n            scheme = (window.matchMedia(\"(prefers-color-scheme: dark)\").matches) ? \"dracula\" : \"default\"\n        } else {\n            prefers = \"false\"\n        }\n\n        body.setAttribute(\"data-md-prefers-color-scheme\", prefers)\n        body.setAttribute(\"data-md-color-scheme\", scheme)\n\n        if (preferSupported) {\n            const matchListener = window.matchMedia(\"(prefers-color-scheme: dark)\")\n            matchListener.addListener(preferToggle)\n        }\n    }\n\n    const observer = new MutationObserver(mutations => {\n        mutations.forEach(mutation => {\n            if (mutation.type === \"childList\") {\n                if (mutation.addedNodes.length) {\n                    for (let i = 0; i < mutation.addedNodes.length; i++) {\n                        const el = mutation.addedNodes[i]\n\n                        if (el.nodeType === 1 && el.tagName.toLowerCase() === \"body\") {\n                            setupTheme(el)\n                            break\n                        }\n                    }\n                }\n            }\n        })\n    })\n\n    observer.observe(document.querySelector(\"html\"), { childList: true })\n})()\n\nwindow.toggleScheme = function () {\n    const body = document.querySelector(\"body\")\n    const preferSupported = window.matchMedia(\"(prefers-color-scheme)\").media !== \"not all\"\n    let scheme = body.getAttribute(\"data-md-color-scheme\")\n    let prefer = body.getAttribute(\"data-md-prefers-color-scheme\")\n\n    if (preferSupported && scheme === \"default\" && prefer !== \"true\") {\n        prefer = \"true\"\n        scheme = (window.matchMedia(\"(prefers-color-scheme: dark)\").matches) ? \"dracula\" : \"default\"\n    } else if (preferSupported && prefer === \"true\") {\n        prefer = \"false\"\n        scheme = \"dracula\"\n    } else if (scheme === \"dracula\") {\n        prefer = \"false\"\n        scheme = \"default\"\n    } else {\n        prefer = \"false\"\n        scheme = \"dracula\"\n    }\n    localStorage.setItem(\"data-md-prefers-color-scheme\", prefer)\n    body.setAttribute(\"data-md-prefers-color-scheme\", prefer)\n    body.setAttribute(\"data-md-color-scheme\", scheme)\n}\n"
  },
  {
    "path": "docs/assets/css-js/general/js/material-extra/uml.js",
    "content": "/* Notes (as of Mermaid 8.7.0):\n * - Gantt: width is always relative to the parent, if you have a small parent, the chart will be squashed.\n *   Can't help it.\n * - Journey: Suffers from the same issues that Gantt does.\n * - Pie: These charts have no default height or width. Good luck pinning them down to a reasonable size.\n * - Git: The render portion is agnostic to the size of the parent element. But padding of the SVG is relative\n *   to the parent element. You will never find a happy size.\n *\n * Source: https://github.com/facelessuser/pymdown-extensions/blob/main/docs/src/js/uml.js\n *\n */\n\n/**\n * Targets special code or div blocks and converts them to UML.\n * @param {string} className is the name of the class to target.\n * @return {void}\n */\nexport default className => {\n\n    // Custom element to encapsulate Mermaid content.\n    class MermaidDiv extends HTMLElement {\n\n        /**\n        * Creates a special Mermaid div shadow DOM.\n        * Works around issues of shared IDs.\n        * @return {void}\n        */\n        constructor() {\n            super()\n\n            // Create the Shadow DOM and attach style\n            const shadow = this.attachShadow({ mode: \"open\" })\n            const style = document.createElement(\"style\")\n            style.textContent = `\n      :host {\n        display: block;\n        line-height: initial;\n        font-size: 16px;\n      }\n      div.mermaid {\n        margin: 0;\n        overflow: visible;\n      }`\n            shadow.appendChild(style)\n        }\n    }\n\n    if (typeof customElements.get(\"mermaid-div\") === \"undefined\") {\n        customElements.define(\"mermaid-div\", MermaidDiv)\n    }\n\n    const getFromCode = function (parent) {\n        // Handles <pre><code> text extraction.\n        let text = \"\"\n        for (let j = 0; j < parent.childNodes.length; j++) {\n            const subEl = parent.childNodes[j]\n            if (subEl.tagName.toLowerCase() === \"code\") {\n                for (let k = 0; k < subEl.childNodes.length; k++) {\n                    const child = subEl.childNodes[k]\n                    const whitespace = /^\\s*$/\n                    if (child.nodeName === \"#text\" && !(whitespace.test(child.nodeValue))) {\n                        text = child.nodeValue\n                        break\n                    }\n                }\n            }\n        }\n        return text\n    }\n\n    // We use this to determine if we want the dark or light theme.\n    // This is specific for our MkDocs Material environment.\n    // You should load your configs based on your own environment's needs.\n    const defaultConfig = {\n        startOnLoad: false,\n        theme: \"default\",\n        flowchart: {\n            htmlLabels: false\n        },\n        er: {\n            useMaxWidth: false\n        },\n        sequence: {\n            useMaxWidth: false,\n            noteFontWeight: \"14px\",\n            actorFontSize: \"14px\",\n            messageFontSize: \"16px\"\n        }\n    }\n    mermaid.mermaidAPI.globalReset()\n    // Non Material themes should just use \"default\"\n    let scheme = null\n    try {\n        scheme = document.querySelector(\"[data-md-color-scheme]\").getAttribute(\"data-md-color-scheme\")\n    } catch (err) {\n        scheme = \"default\"\n    }\n    const config = (typeof mermaidConfig === \"undefined\") ?\n        defaultConfig :\n        mermaidConfig[scheme] || (mermaidConfig.default || defaultConfig)\n    mermaid.initialize(config)\n\n    // Find all of our Mermaid sources and render them.\n    const blocks = document.querySelectorAll(`pre.${className}, mermaid-div`)\n    const surrogate = document.querySelector(\"html\")\n    for (let i = 0; i < blocks.length; i++) {\n        const block = blocks[i]\n        const parentEl = (block.tagName.toLowerCase() === \"mermaid-div\") ?\n            block.shadowRoot.querySelector(`pre.${className}`) :\n            block\n\n        // Create a temporary element with the typeset and size we desire.\n        // Insert it at the end of our parent to render the SVG.\n        const temp = document.createElement(\"div\")\n        temp.style.visibility = \"hidden\"\n        temp.style.display = \"display\"\n        temp.style.padding = \"0\"\n        temp.style.margin = \"0\"\n        temp.style.lineHeight = \"initial\"\n        temp.style.fontSize = \"16px\"\n        surrogate.appendChild(temp)\n\n        try {\n            mermaid.mermaidAPI.render(\n                `_mermaid_${i}`,\n                getFromCode(parentEl),\n                content => {\n                    const el = document.createElement(\"div\")\n                    el.className = className\n                    el.innerHTML = content\n\n                    // Insert the render where we want it and remove the original text source.\n                    // Mermaid will clean up the temporary element.\n                    const shadow = document.createElement(\"mermaid-div\")\n                    shadow.shadowRoot.appendChild(el)\n                    block.parentNode.insertBefore(shadow, block)\n                    parentEl.style.display = \"none\"\n                    shadow.shadowRoot.appendChild(parentEl)\n                    if (parentEl !== block) {\n                        block.parentNode.removeChild(block)\n                    }\n                },\n                temp\n            )\n        } catch (err) { } // eslint-disable-line no-empty\n\n        if (surrogate.contains(temp)) {\n            surrogate.removeChild(temp)\n        }\n    }\n}\n"
  },
  {
    "path": "docs/assets/css-js/general/js/tables.js",
    "content": "document$.subscribe(function () {\n    var tables = document.querySelectorAll(\"article table\")\n    tables.forEach(function (table) {\n        new Tablesort(table)\n    })\n})\n"
  },
  {
    "path": "docs/assets/css-js/mkdocs-tooltips/css/custom.css",
    "content": ".tooltip {\n  cursor:default;\n}\n\n.icons {\n  margin: 0 auto !important;\n}\n\n.icons.position {\n  width: 240px; /* 3 * (10 + 60 + 10) */\n}\n\n.icons.color {\n  width: 325px; /* 4 * (10 + 60 + 10) + 5 (?) */\n}\n\n@media only screen and (max-width: 400px) {\n  .icons.color {\n    width: 165px; /* 2 * (10 + 60 + 10) + 5 (?) */\n  }\n}\n\n.icons span {\n  line-height: 60px;\n  display: inline-block;\n  cursor: default;\n  font-size: 60px;\n  margin: 10px;\n  padding: 0px;\n  width: 60px;\n  height: 60px;\n  text-align: center;\n  vertical-align: middle;\n}\n\n.md-button {\n  text-align: center;\n  margin: 10px;\n}\n\n.disabled {\n  cursor: not-allowed;\n  pointer-events: none;\n}\n"
  },
  {
    "path": "docs/assets/css-js/pymdownx-extras/css/extra.css",
    "content": "@charset \"UTF-8\";\n\n:root>* {\n\t--md-code-link-bg-color: hsla(0, 0%, 96%, 1);\n\t--md-code-link-accent-bg-color: var(--md-code-link-bg-color);\n\t--md-default-bg-color--trans: rgb(100%, 100%, 100%, 0);\n\t--md-code-special-bg-color: #e8e8e8;\n\t--md-code-alternate-bg-color: var(--md-code-bg-color);\n\t--md-code-hl-punctuation-color: var(--md-code-fg-color);\n\t--md-code-hl-namespace-color: var(--md-code-fg-color);\n\t--md-code-hl-entity-color: var(--md-code-hl-keyword-color);\n\t--md-code-hl-tag-color: var(--md-code-hl-keyword-color);\n\t--md-code-hl-builtin-color: var(--md-code-hl-constant-color);\n\t--md-code-hl-class-color: var(--md-code-hl-function-color);\n\t--md-typeset-a-color: #00bcd4;\n\t--md-progress-stripe: var(--md-default-bg-color--lighter);\n\t--md-progress-100: #00e676;\n\t--md-progress-80: #00e676;\n\t--md-progress-60: #fbc02d;\n\t--md-progress-40: #ff9100;\n\t--md-progress-20: #ff5252;\n\t--md-progress-0: #ff1744;\n\t--md-typeset-kbd-color: #ebebeb;\n\t--md-typeset-kbd-border-color: #b8b8b8;\n\t--md-typeset-kbd-accent-color: hsla(0, 100%, 100%, 1);\n\t--md-default-bg-color--dark: #2b2e3b;\n\t--md-default-bg-color--darker: #252732;\n\t--md-default-bg-color--darkest: #1e2029;\n\t--md-default-bg-color--ultra-dark: #111217\n}\n\n:root>[data-md-color-scheme=slate] {\n\t--md-code-link-bg-color: hsla(232, 15%, 15%, 1);\n\t--md-code-link-accent-bg-color: var(--md-code-link-bg-color);\n\t--md-code-special-bg-color: #2b2d3b;\n\t--md-default-bg-color--trans: hsla(232, 15%, 15%, 0);\n\t--md-typeset-kbd-color: var(--md-default-fg-color--lightest);\n\t--md-typeset-kbd-border-color: #1a1c24;\n\t--md-typeset-kbd-accent-color: var(--md-default-fg-color--lighter)\n}\n\n:root>[data-md-color-scheme=dracula] {\n\t--md-default-fg-color: rgba(248, 248, 242, 0.87);\n\t--md-default-fg-color--light: rgba(248, 248, 242, 0.54);\n\t--md-default-fg-color--lighter: rgba(248, 248, 242, 0.16);\n\t--md-default-fg-color--lightest: rgba(248, 248, 242, 0.07);\n\t--md-default-bg-color: var(--md-default-bg-color--darkest);\n\t--md-default-bg-color--light: rgba(50, 52, 67, 0.7);\n\t--md-default-bg-color--lighter: rgba(50, 52, 67, 0.3);\n\t--md-default-bg-color--lightest: rgba(50, 52, 67, 0.12);\n\t--md-default-bg-color--trans: rgba(50, 52, 67, 0);\n\t--md-text-color: var(--md-default-fg-color);\n\t--md-typeset-color: var(--md-default-fg-color);\n\t--md-admonition-fg-color: var(--md-default-fg-color);\n\t--md-code-fg-color: hsl(60deg, 30%, 96%);\n\t--md-code-bg-color: hsl(231deg, 15%, 18%);\n\t--md-code-inline-bg-color: #323443;\n\t--md-code-hl-operator-color: hsl(326deg, 100%, 74%);\n\t--md-code-hl-punctuation-color: hsl(60deg, 30%, 96%);\n\t--md-code-hl-string-color: hsl(65deg, 92%, 76%);\n\t--md-code-hl-special-color: hsl(265deg, 89%, 78%);\n\t--md-code-hl-number-color: hsl(265deg, 89%, 78%);\n\t--md-code-hl-keyword-color: hsl(326deg, 100%, 74%);\n\t--md-code-hl-name-color: hsl(60deg, 30%, 96%);\n\t--md-code-hl-constant-color: hsl(265deg, 89%, 78%);\n\t--md-code-hl-function-color: hsl(135deg, 94%, 65%);\n\t--md-code-hl-comment-color: hsl(225deg, 27%, 51%);\n\t--md-code-hl-variable-color: hsl(31deg, 100%, 71%);\n\t--md-code-hl-generic-color: hsl(225deg, 27%, 51%);\n\t--md-code-hl-color: hsl(231deg, 25%, 25%);\n\t--md-code-hl-entity-color: hsl(135deg, 94%, 65%);\n\t--md-code-hl-tag-color: hsl(326deg, 100%, 74%);\n\t--md-code-hl-namespace-color: hsl(60deg, 30%, 96%);\n\t--md-code-hl-builtin-color: hsl(191deg, 97%, 77%);\n\t--md-code-hl-class-color: hsl(191deg, 97%, 77%);\n\t--md-code-special-bg-color: #1c1e26;\n\t--md-code-alternate-bg-color: #3d3e49;\n\t--md-code-link-bg-color: #364653;\n\t--md-typeset-a-color: hsl(191deg, 97%, 77%);\n\t--md-typeset-mark-color: #6e7252;\n\t--md-typeset-del-color: #734568;\n\t--md-typeset-ins-color: #36724e;\n\t--md-progress-stripe: var(--md-default-bg-color--lightest);\n\t--md-progress-100: hsl(135deg, 94%, 65%);\n\t--md-progress-80: hsl(135deg, 92%, 79%);\n\t--md-progress-60: hsl(65deg, 92%, 76%);\n\t--md-progress-40: hsl(31deg, 100%, 71%);\n\t--md-progress-20: hsl(326deg, 100%, 74%);\n\t--md-progress-0: hsl(0deg, 100%, 67%);\n\t--md-typeset-kbd-color: var(--md-default-fg-color--lightest);\n\t--md-typeset-kbd-border-color: var(--md-default-bg-color--ultra-dark);\n\t--md-typeset-kbd-accent-color: var(--md-default-fg-color--lighter)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=red],\n[data-md-color-scheme=dracula][data-md-color-primary=red] {\n\t--md-primary-code-bg-color: #47303a;\n\t--md-primary-fg-color: hsla(0deg, 100%, 67%, 1);\n\t--md-primary-fg-color--transparent: hsla(0deg, 100%, 67%, 0.1);\n\t--md-primary-fg-color--light: hsla(0deg, 100%, 72%, 1);\n\t--md-primary-fg-color--dark: hsla(0deg, 100%, 62%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=pink],\n[data-md-color-scheme=dracula][data-md-color-primary=pink] {\n\t--md-primary-code-bg-color: #47354b;\n\t--md-primary-fg-color: hsla(326deg, 100%, 74%, 1);\n\t--md-primary-fg-color--transparent: hsla(326deg, 100%, 74%, 0.1);\n\t--md-primary-fg-color--light: hsla(326deg, 100%, 79%, 1);\n\t--md-primary-fg-color--dark: hsla(326deg, 100%, 69%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=purple],\n[data-md-color-scheme=dracula][data-md-color-primary=purple] {\n\t--md-primary-code-bg-color: #3e3952;\n\t--md-primary-fg-color: hsla(265deg, 89%, 78%, 1);\n\t--md-primary-fg-color--transparent: hsla(265deg, 89%, 78%, 0.1);\n\t--md-primary-fg-color--light: hsla(265deg, 89%, 83%, 1);\n\t--md-primary-fg-color--dark: hsla(265deg, 89%, 73%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-purple],\n[data-md-color-scheme=dracula][data-md-color-primary=deep-purple] {\n\t--md-primary-code-bg-color: #3e3952;\n\t--md-primary-fg-color: hsla(265deg, 89%, 78%, 1);\n\t--md-primary-fg-color--transparent: hsla(265deg, 89%, 78%, 0.1);\n\t--md-primary-fg-color--light: hsla(265deg, 89%, 83%, 1);\n\t--md-primary-fg-color--dark: hsla(265deg, 89%, 73%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=blue],\n[data-md-color-scheme=dracula][data-md-color-primary=blue] {\n\t--md-primary-code-bg-color: #303446;\n\t--md-primary-fg-color: hsla(225deg, 27%, 51%, 1);\n\t--md-primary-fg-color--transparent: hsla(225deg, 27%, 51%, 0.1);\n\t--md-primary-fg-color--light: hsla(225deg, 27%, 56%, 1);\n\t--md-primary-fg-color--dark: hsla(225deg, 27%, 46%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=indigo],\n[data-md-color-scheme=dracula][data-md-color-primary=indigo] {\n\t--md-primary-code-bg-color: #303446;\n\t--md-primary-fg-color: hsla(225deg, 27%, 51%, 1);\n\t--md-primary-fg-color--transparent: hsla(225deg, 27%, 51%, 0.1);\n\t--md-primary-fg-color--light: hsla(225deg, 27%, 56%, 1);\n\t--md-primary-fg-color--dark: hsla(225deg, 27%, 46%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-blue],\n[data-md-color-scheme=dracula][data-md-color-primary=light-blue] {\n\t--md-primary-code-bg-color: #303446;\n\t--md-primary-fg-color: hsla(225deg, 27%, 51%, 1);\n\t--md-primary-fg-color--transparent: hsla(225deg, 27%, 51%, 0.1);\n\t--md-primary-fg-color--light: hsla(225deg, 27%, 56%, 1);\n\t--md-primary-fg-color--dark: hsla(225deg, 27%, 46%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=cyan],\n[data-md-color-scheme=dracula][data-md-color-primary=cyan] {\n\t--md-primary-code-bg-color: #364653;\n\t--md-primary-fg-color: hsla(191deg, 97%, 77%, 1);\n\t--md-primary-fg-color--transparent: hsla(191deg, 97%, 77%, 0.1);\n\t--md-primary-fg-color--light: hsla(191deg, 97%, 82%, 1);\n\t--md-primary-fg-color--dark: hsla(191deg, 97%, 72%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=teal],\n[data-md-color-scheme=dracula][data-md-color-primary=teal] {\n\t--md-primary-code-bg-color: #364653;\n\t--md-primary-fg-color: hsla(191deg, 97%, 77%, 1);\n\t--md-primary-fg-color--transparent: hsla(191deg, 97%, 77%, 0.1);\n\t--md-primary-fg-color--light: hsla(191deg, 97%, 82%, 1);\n\t--md-primary-fg-color--dark: hsla(191deg, 97%, 72%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=green],\n[data-md-color-scheme=dracula][data-md-color-primary=green] {\n\t--md-primary-code-bg-color: #2d4840;\n\t--md-primary-fg-color: hsla(135deg, 94%, 65%, 1);\n\t--md-primary-fg-color--transparent: hsla(135deg, 94%, 65%, 0.1);\n\t--md-primary-fg-color--light: hsla(135deg, 94%, 70%, 1);\n\t--md-primary-fg-color--dark: hsla(135deg, 94%, 60%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-green],\n[data-md-color-scheme=dracula][data-md-color-primary=light-green] {\n\t--md-primary-code-bg-color: #2d4840;\n\t--md-primary-fg-color: hsla(135deg, 94%, 65%, 1);\n\t--md-primary-fg-color--transparent: hsla(135deg, 94%, 65%, 0.1);\n\t--md-primary-fg-color--light: hsla(135deg, 94%, 70%, 1);\n\t--md-primary-fg-color--dark: hsla(135deg, 94%, 60%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=lime],\n[data-md-color-scheme=dracula][data-md-color-primary=lime] {\n\t--md-primary-code-bg-color: #2d4840;\n\t--md-primary-fg-color: hsla(135deg, 94%, 65%, 1);\n\t--md-primary-fg-color--transparent: hsla(135deg, 94%, 65%, 0.1);\n\t--md-primary-fg-color--light: hsla(135deg, 94%, 70%, 1);\n\t--md-primary-fg-color--dark: hsla(135deg, 94%, 60%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=yellow],\n[data-md-color-scheme=dracula][data-md-color-primary=yellow] {\n\t--md-primary-code-bg-color: #454842;\n\t--md-primary-fg-color: hsla(65deg, 92%, 76%, 1);\n\t--md-primary-fg-color--transparent: hsla(65deg, 92%, 76%, 0.1);\n\t--md-primary-fg-color--light: hsla(65deg, 92%, 81%, 1);\n\t--md-primary-fg-color--dark: hsla(65deg, 92%, 71%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=amber],\n[data-md-color-scheme=dracula][data-md-color-primary=amber] {\n\t--md-primary-code-bg-color: #454842;\n\t--md-primary-fg-color: hsla(65deg, 92%, 76%, 1);\n\t--md-primary-fg-color--transparent: hsla(65deg, 92%, 76%, 0.1);\n\t--md-primary-fg-color--light: hsla(65deg, 92%, 81%, 1);\n\t--md-primary-fg-color--dark: hsla(65deg, 92%, 71%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=orange],\n[data-md-color-scheme=dracula][data-md-color-primary=orange] {\n\t--md-primary-code-bg-color: #473e3d;\n\t--md-primary-fg-color: hsla(31deg, 100%, 71%, 1);\n\t--md-primary-fg-color--transparent: hsla(31deg, 100%, 71%, 0.1);\n\t--md-primary-fg-color--light: hsla(31deg, 100%, 76%, 1);\n\t--md-primary-fg-color--dark: hsla(31deg, 100%, 66%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-orange],\n[data-md-color-scheme=dracula][data-md-color-primary=deep-orange] {\n\t--md-primary-code-bg-color: #473e3d;\n\t--md-primary-fg-color: hsla(31deg, 100%, 71%, 1);\n\t--md-primary-fg-color--transparent: hsla(31deg, 100%, 71%, 0.1);\n\t--md-primary-fg-color--light: hsla(31deg, 100%, 76%, 1);\n\t--md-primary-fg-color--dark: hsla(31deg, 100%, 66%, 1);\n\t--md-primary-bg-color: var(--md-default-bg-color);\n\t--md-primary-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=red],\n[data-md-color-scheme=dracula][data-md-color-accent=red] {\n\t--md-code-link-accent-bg-color: #472c36;\n\t--md-accent-fg-color: hsla(0deg, 100%, 62%, 1);\n\t--md-accent-fg-color--transparent: hsla(0deg, 100%, 62%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=pink],\n[data-md-color-scheme=dracula][data-md-color-accent=pink] {\n\t--md-code-link-accent-bg-color: #473149;\n\t--md-accent-fg-color: hsla(326deg, 100%, 69%, 1);\n\t--md-accent-fg-color--transparent: hsla(326deg, 100%, 69%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=purple],\n[data-md-color-scheme=dracula][data-md-color-accent=purple] {\n\t--md-code-link-accent-bg-color: #3c3652;\n\t--md-accent-fg-color: hsla(265deg, 89%, 73%, 1);\n\t--md-accent-fg-color--transparent: hsla(265deg, 89%, 73%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-purple],\n[data-md-color-scheme=dracula][data-md-color-accent=deep-purple] {\n\t--md-code-link-accent-bg-color: #3c3652;\n\t--md-accent-fg-color: hsla(265deg, 89%, 73%, 1);\n\t--md-accent-fg-color--transparent: hsla(265deg, 89%, 73%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=blue],\n[data-md-color-scheme=dracula][data-md-color-accent=blue] {\n\t--md-code-link-accent-bg-color: #2e3243;\n\t--md-accent-fg-color: hsla(225deg, 27%, 46%, 1);\n\t--md-accent-fg-color--transparent: hsla(225deg, 27%, 46%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=indigo],\n[data-md-color-scheme=dracula][data-md-color-accent=indigo] {\n\t--md-code-link-accent-bg-color: #2e3243;\n\t--md-accent-fg-color: hsla(225deg, 27%, 46%, 1);\n\t--md-accent-fg-color--transparent: hsla(225deg, 27%, 46%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-blue],\n[data-md-color-scheme=dracula][data-md-color-accent=light-blue] {\n\t--md-code-link-accent-bg-color: #2e3243;\n\t--md-accent-fg-color: hsla(225deg, 27%, 46%, 1);\n\t--md-accent-fg-color--transparent: hsla(225deg, 27%, 46%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=cyan],\n[data-md-color-scheme=dracula][data-md-color-accent=cyan] {\n\t--md-code-link-accent-bg-color: #324553;\n\t--md-accent-fg-color: hsla(191deg, 97%, 72%, 1);\n\t--md-accent-fg-color--transparent: hsla(191deg, 97%, 72%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=teal],\n[data-md-color-scheme=dracula][data-md-color-accent=teal] {\n\t--md-code-link-accent-bg-color: #324553;\n\t--md-accent-fg-color: hsla(191deg, 97%, 72%, 1);\n\t--md-accent-fg-color--transparent: hsla(191deg, 97%, 72%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=green],\n[data-md-color-scheme=dracula][data-md-color-accent=green] {\n\t--md-code-link-accent-bg-color: #2a483d;\n\t--md-accent-fg-color: hsla(135deg, 94%, 60%, 1);\n\t--md-accent-fg-color--transparent: hsla(135deg, 94%, 60%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=light-green],\n[data-md-color-scheme=dracula][data-md-color-accent=light-green] {\n\t--md-code-link-accent-bg-color: #2a483d;\n\t--md-accent-fg-color: hsla(135deg, 94%, 60%, 1);\n\t--md-accent-fg-color--transparent: hsla(135deg, 94%, 60%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=lime],\n[data-md-color-scheme=dracula][data-md-color-accent=lime] {\n\t--md-code-link-accent-bg-color: #2a483d;\n\t--md-accent-fg-color: hsla(135deg, 94%, 60%, 1);\n\t--md-accent-fg-color--transparent: hsla(135deg, 94%, 60%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=yellow],\n[data-md-color-scheme=dracula][data-md-color-accent=yellow] {\n\t--md-code-link-accent-bg-color: #45483e;\n\t--md-accent-fg-color: hsla(65deg, 92%, 71%, 1);\n\t--md-accent-fg-color--transparent: hsla(65deg, 92%, 71%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=amber],\n[data-md-color-scheme=dracula][data-md-color-accent=amber] {\n\t--md-code-link-accent-bg-color: #45483e;\n\t--md-accent-fg-color: hsla(65deg, 92%, 71%, 1);\n\t--md-accent-fg-color--transparent: hsla(65deg, 92%, 71%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=orange],\n[data-md-color-scheme=dracula][data-md-color-accent=orange] {\n\t--md-code-link-accent-bg-color: #473d39;\n\t--md-accent-fg-color: hsla(31deg, 100%, 66%, 1);\n\t--md-accent-fg-color--transparent: hsla(31deg, 100%, 66%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] :not([data-md-color-scheme])[data-md-color-primary=deep-orange],\n[data-md-color-scheme=dracula][data-md-color-accent=deep-orange] {\n\t--md-code-link-accent-bg-color: #473d39;\n\t--md-accent-fg-color: hsla(31deg, 100%, 66%, 1);\n\t--md-accent-fg-color--transparent: hsla(31deg, 100%, 66%, 0.1);\n\t--md-accent-bg-color: var(--md-default-bg-color);\n\t--md-accent-bg-color--light: var(--md-default-bg-color--light)\n}\n\n:root {\n\t--md-heart: #ff5252;\n\t--md-heart-big: #ff1744\n}\n\n:root [data-md-color-scheme=dracula] {\n\t--md-heart: hsl(326deg, 100%, 74%);\n\t--md-heart-big: hsl(0deg, 100%, 67%)\n}\n\n.md-typeset a.source-link {\n\tposition: relative;\n\ttop: -.6rem;\n\tfloat: right;\n\tcolor: var(--md-default-fg-color--lighter);\n\ttransition: color 125ms\n}\n\n.md-typeset a.source-link:hover {\n\tcolor: var(--md-accent-fg-color)\n}\n\n.md-typeset a.source-link .twemoji {\n\theight: 1.2rem\n}\n\n.md-typeset a.source-link .twemoji svg {\n\twidth: 1.2rem;\n\theight: 1.2rem\n}\n\n.md-typeset div.highlight.md-max-height pre>code {\n\tmax-height: 15rem\n}\n\n.twemoji.heart-throb svg,\n.twemoji.heart-throb-hover svg {\n\tposition: relative;\n\tcolor: var(--md-heart);\n\t-webkit-animation: pulse 1.5s ease infinite;\n\tanimation: pulse 1.5s ease infinite\n}\n\n@-webkit-keyframes pulse {\n\t0% {\n\t\ttransform: scale(1)\n\t}\n\n\t40% {\n\t\tcolor: var(--md-heart-big);\n\t\ttransform: scale(1.3)\n\t}\n\n\t50% {\n\t\ttransform: scale(1.2)\n\t}\n\n\t60% {\n\t\tcolor: var(--md-heart-big);\n\t\ttransform: scale(1.3)\n\t}\n\n\t100% {\n\t\ttransform: scale(1)\n\t}\n}\n\n@keyframes pulse {\n\t0% {\n\t\ttransform: scale(1)\n\t}\n\n\t40% {\n\t\tcolor: var(--md-heart-big);\n\t\ttransform: scale(1.3)\n\t}\n\n\t50% {\n\t\ttransform: scale(1.2)\n\t}\n\n\t60% {\n\t\tcolor: var(--md-heart-big);\n\t\ttransform: scale(1.3)\n\t}\n\n\t100% {\n\t\ttransform: scale(1)\n\t}\n}\n\nfooter.sponsorship {\n\ttext-align: center\n}\n\nfooter.sponsorship hr {\n\tdisplay: inline-block;\n\twidth: 1.6rem;\n\tmargin: 0 .7rem;\n\tvertical-align: middle;\n\tborder-bottom: 2px solid var(--md-default-fg-color--lighter)\n}\n\nfooter.sponsorship:hover hr {\n\tborder-color: var(--md-accent-fg-color)\n}\n\nfooter.sponsorship:not(:hover) .twemoji.heart-throb-hover svg {\n\tcolor: var(--md-default-fg-color--lighter) !important\n}\n\nbody:not([data-md-prefers-color-scheme=true])[data-md-color-scheme=dracula] .md-icon .light-mode,\nbody:not([data-md-prefers-color-scheme=true])[data-md-color-scheme=dracula] .md-icon .system-mode,\nbody:not([data-md-prefers-color-scheme=true])[data-md-color-scheme=dracula] .md-icon .unknown-mode {\n\tdisplay: none\n}\n\nbody:not([data-md-prefers-color-scheme=true])[data-md-color-scheme=default] .md-icon .dark-mode,\nbody:not([data-md-prefers-color-scheme=true])[data-md-color-scheme=default] .md-icon .system-mode,\nbody:not([data-md-prefers-color-scheme=true])[data-md-color-scheme=default] .md-icon .unknown-mode {\n\tdisplay: none\n}\n\nbody:not([data-md-prefers-color-scheme=true]):not([data-md-color-scheme=default]):not([data-md-color-scheme=dracula]) .md-icon .dark-mode,\nbody:not([data-md-prefers-color-scheme=true]):not([data-md-color-scheme=default]):not([data-md-color-scheme=dracula]) .md-icon .light-mode,\nbody:not([data-md-prefers-color-scheme=true]):not([data-md-color-scheme=default]):not([data-md-color-scheme=dracula]) .md-icon .system-mode {\n\tdisplay: none\n}\n\nbody[data-md-prefers-color-scheme=true] .md-icon .dark-mode,\nbody[data-md-prefers-color-scheme=true] .md-icon .light-mode,\nbody[data-md-prefers-color-scheme=true] .md-icon .unknown-mode {\n\tdisplay: none\n}\n\n.md-header-nav__scheme {\n\tz-index: 0\n}\n\n[data-md-toggle=search]:checked~.md-header .md-header-nav__scheme {\n\tdisplay: none\n}\n\n:root>* {\n\t--md-admonition-bg-color: transparent;\n\t--md-admonition-icon--settings: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 15.5A3.5 3.5 0 0 1 8.5 12 3.5 3.5 0 0 1 12 8.5a3.5 3.5 0 0 1 3.5 3.5 3.5 3.5 0 0 1-3.5 3.5m7.43-2.53c.04-.32.07-.64.07-.97 0-.33-.03-.66-.07-1l2.11-1.63c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.31-.61-.22l-2.49 1c-.52-.39-1.06-.73-1.69-.98l-.37-2.65A.506.506 0 0 0 14 2h-4c-.25 0-.46.18-.5.42l-.37 2.65c-.63.25-1.17.59-1.69.98l-2.49-1c-.22-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64L4.57 11c-.04.34-.07.67-.07 1 0 .33.03.65.07.97l-2.11 1.66c-.19.15-.25.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1.01c.52.4 1.06.74 1.69.99l.37 2.65c.04.24.25.42.5.42h4c.25 0 .46-.18.5-.42l.37-2.65c.63-.26 1.17-.59 1.69-.99l2.49 1.01c.22.08.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.66Z\"/></svg>');\n\t--md-admonition-bg-color--settings: rgba(170, 0, 255, 0.1);\n\t--md-admonition-icon-color--settings: #aa00ff;\n\t--md-admonition-icon--new: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"m23 12-2.44-2.78.34-3.68-3.61-.82-1.89-3.18L12 3 8.6 1.54 6.71 4.72l-3.61.81.34 3.68L1 12l2.44 2.78-.34 3.69 3.61.82 1.89 3.18L12 21l3.4 1.46 1.89-3.18 3.61-.82-.34-3.68L23 12m-10 5h-2v-2h2v2m0-4h-2V7h2v6Z\"/></svg>');\n\t--md-admonition-bg-color--new: rgba(255, 214, 0, 0.1);\n\t--md-admonition-icon-color--new: #ffd600;\n\t--md-admonition-bg-color--note: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--note: hsl(51deg, 94%, 73%);\n\t--md-admonition-bg-color--abstract: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--abstract: hsl(191deg, 97%, 77%);\n\t--md-admonition-bg-color--info: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--info: hsl(190deg, 94%, 87%);\n\t--md-admonition-bg-color--tip: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--tip: hsl(161deg, 97%, 77%);\n\t--md-admonition-bg-color--success: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--success: hsl(135deg, 94%, 65%);\n\t--md-admonition-bg-color--question: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--question: hsl(135deg, 92%, 79%);\n\t--md-admonition-bg-color--warning: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--warning: hsl(31deg, 100%, 71%);\n\t--md-admonition-bg-color--failure: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--failure: hsl(0deg, 100%, 59%);\n\t--md-admonition-bg-color--danger: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--danger: hsl(0deg, 100%, 67%);\n\t--md-admonition-bg-color--bug: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--bug: hsl(325deg, 100%, 64%);\n\t--md-admonition-bg-color--example: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--example: hsl(265deg, 89%, 78%);\n\t--md-admonition-bg-color--quote: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--quote: hsl(225deg, 8%, 51%)\n}\n\n:root>[data-md-color-scheme=dracula] {\n\t--md-admonition-icon-color: $drac-dark-yellow\n}\n\n:root>[data-md-color-scheme=dracula] {\n\t--md-admonition-bg-color--settings: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--settings: hsl(326deg, 100%, 74%)\n}\n\n:root>[data-md-color-scheme=dracula] {\n\t--md-admonition-bg-color--new: var(--md-default-bg-color--ultra-dark);\n\t--md-admonition-icon-color--new: hsl(65deg, 92%, 76%)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition,\n[data-md-color-scheme=dracula] .md-typeset details {\n\tborder-color: var(--md-admonition-icon-color--note);\n\tbox-shadow: 0 .2rem .5rem hsla(0deg, 0%, 0%, .3), 0 0 .05rem hsla(0deg, 0%, 0%, .2)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details>summary {\n\tbackground-color: var(--md-admonition-bg-color--note);\n\tborder-color: var(--md-admonition-icon-color--note);\n\tborder-left: .2rem solid\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--note)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.note,\n[data-md-color-scheme=dracula] .md-typeset details.note {\n\tborder-color: var(--md-admonition-icon-color--note)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.note>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.note>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.note>summary {\n\tbackground-color: var(--md-admonition-bg-color--note);\n\tborder-color: var(--md-admonition-icon-color--note)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.note>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.note>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.note>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--note)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.abstract,\n[data-md-color-scheme=dracula] .md-typeset .admonition.summary,\n[data-md-color-scheme=dracula] .md-typeset .admonition.tldr,\n[data-md-color-scheme=dracula] .md-typeset details.abstract,\n[data-md-color-scheme=dracula] .md-typeset details.summary,\n[data-md-color-scheme=dracula] .md-typeset details.tldr {\n\tborder-color: var(--md-admonition-icon-color--abstract)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.abstract>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.summary>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.tldr>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.abstract>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.abstract>summary,\n[data-md-color-scheme=dracula] .md-typeset details.summary>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.summary>summary,\n[data-md-color-scheme=dracula] .md-typeset details.tldr>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.tldr>summary {\n\tbackground-color: var(--md-admonition-bg-color--abstract);\n\tborder-color: var(--md-admonition-icon-color--abstract)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.abstract>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.summary>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.tldr>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.abstract>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.abstract>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.summary>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.summary>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.tldr>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.tldr>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--abstract)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.info,\n[data-md-color-scheme=dracula] .md-typeset .admonition.todo,\n[data-md-color-scheme=dracula] .md-typeset details.info,\n[data-md-color-scheme=dracula] .md-typeset details.todo {\n\tborder-color: var(--md-admonition-icon-color--info)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.info>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.todo>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.info>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.info>summary,\n[data-md-color-scheme=dracula] .md-typeset details.todo>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.todo>summary {\n\tbackground-color: var(--md-admonition-bg-color--info);\n\tborder-color: var(--md-admonition-icon-color--info)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.info>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.todo>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.info>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.info>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.todo>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.todo>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--info)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.hint,\n[data-md-color-scheme=dracula] .md-typeset .admonition.important,\n[data-md-color-scheme=dracula] .md-typeset .admonition.tip,\n[data-md-color-scheme=dracula] .md-typeset details.hint,\n[data-md-color-scheme=dracula] .md-typeset details.important,\n[data-md-color-scheme=dracula] .md-typeset details.tip {\n\tborder-color: var(--md-admonition-icon-color--tip)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.hint>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.important>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.tip>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.hint>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.hint>summary,\n[data-md-color-scheme=dracula] .md-typeset details.important>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.important>summary,\n[data-md-color-scheme=dracula] .md-typeset details.tip>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.tip>summary {\n\tbackground-color: var(--md-admonition-bg-color--tip);\n\tborder-color: var(--md-admonition-icon-color--tip)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.hint>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.important>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.tip>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.hint>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.hint>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.important>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.important>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.tip>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.tip>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--tip)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.check,\n[data-md-color-scheme=dracula] .md-typeset .admonition.done,\n[data-md-color-scheme=dracula] .md-typeset .admonition.success,\n[data-md-color-scheme=dracula] .md-typeset details.check,\n[data-md-color-scheme=dracula] .md-typeset details.done,\n[data-md-color-scheme=dracula] .md-typeset details.success {\n\tborder-color: var(--md-admonition-icon-color--success)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.check>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.done>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.success>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.check>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.check>summary,\n[data-md-color-scheme=dracula] .md-typeset details.done>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.done>summary,\n[data-md-color-scheme=dracula] .md-typeset details.success>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.success>summary {\n\tbackground-color: var(--md-admonition-bg-color--success);\n\tborder-color: var(--md-admonition-icon-color--success)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.check>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.done>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.success>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.check>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.check>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.done>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.done>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.success>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.success>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--success)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.faq,\n[data-md-color-scheme=dracula] .md-typeset .admonition.help,\n[data-md-color-scheme=dracula] .md-typeset .admonition.question,\n[data-md-color-scheme=dracula] .md-typeset details.faq,\n[data-md-color-scheme=dracula] .md-typeset details.help,\n[data-md-color-scheme=dracula] .md-typeset details.question {\n\tborder-color: var(--md-admonition-icon-color--question)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.faq>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.help>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.question>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.faq>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.faq>summary,\n[data-md-color-scheme=dracula] .md-typeset details.help>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.help>summary,\n[data-md-color-scheme=dracula] .md-typeset details.question>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.question>summary {\n\tbackground-color: var(--md-admonition-bg-color--question);\n\tborder-color: var(--md-admonition-icon-color--question)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.faq>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.help>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.question>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.faq>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.faq>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.help>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.help>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.question>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.question>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--question)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.attention,\n[data-md-color-scheme=dracula] .md-typeset .admonition.caution,\n[data-md-color-scheme=dracula] .md-typeset .admonition.warning,\n[data-md-color-scheme=dracula] .md-typeset details.attention,\n[data-md-color-scheme=dracula] .md-typeset details.caution,\n[data-md-color-scheme=dracula] .md-typeset details.warning {\n\tborder-color: var(--md-admonition-icon-color--warning)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.attention>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.caution>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.warning>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.attention>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.attention>summary,\n[data-md-color-scheme=dracula] .md-typeset details.caution>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.caution>summary,\n[data-md-color-scheme=dracula] .md-typeset details.warning>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.warning>summary {\n\tbackground-color: var(--md-admonition-bg-color--warning);\n\tborder-color: var(--md-admonition-icon-color--warning)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.attention>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.caution>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.warning>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.attention>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.attention>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.caution>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.caution>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.warning>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.warning>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--warning)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.fail,\n[data-md-color-scheme=dracula] .md-typeset .admonition.failure,\n[data-md-color-scheme=dracula] .md-typeset .admonition.missing,\n[data-md-color-scheme=dracula] .md-typeset details.fail,\n[data-md-color-scheme=dracula] .md-typeset details.failure,\n[data-md-color-scheme=dracula] .md-typeset details.missing {\n\tborder-color: var(--md-admonition-icon-color--failure)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.fail>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.failure>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.missing>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.fail>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.fail>summary,\n[data-md-color-scheme=dracula] .md-typeset details.failure>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.failure>summary,\n[data-md-color-scheme=dracula] .md-typeset details.missing>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.missing>summary {\n\tbackground-color: var(--md-admonition-bg-color--failure);\n\tborder-color: var(--md-admonition-icon-color--failure)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.fail>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.failure>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.missing>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.fail>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.fail>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.failure>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.failure>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.missing>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.missing>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--failure)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.danger,\n[data-md-color-scheme=dracula] .md-typeset .admonition.error,\n[data-md-color-scheme=dracula] .md-typeset details.danger,\n[data-md-color-scheme=dracula] .md-typeset details.error {\n\tborder-color: var(--md-admonition-icon-color--danger)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.danger>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.error>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.danger>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.danger>summary,\n[data-md-color-scheme=dracula] .md-typeset details.error>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.error>summary {\n\tbackground-color: var(--md-admonition-bg-color--danger);\n\tborder-color: var(--md-admonition-icon-color--danger)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.danger>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.error>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.danger>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.danger>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.error>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.error>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--danger)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.bug,\n[data-md-color-scheme=dracula] .md-typeset details.bug {\n\tborder-color: var(--md-admonition-icon-color--bug)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.bug>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.bug>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.bug>summary {\n\tbackground-color: var(--md-admonition-bg-color--bug);\n\tborder-color: var(--md-admonition-icon-color--bug)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.bug>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.bug>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.bug>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--bug)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.example,\n[data-md-color-scheme=dracula] .md-typeset details.example {\n\tborder-color: var(--md-admonition-icon-color--example)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.example>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.example>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.example>summary {\n\tbackground-color: var(--md-admonition-bg-color--example);\n\tborder-color: var(--md-admonition-icon-color--example)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.example>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.example>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.example>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--example)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.cite,\n[data-md-color-scheme=dracula] .md-typeset .admonition.quote,\n[data-md-color-scheme=dracula] .md-typeset details.cite,\n[data-md-color-scheme=dracula] .md-typeset details.quote {\n\tborder-color: var(--md-admonition-icon-color--quote)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.cite>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset .admonition.quote>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.cite>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.cite>summary,\n[data-md-color-scheme=dracula] .md-typeset details.quote>.admonition-title,\n[data-md-color-scheme=dracula] .md-typeset details.quote>summary {\n\tbackground-color: var(--md-admonition-bg-color--quote);\n\tborder-color: var(--md-admonition-icon-color--quote)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .admonition.cite>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset .admonition.quote>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.cite>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.cite>summary::before,\n[data-md-color-scheme=dracula] .md-typeset details.quote>.admonition-title::before,\n[data-md-color-scheme=dracula] .md-typeset details.quote>summary::before {\n\tbackground-color: var(--md-admonition-icon-color--quote)\n}\n\n.md-typeset .admonition.config,\n.md-typeset .admonition.settings,\n.md-typeset details.config,\n.md-typeset details.settings {\n\tborder-color: var(--md-admonition-icon-color--settings)\n}\n\n.md-typeset .admonition.config>.admonition-title,\n.md-typeset .admonition.settings>.admonition-title,\n.md-typeset details.config>.admonition-title,\n.md-typeset details.config>summary,\n.md-typeset details.settings>.admonition-title,\n.md-typeset details.settings>summary {\n\tbackground-color: var(--md-admonition-bg-color--settings);\n\tborder-color: var(--md-admonition-icon-color--settings)\n}\n\n.md-typeset .admonition.config>.admonition-title::before,\n.md-typeset .admonition.settings>.admonition-title::before,\n.md-typeset details.config>.admonition-title::before,\n.md-typeset details.config>summary::before,\n.md-typeset details.settings>.admonition-title::before,\n.md-typeset details.settings>summary::before {\n\twidth: 1rem;\n\theight: 1rem;\n\tbackground-color: var(--md-admonition-icon-color--settings);\n\tbackground-size: 1rem;\n\t-webkit-mask-image: var(--md-admonition-icon--settings);\n\tmask-image: var(--md-admonition-icon--settings);\n\tcontent: \" \"\n}\n\n.md-typeset .admonition.new,\n.md-typeset details.new {\n\tborder-color: var(--md-admonition-icon-color--new)\n}\n\n.md-typeset .admonition.new>.admonition-title,\n.md-typeset details.new>.admonition-title,\n.md-typeset details.new>summary {\n\tbackground-color: var(--md-admonition-bg-color--new);\n\tborder-color: var(--md-admonition-icon-color--new)\n}\n\n.md-typeset .admonition.new>.admonition-title::before,\n.md-typeset details.new>.admonition-title::before,\n.md-typeset details.new>summary::before {\n\twidth: 1rem;\n\theight: 1rem;\n\tbackground-color: var(--md-admonition-icon-color--new);\n\tbackground-size: 1rem;\n\t-webkit-mask-image: var(--md-admonition-icon--new);\n\tmask-image: var(--md-admonition-icon--new);\n\tcontent: \" \"\n}\n\nmjx-container[display=true] {\n\tfont-size: 120% !important\n}\n\nmjx-container:not([display]) {\n\tfont-size: 100% !important\n}\n\n[data-md-color-scheme=dracula] .CtxtMenu_InfoContent pre,\n[data-md-color-scheme=dracula] .CtxtMenu_InfoSignature input,\n[data-md-color-scheme=slate] .CtxtMenu_InfoContent pre,\n[data-md-color-scheme=slate] .CtxtMenu_InfoSignature input {\n\tcolor: #000\n}\n\n[data-md-color-scheme=dracula] .CtxtMenu_Info,\n[data-md-color-scheme=dracula] .CtxtMenu_Menu,\n[data-md-color-scheme=slate] .CtxtMenu_Info,\n[data-md-color-scheme=slate] .CtxtMenu_Menu {\n\tbox-shadow: 0 10px 20px rgba(0, 0, 0, .5)\n}\n\n.md-typeset .arithmatex {\n\toverflow-x: auto !important;\n\toverflow-y: hidden !important\n}\n\n.katex-display .katex-html {\n\tdisplay: flex !important;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\talign-items: baseline;\n\tjustify-content: space-between\n}\n\n.katex-display .katex-html .base {\n\tdisplay: inline !important\n}\n\n.katex-display .katex-html .tag {\n\tposition: relative !important;\n\tdisplay: inline !important;\n\tmargin-left: var(--margin-small)\n}\n\n.md-typeset del.critic,\n.md-typeset ins.critic,\n.md-typeset mark.critic {\n\tpadding: 0 .25em;\n\tcolor: unset;\n\tbox-shadow: none\n}\n\n.md-typeset .critic.break {\n\tmargin: 0\n}\n\n.md-typeset details {\n\toverflow: hidden\n}\n\n.md-typeset details>summary:focus {\n\toutline-style: none\n}\n\n.highlight .kc {\n\tcolor: var(--md-code-hl-constant-color)\n}\n\n.highlight .nc,\n.highlight .ne {\n\tcolor: var(--md-code-hl-class-color)\n}\n\n.highlight .mb {\n\tcolor: var(--md-code-hl-number-color)\n}\n\n.highlight .bp,\n.highlight .nb {\n\tcolor: var(--md-code-hl-builtin-color)\n}\n\n.highlight .nn {\n\tcolor: var(--md-code-hl-namespace-color)\n}\n\n.highlight .na,\n.highlight .nd,\n.highlight .ni {\n\tcolor: var(--md-code-hl-entity-color)\n}\n\n.highlight .nl,\n.highlight .nt {\n\tcolor: var(--md-code-hl-tag-color)\n}\n\n.md-typeset :not(pre)>code {\n\tmargin: 0;\n\tpadding: 0 .2941176471em;\n\tcolor: var(--md-code-fg-color);\n\tbackground-color: var(--md-code-inline-bg-color);\n\tborder-radius: .1rem;\n\tbox-shadow: none\n}\n\n.md-typeset a>code {\n\tcolor: inherit !important;\n\tbackground-color: var(--md-code-link-bg-color) !important;\n\ttransition: color 125ms;\n\ttransition: background-color 125ms\n}\n\n.md-typeset a>code * {\n\tcolor: var(--md-typeset-a-color) !important\n}\n\n.md-typeset a>code:hover {\n\tbackground-color: var(--md-code-link-accent-bg-color) !important\n}\n\n.md-typeset a>code:hover * {\n\tcolor: var(--md-accent-fg-color) !important\n}\n\n.md-typeset pre>code {\n\toutline: 0\n}\n\n.md-typeset td code {\n\tword-break: normal\n}\n\n.md-typeset .highlight {\n\t-moz-tab-size: 8;\n\t-o-tab-size: 8;\n\ttab-size: 8\n}\n\n.md-typeset .highlight [data-linenos].special::before {\n\tbackground-color: var(--md-code-special-bg-color)\n}\n\n.md-typeset .highlighttable .linenodiv .special {\n\tmargin-right: -.5882352941em;\n\tmargin-left: -1.1764705882em;\n\tpadding-right: .5882352941em;\n\tpadding-left: 1.1764705882em;\n\tbackground-color: var(--md-code-special-bg-color)\n}\n\n.md-typeset .highlight span.filename {\n\tposition: relative;\n\tdisplay: block;\n\tmargin-top: 1em;\n\tpadding: .5em 1.1764705882em .5em 2.9411764706em;\n\tfont-weight: 700;\n\tfont-size: .68rem;\n\tbackground-color: var(--md-default-bg-color--ultra-dark);\n\tborder-top-left-radius: .1rem;\n\tborder-top-right-radius: .1rem\n}\n\n.md-typeset .highlight span.filename+pre {\n\tmargin-top: 0\n}\n\n.md-typeset .highlight span.filename+pre code {\n\tborder-top-left-radius: 0;\n\tborder-top-right-radius: 0\n}\n\n.md-typeset .highlight span.filename::before {\n\tposition: absolute;\n\tleft: .8823529412em;\n\twidth: 1.4705882353em;\n\theight: 1.4705882353em;\n\tbackground-color: var(--md-default-fg-color);\n\t-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M20 19V7H4v12h16m0-16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16m-7 14v-2h5v2h-5m-3.42-4L5.57 9H8.4l3.3 3.3c.39.39.39 1.03 0 1.42L8.42 17H5.59l3.99-4Z\"/></svg>');\n\tmask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M20 19V7H4v12h16m0-16a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16m-7 14v-2h5v2h-5m-3.42-4L5.57 9H8.4l3.3 3.3c.39.39.39 1.03 0 1.42L8.42 17H5.59l3.99-4Z\"/></svg>');\n\t-webkit-mask-repeat: no-repeat;\n\tmask-repeat: no-repeat;\n\t-webkit-mask-size: contain;\n\tmask-size: contain;\n\tcontent: \"\"\n}\n\n.md-typeset .keys .key-power::before {\n\tpadding-right: .4em;\n\tcontent: \"⏻\"\n}\n\n.md-typeset .keys .key-fingerprint::before {\n\tpadding-right: .4em;\n\tcontent: \"☝\"\n}\n\n:root>* {\n\t--magiclink-email-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M20 4H4c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m-3 13H7v-2h10m0-2H7v-2h10m3-2h-3V6h3\"/></svg>');\n\t--magiclink-github-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.87 1.52 2.34 1.07 2.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92 0-1.11.38-2 1.03-2.71-.1-.25-.45-1.29.1-2.64 0 0 .84-.27 2.75 1.02.79-.22 1.65-.33 2.5-.33.85 0 1.71.11 2.5.33 1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.39.1 2.64.65.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91.36.31.69.92.69 1.85V21c0 .27.16.59.67.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2Z\"/></svg>');\n\t--magiclink-bitbucket-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M2.65 3C2.3 3 2 3.3 2 3.65v.12l2.73 16.5c.07.42.43.73.85.73h13.05c.31 0 .59-.22.64-.54L22 3.77a.643.643 0 0 0-.54-.73c-.03-.01-.07-.01-.11-.01L2.65 3M14.1 14.95H9.94L8.81 9.07h6.3l-1.01 5.88Z\"/></svg>');\n\t--magiclink-gitlab-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"m21.94 13.11-1.05-3.22c0-.03-.01-.06-.02-.09l-2.11-6.48a.859.859 0 0 0-.8-.57c-.36 0-.68.25-.79.58l-2 6.17H8.84L6.83 3.33a.851.851 0 0 0-.79-.58c-.37 0-.69.25-.8.58L3.13 9.82v.01l-1.07 3.28c-.16.5.01 1.04.44 1.34l9.22 6.71c.17.12.39.12.56-.01l9.22-6.7c.43-.3.6-.84.44-1.34M8.15 10.45l2.57 7.91-6.17-7.91m8.73 7.92 2.47-7.59.1-.33h3.61l-5.59 7.16m4.1-13.67 1.81 5.56h-3.62m-1.3.95-1.79 5.51L12 19.24l-2.86-8.79M6.03 3.94 7.84 9.5H4.23m-1.18 4.19c-.09-.07-.13-.19-.09-.29l.79-2.43 5.82 7.45m11.38-4.73-6.51 4.73.02-.03 5.79-7.42.79 2.43c.04.1 0 .22-.09.29\"/></svg>');\n\t--magiclink-commit-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill-rule=\"evenodd\" d=\"M15.5 11.75a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0zm1.444-.75a5.001 5.001 0 0 0-9.888 0H2.75a.75.75 0 1 0 0 1.5h4.306a5.001 5.001 0 0 0 9.888 0h4.306a.75.75 0 1 0 0-1.5h-4.306z\"/></svg>');\n\t--magiclink-compare-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M12.5 6.75a.75.75 0 0 0-1.5 0V9H8.75a.75.75 0 0 0 0 1.5H11v2.25a.75.75 0 0 0 1.5 0V10.5h2.25a.75.75 0 0 0 0-1.5H12.5V6.75zM8.75 16a.75.75 0 0 0 0 1.5h6a.75.75 0 0 0 0-1.5h-6z\"/><path fill-rule=\"evenodd\" d=\"M5 1a2 2 0 0 0-2 2v18a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V7.018a2 2 0 0 0-.586-1.414l-4.018-4.018A2 2 0 0 0 14.982 1H5zm-.5 2a.5.5 0 0 1 .5-.5h9.982a.5.5 0 0 1 .354.146l4.018 4.018a.5.5 0 0 1 .146.354V21a.5.5 0 0 1-.5.5H5a.5.5 0 0 1-.5-.5V3z\"/></svg>');\n\t--magiclink-pull-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill-rule=\"evenodd\" d=\"M4.75 3a1.75 1.75 0 1 0 0 3.5 1.75 1.75 0 0 0 0-3.5zM1.5 4.75a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0zM4.75 17.5a1.75 1.75 0 1 0 0 3.5 1.75 1.75 0 0 0 0-3.5zM1.5 19.25a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0zm17.75-1.75a1.75 1.75 0 1 0 0 3.5 1.75 1.75 0 0 0 0-3.5zM16 19.25a3.25 3.25 0 1 1 6.5 0 3.25 3.25 0 0 1-6.5 0z\"/><path fill-rule=\"evenodd\" d=\"M4.75 7.25A.75.75 0 0 1 5.5 8v8A.75.75 0 0 1 4 16V8a.75.75 0 0 1 .75-.75zm8.655-5.53a.75.75 0 0 1 0 1.06L12.185 4h4.065A3.75 3.75 0 0 1 20 7.75v8.75a.75.75 0 0 1-1.5 0V7.75a2.25 2.25 0 0 0-2.25-2.25h-4.064l1.22 1.22a.75.75 0 0 1-1.061 1.06l-2.5-2.5a.75.75 0 0 1 0-1.06l2.5-2.5a.75.75 0 0 1 1.06 0z\"/></svg>');\n\t--magiclink-issue-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill-rule=\"evenodd\" d=\"M2.5 12a9.5 9.5 0 1 1 19 0 9.5 9.5 0 0 1-19 0zM12 1C5.925 1 1 5.925 1 12s4.925 11 11 11 11-4.925 11-11S18.075 1 12 1zm0 13a2 2 0 1 0 0-4 2 2 0 0 0 0 4z\"/></svg>');\n\t--magiclink-discussion-icon: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill-rule=\"evenodd\" d=\"M1.75 1A1.75 1.75 0 0 0 0 2.75v9.5C0 13.216.784 14 1.75 14H3v1.543a1.457 1.457 0 0 0 2.487 1.03L8.061 14h6.189A1.75 1.75 0 0 0 16 12.25v-9.5A1.75 1.75 0 0 0 14.25 1H1.75zM1.5 2.75a.25.25 0 0 1 .25-.25h12.5a.25.25 0 0 1 .25.25v9.5a.25.25 0 0 1-.25.25h-6.5a.75.75 0 0 0-.53.22L4.5 15.44v-2.19a.75.75 0 0 0-.75-.75h-2a.25.25 0 0 1-.25-.25v-9.5z\"/><path d=\"M22.5 8.75a.25.25 0 0 0-.25-.25h-3.5a.75.75 0 0 1 0-1.5h3.5c.966 0 1.75.784 1.75 1.75v9.5A1.75 1.75 0 0 1 22.25 20H21v1.543a1.457 1.457 0 0 1-2.487 1.03L15.939 20H10.75A1.75 1.75 0 0 1 9 18.25v-1.465a.75.75 0 0 1 1.5 0v1.465c0 .138.112.25.25.25h5.5a.75.75 0 0 1 .53.22l2.72 2.72v-2.19a.75.75 0 0 1 .75-.75h2a.25.25 0 0 0 .25-.25v-9.5z\"/></svg>')\n}\n\n.md-typeset a[href^=\"mailto:\"]:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-email-icon);\n\tmask-image: var(--magiclink-email-icon)\n}\n\n.md-typeset .magiclink-commit:not(.magiclink-ignore),\n.md-typeset .magiclink-compare:not(.magiclink-ignore),\n.md-typeset .magiclink-discussion:not(.magiclink-ignore),\n.md-typeset .magiclink-issue:not(.magiclink-ignore),\n.md-typeset .magiclink-pull:not(.magiclink-ignore),\n.md-typeset .magiclink-repository:not(.magiclink-ignore),\n.md-typeset a[href^=\"mailto:\"]:not(.magiclink-ignore) {\n\tposition: relative;\n\tpadding-left: 1.375em\n}\n\n.md-typeset .magiclink-commit:not(.magiclink-ignore)::before,\n.md-typeset .magiclink-compare:not(.magiclink-ignore)::before,\n.md-typeset .magiclink-discussion:not(.magiclink-ignore)::before,\n.md-typeset .magiclink-issue:not(.magiclink-ignore)::before,\n.md-typeset .magiclink-pull:not(.magiclink-ignore)::before,\n.md-typeset .magiclink-repository:not(.magiclink-ignore)::before,\n.md-typeset a[href^=\"mailto:\"]:not(.magiclink-ignore)::before {\n\tposition: absolute;\n\ttop: 0;\n\tleft: 0;\n\tdisplay: block;\n\tbox-sizing: border-box;\n\twidth: 1.25em;\n\theight: 1.25em;\n\tbackground-color: var(--md-typeset-a-color);\n\tbackground-size: 1.25em;\n\ttransition: background-color 125ms;\n\t-webkit-mask-repeat: no-repeat;\n\tmask-repeat: no-repeat;\n\t-webkit-mask-size: contain;\n\tmask-size: contain;\n\tcontent: \"\"\n}\n\n.md-typeset .magiclink-commit:not(.magiclink-ignore):hover::before,\n.md-typeset .magiclink-compare:not(.magiclink-ignore):hover::before,\n.md-typeset .magiclink-discussion:not(.magiclink-ignore):hover::before,\n.md-typeset .magiclink-issue:not(.magiclink-ignore):hover::before,\n.md-typeset .magiclink-pull:not(.magiclink-ignore):hover::before,\n.md-typeset .magiclink-repository:not(.magiclink-ignore):hover::before,\n.md-typeset a[href^=\"mailto:\"]:not(.magiclink-ignore):hover::before {\n\tbackground-color: var(--md-accent-fg-color)\n}\n\n.md-typeset .magiclink-commit:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-commit-icon);\n\tmask-image: var(--magiclink-commit-icon)\n}\n\n.md-typeset .magiclink-compare:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-compare-icon);\n\tmask-image: var(--magiclink-compare-icon)\n}\n\n.md-typeset .magiclink-pull:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-pull-icon);\n\tmask-image: var(--magiclink-pull-icon)\n}\n\n.md-typeset .magiclink-issue:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-issue-icon);\n\tmask-image: var(--magiclink-issue-icon)\n}\n\n.md-typeset .magiclink-discussion:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-discussion-icon);\n\tmask-image: var(--magiclink-discussion-icon)\n}\n\n.md-typeset .magiclink-repository.magiclink-github:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-github-icon);\n\tmask-image: var(--magiclink-github-icon)\n}\n\n.md-typeset .magiclink-repository.magiclink-gitlab:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-gitlab-icon);\n\tmask-image: var(--magiclink-gitlab-icon)\n}\n\n.md-typeset .magiclink-repository.magiclink-bitbucket:not(.magiclink-ignore)::before {\n\t-webkit-mask-image: var(--magiclink-bitbucket-icon);\n\tmask-image: var(--magiclink-bitbucket-icon)\n}\n\n.md-typeset mark:not(.critic) {\n\tpadding: 0 .25em;\n\tbox-shadow: none\n}\n\n.md-typeset .progress-label {\n\tposition: absolute;\n\twidth: 100%;\n\tmargin: 0;\n\tcolor: var(--md-text-color);\n\tfont-weight: 700;\n\tline-height: 1.4rem;\n\twhite-space: nowrap;\n\ttext-align: center;\n\ttext-shadow: -.0625em -.0625em .375em var(--md-default-bg-color--light), .0625em -.0625em .375em var(--md-default-bg-color--light), -.0625em .0625em .375em var(--md-default-bg-color--light), .0625em .0625em .375em var(--md-default-bg-color--light)\n}\n\n.md-typeset .progress-bar {\n\tfloat: left;\n\theight: 1.2rem;\n\tbackground-color: #2979ff\n}\n\n.md-typeset .candystripe-animate .progress-bar {\n\t-webkit-animation: animate-stripes 3s linear infinite;\n\tanimation: animate-stripes 3s linear infinite\n}\n\n.md-typeset .progress {\n\tposition: relative;\n\tdisplay: block;\n\twidth: 100%;\n\theight: 1.2rem;\n\tmargin: .5rem 0;\n\tbackground-color: var(--md-default-fg-color--lightest)\n}\n\n.md-typeset .progress.thin {\n\theight: .4rem;\n\tmargin-top: .9rem\n}\n\n.md-typeset .progress.thin .progress-label {\n\tmargin-top: -.4rem\n}\n\n.md-typeset .progress.thin .progress-bar {\n\theight: .4rem\n}\n\n.md-typeset .progress.candystripe .progress-bar {\n\tbackground-image: linear-gradient(135deg, var(--md-progress-stripe) 27%, transparent 27%, transparent 52%, var(--md-progress-stripe) 52%, var(--md-progress-stripe) 77%, transparent 77%, transparent);\n\tbackground-size: 2rem 2rem\n}\n\n.md-typeset .progress-100plus .progress-bar {\n\tbackground-color: var(--md-progress-100)\n}\n\n.md-typeset .progress-80plus .progress-bar {\n\tbackground-color: var(--md-progress-80)\n}\n\n.md-typeset .progress-60plus .progress-bar {\n\tbackground-color: var(--md-progress-60)\n}\n\n.md-typeset .progress-40plus .progress-bar {\n\tbackground-color: var(--md-progress-40)\n}\n\n.md-typeset .progress-20plus .progress-bar {\n\tbackground-color: var(--md-progress-20)\n}\n\n.md-typeset .progress-0plus .progress-bar {\n\tbackground-color: var(--md-progress-0)\n}\n\n@-webkit-keyframes animate-stripes {\n\t0% {\n\t\tbackground-position: 0 0\n\t}\n\n\t100% {\n\t\tbackground-position: 6rem 0\n\t}\n}\n\n@keyframes animate-stripes {\n\t0% {\n\t\tbackground-position: 0 0\n\t}\n\n\t100% {\n\t\tbackground-position: 6rem 0\n\t}\n}\n\n[data-md-color-scheme=dracula] .md-typeset .tabbed-set>.tabbed-labels {\n\tbox-shadow: 0 -.05rem var(--md-default-fg-color--lighter) inset\n}\n\n.md-typeset .tabbed-alternate.tabbed-set .tabbed-control {\n\twidth: 2rem\n}\n\n.md-typeset .tabbed-alternate.tabbed-set .tabbed-control[hidden] {\n\twidth: 1.2rem;\n\topacity: 0\n}\n\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block {\n\tpadding: 0 .6rem;\n\toverflow: hidden\n}\n\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.codehilite:only-child,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.codehilitetable:only-child,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.highlight:only-child,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.highlighttable:only-child,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>pre:only-child {\n\tmargin-right: -1.2rem;\n\tmargin-left: -1.2rem;\n\tpadding-right: .6rem;\n\tpadding-left: .6rem\n}\n\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.codehilite:only-child span.filename,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.codehilitetable:only-child span.filename,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.highlight:only-child span.filename,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>.highlighttable:only-child span.filename,\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>pre:only-child span.filename {\n\tmargin-top: 0\n}\n\n.md-typeset .tabbed-alternate.tabbed-set>.tabbed-content>.tabbed-block>mermaid-div:only-child {\n\tmargin-right: -1.2rem;\n\tmargin-left: -1.2rem;\n\tpadding-right: .6rem;\n\tpadding-left: .6rem\n}\n\n[data-md-color-scheme=dracula] .md-typeset table:not([class]) {\n\tbox-shadow: 0 .2rem .5rem hsla(0deg, 0%, 0%, .3), 0 0 .05rem hsla(0deg, 0%, 0%, .2)\n}\n\n[data-md-color-scheme=dracula] .md-typeset table:not([class]) tr:hover {\n\tbackground-color: rgba(0, 0, 0, .08)\n}\n\n[data-md-color-scheme=dracula] .md-typeset table:not([class]) th {\n\tcolor: var(--md-text-color);\n\tbackground-color: var(--md-default-bg-color--ultra-dark);\n\tborder-bottom: .05rem solid var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-typeset table:not([class]) td {\n\tborder-top: .05rem solid var(--md-default-fg-color--lighter)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .task-list-control .task-list-indicator::before {\n\tbackground-color: var(--md-default-fg-color--lighter)\n}\n\n[data-md-color-scheme=dracula] .md-typeset .task-list-control [type=checkbox]:checked+.task-list-indicator::before {\n\tbackground-color: hsl(135deg, 94%, 65%)\n}\n\n.md-typeset .headerlink {\n\twidth: 1em;\n\theight: 1em;\n\tvertical-align: middle;\n\tbackground-color: var(--md-default-fg-color--lighter);\n\tbackground-size: 1em;\n\t-webkit-mask-size: 1em;\n\tmask-size: 1em;\n\t-webkit-mask-repeat: no-repeat;\n\tmask-repeat: no-repeat;\n\tvisibility: visible;\n\t-webkit-mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1M8 13h8v-2H8v2m9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1 0 1.71-1.39 3.1-3.1 3.1h-4V17h4a5 5 0 0 0 5-5 5 5 0 0 0-5-5Z\"/></svg>');\n\tmask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path d=\"M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7a5 5 0 0 0-5 5 5 5 0 0 0 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1M8 13h8v-2H8v2m9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1 0 1.71-1.39 3.1-3.1 3.1h-4V17h4a5 5 0 0 0 5-5 5 5 0 0 0-5-5Z\"/></svg>')\n}\n\n.md-typeset .headerlink:hover,\n.md-typeset [id]:target .headerlink {\n\tbackground-color: var(--md-accent-fg-color)\n}\n\ndiagram-div {\n\toverflow: auto\n}\n\nhtml {\n\tbackground-color: transparent\n}\n\n[data-md-component=announce] .twemoji {\n\tcolor: var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] {\n\t--md-text-color: var(--md-default-fg-color);\n\tbackground-color: var(--md-default-bg-color);\n\t--md-footer-bg-color: transparent;\n\t--md-footer-bg-color--dark: var(--md-default-bg-color--darkest);\n\t--md-header-fg-color: var(--md-text-color);\n\t--md-header-bg-color: var(--md-default-bg-color--darkest)\n}\n\n[data-md-color-scheme=dracula] .md-header {\n\tcolor: var(--md-text-color);\n\tbackground-color: var(--md-header-bg-color);\n\tborder-bottom: .05rem solid var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-header[data-md-state=shadow] {\n\tbox-shadow: 0 0 .2rem rgba(0, 0, 0, .15), 0 0 .2rem .4rem rgba(0, 0, 0, .2)\n}\n\n[data-md-color-scheme=dracula] .md-top {\n\tbackground-color: var(--md-default-bg-color--dark)\n}\n\n[data-md-color-scheme=dracula] .md-top:hover {\n\tbackground-color: var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-tabs {\n\tcolor: var(--md-text-color);\n\tbackground-color: var(--md-primary-fg-color--transparent)\n}\n\n[data-md-color-scheme=dracula] .md-tabs__link--active {\n\tcolor: var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-tabs__link:hover {\n\tcolor: var(--md-accent-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-hero {\n\tcolor: var(--md-text-color);\n\tbackground-color: var(--md-primary-fg-color--transparent)\n}\n\n[data-md-color-scheme=dracula] .md-nav__source {\n\tcolor: var(--md-text-color)\n}\n\n[data-md-color-scheme=dracula] .md-nav__link[data-md-state=blur] {\n\tcolor: var(--md-default-fg-color--light)\n}\n\n[data-md-color-scheme=dracula] .md-nav__item .md-nav__link--active {\n\tcolor: var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-nav__link:focus,\n[data-md-color-scheme=dracula] .md-nav__link:hover {\n\tcolor: var(--md-accent-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-search__input {\n\tcolor: var(--md-text-color);\n\tbackground-color: var(--md-accent-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] .md-search__input:hover {\n\tbackground-color: var(--md-default-bg-color)\n}\n\n[data-md-color-scheme=dracula] .md-search__input~.md-search__icon {\n\tcolor: var(--md-text-color)\n}\n\n[data-md-color-scheme=dracula] .md-search__input::-moz-placeholder {\n\tcolor: var(--md-default-fg-color--light)\n}\n\n[data-md-color-scheme=dracula] .md-search__input:-ms-input-placeholder {\n\tcolor: var(--md-default-fg-color--light)\n}\n\n[data-md-color-scheme=dracula] .md-search__input::placeholder {\n\tcolor: var(--md-default-fg-color--light)\n}\n\n[data-md-color-scheme=dracula] .md-overlay,\n[data-md-color-scheme=dracula] .md-search__overlay {\n\tbackground-color: var(--md-default-bg-color--light)\n}\n\n[data-md-color-scheme=dracula] .md-footer-nav__direction {\n\tcolor: var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] .md-footer-meta {\n\tborder-top: .05rem solid var(--md-primary-fg-color)\n}\n\n[data-md-color-scheme=dracula] [data-md-component=announce] {\n\tbackground-color: var(--md-default-bg-color--ultra-dark)\n}\n\n.md-typeset h5 {\n\tcolor: var(--md-text-color);\n\ttext-transform: none\n}\n\n.md-search__scrollwrap,\n.md-sidebar__scrollwrap,\n.md-typeset div.arithmatex,\n.md-typeset div.mermaid,\n.md-typeset mermaid-div,\n.md-typeset pre.arithmatex,\n.md-typeset pre>code,\n.md-typeset__scrollwrap {\n\tscrollbar-color: var(--md-default-fg-color--lighter) transparent;\n\tscrollbar-width: thin\n}\n\n.md-search__scrollwrap::-webkit-scrollbar,\n.md-sidebar__scrollwrap::-webkit-scrollbar,\n.md-typeset div.arithmatex::-webkit-scrollbar,\n.md-typeset div.mermaid::-webkit-scrollbar,\n.md-typeset mermaid-div::-webkit-scrollbar,\n.md-typeset pre.arithmatex::-webkit-scrollbar,\n.md-typeset pre>code::-webkit-scrollbar,\n.md-typeset__scrollwrap::-webkit-scrollbar {\n\twidth: .2rem;\n\theight: .2rem\n}\n\n.md-search__scrollwrap::-webkit-scrollbar-corner,\n.md-sidebar__scrollwrap::-webkit-scrollbar-corner,\n.md-typeset div.arithmatex::-webkit-scrollbar-corner,\n.md-typeset div.mermaid::-webkit-scrollbar-corner,\n.md-typeset mermaid-div::-webkit-scrollbar-corner,\n.md-typeset pre.arithmatex::-webkit-scrollbar-corner,\n.md-typeset pre>code::-webkit-scrollbar-corner,\n.md-typeset__scrollwrap::-webkit-scrollbar-corner {\n\tbackground-color: transparent\n}\n\n.md-search__scrollwrap::-webkit-scrollbar-thumb,\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb,\n.md-typeset div.arithmatex::-webkit-scrollbar-thumb,\n.md-typeset div.mermaid::-webkit-scrollbar-thumb,\n.md-typeset mermaid-div::-webkit-scrollbar-thumb,\n.md-typeset pre.arithmatex::-webkit-scrollbar-thumb,\n.md-typeset pre>code::-webkit-scrollbar-thumb,\n.md-typeset__scrollwrap::-webkit-scrollbar-thumb {\n\tbackground-color: var(--md-default-fg-color--lighter)\n}\n\n.md-search__scrollwrap::-webkit-scrollbar-thumb:hover,\n.md-sidebar__scrollwrap::-webkit-scrollbar-thumb:hover,\n.md-typeset div.arithmatex::-webkit-scrollbar-thumb:hover,\n.md-typeset div.mermaid::-webkit-scrollbar-thumb:hover,\n.md-typeset mermaid-div::-webkit-scrollbar-thumb:hover,\n.md-typeset pre.arithmatex::-webkit-scrollbar-thumb:hover,\n.md-typeset pre>code::-webkit-scrollbar-thumb:hover,\n.md-typeset__scrollwrap::-webkit-scrollbar-thumb:hover {\n\tbackground-color: var(--md-accent-fg-color)\n}\n\n.md-search__scrollwrap:hover,\n.md-sidebar__scrollwrap:hover,\n.md-typeset div.arithmatex:hover,\n.md-typeset div.mermaid:hover,\n.md-typeset mermaid-div:hover,\n.md-typeset pre.arithmatex:hover,\n.md-typeset pre>code:hover,\n.md-typeset__scrollwrap:hover {\n\tscrollbar-color: var(--md-accent-fg-color) transparent\n}\n\n@media screen and (max-width:59.9375em) {\n\t.md-header-nav__scheme {\n\t\tpadding-right: 0\n\t}\n\n\tlabel[for=__search] {\n\t\tpadding-left: 0\n\t}\n\n\t[data-md-color-scheme=dracula] .md-nav__source {\n\t\tcolor: var(--md-text-color);\n\t\tbackground-color: var(--md-primary-fg-color--transparent)\n\t}\n\n\t[data-md-color-scheme=dracula] .md-nav .md-nav__title {\n\t\tcolor: var(--md-text-color);\n\t\tbackground-color: var(--md-header-bg-color);\n\t\tborder-bottom: .05rem solid var(--md-primary-fg-color)\n\t}\n}\n\n@media screen and (max-width:44.9375em) {\n\t[dir=ltr] .md-content__inner>.tabbed-set .tabbed-labels {\n\t\tpadding-left: 0\n\t}\n\n\t.md-content__inner>.tabbed-set .tabbed-labels {\n\t\tmax-width: 100%;\n\t\tmargin: 0;\n\t\t-webkit-padding-start: 0;\n\t\tpadding-inline-start: 0;\n\t\tscroll-padding-inline-start: 0\n\t}\n\n\t.md-content__inner>.tabbed-set .tabbed-labels::after {\n\t\t-webkit-padding-end: 0;\n\t\tpadding-inline-end: 0;\n\t\tcontent: none\n\t}\n\n\t.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--prev {\n\t\t-webkit-margin-start: 0;\n\t\tmargin-inline-start: 0;\n\t\t-webkit-padding-start: 0;\n\t\tpadding-inline-start: 0\n\t}\n\n\t.md-content__inner>.tabbed-set .tabbed-labels~.tabbed-control--next {\n\t\t-webkit-margin-end: 0;\n\t\tmargin-inline-end: 0;\n\t\t-webkit-padding-end: 0;\n\t\tpadding-inline-end: 0\n\t}\n}\n\n@media screen and (max-width:76.1875em) {\n\t[data-md-color-scheme=dracula] .md-nav--primary .md-nav__item--active>.md-nav__link:not(:hover) {\n\t\tcolor: var(--md-primary-fg-color)\n\t}\n\n\t[data-md-color-scheme=dracula] .md-nav--primary .md-nav__title {\n\t\tcolor: var(--md-text-color);\n\t\tbackground-color: var(--md-header-bg-color);\n\t\tborder-bottom: .05rem solid var(--md-primary-fg-color)\n\t}\n}\n\n/*# sourceMappingURL=extra-e384f43f0f.css.map */\n\n/*# sourceMappingURL=extra-4ca32c29f0.css.map */\n/*# source: https://github.com/facelessuser/pymdown-extensions/blob/main/docs/theme/assets/pymdownx-extras/extra-4ca32c29f0.css */\n/*# css unpacked using: http://cssunpacker.com/ */\n"
  },
  {
    "path": "docs/assets/css-js/pymdownx-extras/js/extra-uml.js",
    "content": "function _typeof(e) { return (_typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (e) { return typeof e } : function (e) { return e && \"function\" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? \"symbol\" : typeof e })(e) } !function () { \"use strict\"; function e(t) { return (e = Object.setPrototypeOf ? Object.getPrototypeOf : function (e) { return e.__proto__ || Object.getPrototypeOf(e) })(t) } function t(e, n) { return (t = Object.setPrototypeOf || function (e, t) { return e.__proto__ = t, e })(e, n) } function n() { if (\"undefined\" == typeof Reflect || !Reflect.construct) return !1; if (Reflect.construct.sham) return !1; if (\"function\" == typeof Proxy) return !0; try { return Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], (function () { }))), !0 } catch (e) { return !1 } } function o(e, r, i) { return (o = n() ? Reflect.construct : function (e, n, o) { var r = [null]; r.push.apply(r, n); var i = new (Function.bind.apply(e, r)); return o && t(i, o.prototype), i }).apply(null, arguments) } function r(n) { var i = \"function\" == typeof Map ? new Map : void 0; return (r = function (n) { if (null === n || (r = n, -1 === Function.toString.call(r).indexOf(\"[native code]\"))) return n; var r; if (\"function\" != typeof n) throw new TypeError(\"Super expression must either be null or a function\"); if (void 0 !== i) { if (i.has(n)) return i.get(n); i.set(n, a) } function a() { return o(n, arguments, e(this).constructor) } return a.prototype = Object.create(n.prototype, { constructor: { value: a, enumerable: !1, writable: !0, configurable: !0 } }), t(a, n) })(n) } function i(e, t) { if (t && (\"object\" === _typeof(t) || \"function\" == typeof t)) return t; if (void 0 !== t) throw new TypeError(\"Derived constructors may only return object or undefined\"); return function (e) { if (void 0 === e) throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); return e }(e) } var a, c, u = function (o) { var a = function (o) { !function (e, n) { if (\"function\" != typeof n && null !== n) throw new TypeError(\"Super expression must either be null or a function\"); e.prototype = Object.create(n && n.prototype, { constructor: { value: e, writable: !0, configurable: !0 } }), n && t(e, n) }(u, o); var r, a, c = (r = u, a = n(), function () { var t, n = e(r); if (a) { var o = e(this).constructor; t = Reflect.construct(n, arguments, o) } else t = n.apply(this, arguments); return i(this, t) }); function u() { var e; !function (e, t) { if (!(e instanceof t)) throw new TypeError(\"Cannot call a class as a function\") }(this, u); var t = (e = c.call(this)).attachShadow({ mode: \"open\" }), n = document.createElement(\"style\"); return n.textContent = \"\\n      :host {\\n        display: block;\\n        line-height: initial;\\n        font-size: 16px;\\n      }\\n      div.mermaid {\\n        margin: 0;\\n        overflow: visible;\\n      }\", t.appendChild(n), e } return u }(r(HTMLElement)); void 0 === customElements.get(\"mermaid-div\") && customElements.define(\"mermaid-div\", a); var c = { startOnLoad: !1, theme: \"default\", flowchart: { htmlLabels: !1 }, er: { useMaxWidth: !1 }, sequence: { useMaxWidth: !1, noteFontWeight: \"14px\", actorFontSize: \"14px\", messageFontSize: \"16px\" } }; mermaid.mermaidAPI.globalReset(); var u = null; try { u = document.querySelector(\"[data-md-color-scheme]\").getAttribute(\"data-md-color-scheme\") } catch (e) { u = \"default\" } var l = \"undefined\" == typeof mermaidConfig ? c : mermaidConfig[u] || mermaidConfig.default || c; mermaid.initialize(l); for (var d = document.querySelectorAll(\"pre.\".concat(o, \", mermaid-div\")), f = document.querySelector(\"html\"), s = function (e) { var t = d[e], n = \"mermaid-div\" === t.tagName.toLowerCase() ? t.shadowRoot.querySelector(\"pre.\".concat(o)) : t, r = document.createElement(\"div\"); r.style.visibility = \"hidden\", r.style.display = \"display\", r.style.padding = \"0\", r.style.margin = \"0\", r.style.lineHeight = \"initial\", r.style.fontSize = \"16px\", f.appendChild(r); try { mermaid.mermaidAPI.render(\"_mermaid_\".concat(e), function (e) { for (var t = \"\", n = 0; n < e.childNodes.length; n++) { var o = e.childNodes[n]; if (\"code\" === o.tagName.toLowerCase()) for (var r = 0; r < o.childNodes.length; r++) { var i = o.childNodes[r]; if (\"#text\" === i.nodeName && !/^\\s*$/.test(i.nodeValue)) { t = i.nodeValue; break } } } return t }(n), (function (e) { var r = document.createElement(\"div\"); r.className = o, r.innerHTML = e; var i = document.createElement(\"mermaid-div\"); i.shadowRoot.appendChild(r), t.parentNode.insertBefore(i, t), n.style.display = \"none\", i.shadowRoot.appendChild(n), n !== t && t.parentNode.removeChild(t) }), r) } catch (e) { } f.contains(r) && f.removeChild(r) }, m = 0; m < d.length; m++)s(m) }; c = new MutationObserver((function (e) { e.forEach((function (e) { if (\"attributes\" === e.type) { var t = e.target.getAttribute(\"data-md-color-scheme\"); t || (t = \"default\"), localStorage.setItem(\"data-md-color-scheme\", t), \"undefined\" != typeof mermaid && u(\"mermaid\") } })) })), a = function () { c.observe(document.querySelector(\"body\"), { attributeFilter: [\"data-md-color-scheme\"] }), \"undefined\" != typeof mermaid && u(\"mermaid\") }, document.addEventListener(\"DOMContentLoaded\", a), document.addEventListener(\"DOMContentSwitch\", a) }();\n//# sourceMappingURL=extra-uml-fcd33c93.js.map\n//# source: https://github.com/facelessuser/pymdown-extensions/blob/main/docs/theme/assets/pymdownx-extras/extra-uml-fcd33c93.js\n"
  },
  {
    "path": "docs/assets/css-js/termynal/css/custom.css",
    "content": ".termynal-comment {\n    color: #4a968f;\n    font-style: italic;\n    display: block;\n}\n\n.termy [data-termynal] {\n    white-space: pre-wrap;\n}\n\na.external-link::after {\n    /* \\00A0 is a non-breaking space\n        to make the mark be on the same line as the link\n    */\n    content: \"\\00A0[↪]\";\n}\n\na.internal-link::after {\n    /* \\00A0 is a non-breaking space\n        to make the mark be on the same line as the link\n    */\n    content: \"\\00A0↪\";\n}\n\n.shadow {\n    box-shadow: 5px 5px 10px #999;\n}\n"
  },
  {
    "path": "docs/assets/css-js/termynal/css/termynal.css",
    "content": "/**\n * termynal.js\n *\n * @author Ines Montani <ines@ines.io>\n * @version 0.0.1\n * @license MIT\n */\n\n:root {\n    --color-bg: #252a33;\n    --color-text: #eee;\n    --color-text-subtle: #a2a2a2;\n}\n\n[data-termynal] {\n    width: 750px;\n    max-width: 100%;\n    background: var(--color-bg);\n    color: var(--color-text);\n    /* font-size: 18px; */\n    font-size: 15px;\n    /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */\n    font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace;\n    border-radius: 4px;\n    padding: 75px 45px 35px;\n    position: relative;\n    -webkit-box-sizing: border-box;\n            box-sizing: border-box;\n}\n\n[data-termynal]:before {\n    content: '';\n    position: absolute;\n    top: 15px;\n    left: 15px;\n    display: inline-block;\n    width: 15px;\n    height: 15px;\n    border-radius: 50%;\n    /* A little hack to display the window buttons in one pseudo element. */\n    background: #d9515d;\n    -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;\n            box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930;\n}\n\n[data-termynal]:after {\n    content: 'bash';\n    position: absolute;\n    color: var(--color-text-subtle);\n    top: 5px;\n    left: 0;\n    width: 100%;\n    text-align: center;\n}\n\na[data-terminal-control] {\n    text-align: right;\n    display: block;\n    color: #aebbff;\n}\n\n[data-ty] {\n    display: block;\n    line-height: 2;\n}\n\n[data-ty]:before {\n    /* Set up defaults and ensure empty lines are displayed. */\n    content: '';\n    display: inline-block;\n    vertical-align: middle;\n}\n\n[data-ty=\"input\"]:before,\n[data-ty-prompt]:before {\n    margin-right: 0.75em;\n    color: var(--color-text-subtle);\n}\n\n[data-ty=\"input\"]:before {\n    content: '$';\n}\n\n[data-ty][data-ty-prompt]:before {\n    content: attr(data-ty-prompt);\n}\n\n[data-ty-cursor]:after {\n    content: attr(data-ty-cursor);\n    font-family: monospace;\n    margin-left: 0.5em;\n    -webkit-animation: blink 1s infinite;\n            animation: blink 1s infinite;\n}\n\n\n/* Cursor animation */\n\n@-webkit-keyframes blink {\n    50% {\n        opacity: 0;\n    }\n}\n\n@keyframes blink {\n    50% {\n        opacity: 0;\n    }\n}\n"
  },
  {
    "path": "docs/assets/css-js/termynal/js/custom.js",
    "content": "function setupTermynal() {\n    document.querySelectorAll(\".use-termynal\").forEach(node => {\n        node.style.display = \"block\";\n        new Termynal(node, {\n            lineDelay: 500\n        });\n    });\n    const progressLiteralStart = \"---> 100%\";\n    const promptLiteralStart = \"$ \";\n    const customPromptLiteralStart = \"# \";\n    const termynalActivateClass = \"termy\";\n    let termynals = [];\n\n    function createTermynals() {\n        document\n            .querySelectorAll(`.${termynalActivateClass} .highlight`)\n            .forEach(node => {\n                const text = node.textContent;\n                const lines = text.split(\"\\n\");\n                const useLines = [];\n                let buffer = [];\n                function saveBuffer() {\n                    if (buffer.length) {\n                        let isBlankSpace = true;\n                        buffer.forEach(line => {\n                            if (line) {\n                                isBlankSpace = false;\n                            }\n                        });\n                        dataValue = {};\n                        if (isBlankSpace) {\n                            dataValue[\"delay\"] = 0;\n                        }\n                        if (buffer[buffer.length - 1] === \"\") {\n                            // A last single <br> won't have effect\n                            // so put an additional one\n                            buffer.push(\"\");\n                        }\n                        const bufferValue = buffer.join(\"<br>\");\n                        dataValue[\"value\"] = bufferValue;\n                        useLines.push(dataValue);\n                        buffer = [];\n                    }\n                }\n                for (let line of lines) {\n                    if (line === progressLiteralStart) {\n                        saveBuffer();\n                        useLines.push({\n                            type: \"progress\"\n                        });\n                    } else if (line.startsWith(promptLiteralStart)) {\n                        saveBuffer();\n                        const value = line.replace(promptLiteralStart, \"\").trimEnd();\n                        useLines.push({\n                            type: \"input\",\n                            value: value\n                        });\n                    } else if (line.startsWith(\"// \")) {\n                        saveBuffer();\n                        const value = \"💬 \" + line.replace(\"// \", \"\").trimEnd();\n                        useLines.push({\n                            value: value,\n                            class: \"termynal-comment\",\n                            delay: 0\n                        });\n                    } else if (line.startsWith(customPromptLiteralStart)) {\n                        saveBuffer();\n                        const promptStart = line.indexOf(promptLiteralStart);\n                        if (promptStart === -1) {\n                            console.error(\"Custom prompt found but no end delimiter\", line)\n                        }\n                        const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, \"\")\n                        let value = line.slice(promptStart + promptLiteralStart.length);\n                        useLines.push({\n                            type: \"input\",\n                            value: value,\n                            prompt: prompt\n                        });\n                    } else {\n                        buffer.push(line);\n                    }\n                }\n                saveBuffer();\n                const div = document.createElement(\"div\");\n                node.replaceWith(div);\n                const termynal = new Termynal(div, {\n                    lineData: useLines,\n                    noInit: true,\n                    lineDelay: 500\n                });\n                termynals.push(termynal);\n            });\n    }\n\n    function loadVisibleTermynals() {\n        termynals = termynals.filter(termynal => {\n            if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) {\n                termynal.init();\n                return false;\n            }\n            return true;\n        });\n    }\n    window.addEventListener(\"scroll\", loadVisibleTermynals);\n    createTermynals();\n    loadVisibleTermynals();\n}\n\nasync function main() {\n    setupTermynal()\n}\n\nmain()\n"
  },
  {
    "path": "docs/assets/css-js/termynal/js/termynal.js",
    "content": "/**\n * termynal.js\n * A lightweight, modern and extensible animated terminal window, using\n * async/await.\n *\n * @author Ines Montani <ines@ines.io>\n * @version 0.0.1\n * @license MIT\n */\n\n'use strict';\n\n/** Generate a terminal widget. */\nclass Termynal {\n    /**\n     * Construct the widget's settings.\n     * @param {(string|Node)=} container - Query selector or container element.\n     * @param {Object=} options - Custom settings.\n     * @param {string} options.prefix - Prefix to use for data attributes.\n     * @param {number} options.startDelay - Delay before animation, in ms.\n     * @param {number} options.typeDelay - Delay between each typed character, in ms.\n     * @param {number} options.lineDelay - Delay between each line, in ms.\n     * @param {number} options.progressLength - Number of characters displayed as progress bar.\n     * @param {string} options.progressChar – Character to use for progress bar, defaults to █.\n     * @param {number} options.progressPercent - Max percent of progress.\n     * @param {string} options.cursor – Character to use for cursor, defaults to ▋.\n     * @param {Object[]} lineData - Dynamically loaded line data objects.\n     * @param {boolean} options.noInit - Don't initialise the animation.\n     */\n    constructor(container = '#termynal', options = {}) {\n        this.container = (typeof container === 'string') ? document.querySelector(container) : container;\n        this.pfx = `data-${options.prefix || 'ty'}`;\n        this.originalStartDelay = this.startDelay = options.startDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600;\n        this.originalTypeDelay = this.typeDelay = options.typeDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90;\n        this.originalLineDelay = this.lineDelay = options.lineDelay\n            || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500;\n        this.progressLength = options.progressLength\n            || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40;\n        this.progressChar = options.progressChar\n            || this.container.getAttribute(`${this.pfx}-progressChar`) || '█';\n        this.progressPercent = options.progressPercent\n            || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100;\n        this.cursor = options.cursor\n            || this.container.getAttribute(`${this.pfx}-cursor`) || '▋';\n        this.lineData = this.lineDataToElements(options.lineData || []);\n        this.loadLines()\n        if (!options.noInit) this.init()\n    }\n\n    loadLines() {\n        // Load all the lines and create the container so that the size is fixed\n        // Otherwise it would be changing and the user viewport would be constantly\n        // moving as she/he scrolls\n        const finish = this.generateFinish()\n        finish.style.visibility = 'hidden'\n        this.container.appendChild(finish)\n        // Appends dynamically loaded lines to existing line elements.\n        this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData);\n        for (let line of this.lines) {\n            line.style.visibility = 'hidden'\n            this.container.appendChild(line)\n        }\n        const restart = this.generateRestart()\n        restart.style.visibility = 'hidden'\n        this.container.appendChild(restart)\n        this.container.setAttribute('data-termynal', '');\n    }\n\n    /**\n     * Initialise the widget, get lines, clear container and start animation.\n     */\n    init() {\n        /**\n         * Calculates width and height of Termynal container.\n         * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.\n         */\n        const containerStyle = getComputedStyle(this.container);\n        this.container.style.width = containerStyle.width !== '0px' ?\n            containerStyle.width : undefined;\n        this.container.style.minHeight = containerStyle.height !== '0px' ?\n            containerStyle.height : undefined;\n\n        this.container.setAttribute('data-termynal', '');\n        this.container.innerHTML = '';\n        for (let line of this.lines) {\n            line.style.visibility = 'visible'\n        }\n        this.start();\n    }\n\n    /**\n     * Start the animation and rener the lines depending on their data attributes.\n     */\n    async start() {\n        this.addFinish()\n        await this._wait(this.startDelay);\n\n        for (let line of this.lines) {\n            const type = line.getAttribute(this.pfx);\n            const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay;\n\n            if (type == 'input') {\n                line.setAttribute(`${this.pfx}-cursor`, this.cursor);\n                await this.type(line);\n                await this._wait(delay);\n            }\n\n            else if (type == 'progress') {\n                await this.progress(line);\n                await this._wait(delay);\n            }\n\n            else {\n                this.container.appendChild(line);\n                await this._wait(delay);\n            }\n\n            line.removeAttribute(`${this.pfx}-cursor`);\n        }\n        this.addRestart()\n        this.finishElement.style.visibility = 'hidden'\n        this.lineDelay = this.originalLineDelay\n        this.typeDelay = this.originalTypeDelay\n        this.startDelay = this.originalStartDelay\n    }\n\n    generateRestart() {\n        const restart = document.createElement('a')\n        restart.onclick = (e) => {\n            e.preventDefault()\n            this.container.innerHTML = ''\n            this.init()\n        }\n        restart.href = '#'\n        restart.setAttribute('data-terminal-control', '')\n        restart.innerHTML = \"restart ↻\"\n        return restart\n    }\n\n    generateFinish() {\n        const finish = document.createElement('a')\n        finish.onclick = (e) => {\n            e.preventDefault()\n            this.lineDelay = 0\n            this.typeDelay = 0\n            this.startDelay = 0\n        }\n        finish.href = '#'\n        finish.setAttribute('data-terminal-control', '')\n        finish.innerHTML = \"fast →\"\n        this.finishElement = finish\n        return finish\n    }\n\n    addRestart() {\n        const restart = this.generateRestart()\n        this.container.appendChild(restart)\n    }\n\n    addFinish() {\n        const finish = this.generateFinish()\n        this.container.appendChild(finish)\n    }\n\n    /**\n     * Animate a typed line.\n     * @param {Node} line - The line element to render.\n     */\n    async type(line) {\n        const chars = [...line.textContent];\n        line.textContent = '';\n        this.container.appendChild(line);\n\n        for (let char of chars) {\n            const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay;\n            await this._wait(delay);\n            line.textContent += char;\n        }\n    }\n\n    /**\n     * Animate a progress bar.\n     * @param {Node} line - The line element to render.\n     */\n    async progress(line) {\n        const progressLength = line.getAttribute(`${this.pfx}-progressLength`)\n            || this.progressLength;\n        const progressChar = line.getAttribute(`${this.pfx}-progressChar`)\n            || this.progressChar;\n        const chars = progressChar.repeat(progressLength);\n        const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`)\n            || this.progressPercent;\n        line.textContent = '';\n        this.container.appendChild(line);\n\n        for (let i = 1; i < chars.length + 1; i++) {\n            await this._wait(this.typeDelay);\n            const percent = Math.round(i / chars.length * 100);\n            line.textContent = `${chars.slice(0, i)} ${percent}%`;\n            if (percent > progressPercent) {\n                break;\n            }\n        }\n    }\n\n    /**\n     * Helper function for animation delays, called with `await`.\n     * @param {number} time - Timeout, in ms.\n     */\n    _wait(time) {\n        return new Promise(resolve => setTimeout(resolve, time));\n    }\n\n    /**\n     * Converts line data objects into line elements.\n     *\n     * @param {Object[]} lineData - Dynamically loaded lines.\n     * @param {Object} line - Line data object.\n     * @returns {Element[]} - Array of line elements.\n     */\n    lineDataToElements(lineData) {\n        return lineData.map(line => {\n            let div = document.createElement('div');\n            div.innerHTML = `<span ${this._attributes(line)}>${line.value || ''}</span>`;\n\n            return div.firstElementChild;\n        });\n    }\n\n    /**\n     * Helper function for generating attributes string.\n     *\n     * @param {Object} line - Line data object.\n     * @returns {string} - String of attributes.\n     */\n    _attributes(line) {\n        let attrs = '';\n        for (let prop in line) {\n            // Custom add class\n            if (prop === 'class') {\n                attrs += ` class=${line[prop]} `\n                continue\n            }\n            if (prop === 'type') {\n                attrs += `${this.pfx}=\"${line[prop]}\" `\n            } else if (prop !== 'value') {\n                attrs += `${this.pfx}-${prop}=\"${line[prop]}\" `\n            }\n        }\n        return attrs;\n    }\n}\n\n/**\n* HTML API: If current script has container(s) specified, initialise Termynal.\n*/\nif (document.currentScript.hasAttribute('data-termynal-container')) {\n    const containers = document.currentScript.getAttribute('data-termynal-container');\n    containers.split('|')\n        .forEach(container => new Termynal(container))\n}\n"
  },
  {
    "path": "docs/assets/css-js/termynal/readme.md",
    "content": "# **Termynal**: Terminal Animation\n\nJust copy and paste the `terminal` folder in a `docs/assests`\nfolder and then add the following snippet to your `mkdocs.yml`.\n\n```yaml\n# for terminal animation\nextra_css:\n  - assets/termynal/css/termynal.css\n  - assets/termynal/css/custom.css\n\nextra_javascript:\n  - assets/termynal/js/termynal.js\n  - assets/termynal/js/custom.js\n``\n"
  },
  {
    "path": "docs/assets/snippets/macros/.gitkeep",
    "content": ""
  },
  {
    "path": "docs/assets/snippets/notifications/videos/disclaimer.md",
    "content": "!!! warning \"Disclaimer\"\n\n    This library is currently under rapid initial development. Breaking changes may happen frequently.\n"
  },
  {
    "path": "docs/examples/hanoi.py",
    "content": "# Based on the following example from Diagrams\n# https://archives.haskell.org/projects.haskell.org/diagrams/gallery/Hanoi.html\n\nfrom PIL import Image as PILImage\nfrom typing import Dict, List, Tuple\n\nfrom colour import Color  # type: ignore\nfrom chalk import Diagram, rectangle, concat, hcat, vcat\n\n\nDisk = int\nStack = List[Disk]  # disks on one peg\nHanoi = List[Stack]  # disks on all three pegs\nMove = Tuple[int, int]\n\ncolors: List[Color] = [\n    Color(\"#9FB4CC\"),\n    Color(\"#CCCC9F\"),\n    Color(\"#DB4105\"),\n]\nblack = Color(\"black\")\n\n\ndef draw_disk(n: Disk) -> Diagram:\n    return (\n        rectangle(n + 2, 1)\n        .fill_color(colors[n])\n        .line_color(colors[n])\n        .line_width(0.05)\n    )\ndraw_disk(0)\n\n\ndef draw_stack(s: Stack) -> Diagram:\n    disks = vcat(map(draw_disk, s))\n    post = rectangle(0.8, 6).fill_color(black)\n    return post.align_b() + disks.align_b()\ndraw_stack([0, 1])\n\n\ndef draw_hanoi(state: Hanoi) -> Diagram:\n    hsep = 7\n    return concat(\n        draw_stack(stack).translate(7 * i, 0) for i, stack in enumerate(state)\n    )\ndraw_hanoi([[0], [1, 2], []])\n\ndef solve_hanoi(num_disks: int) -> List[Move]:\n    def solve_hanoi_1(num_disks, *, source, spare, target):\n        if num_disks <= 0:\n            return []\n        else:\n            return (\n                solve_hanoi_1(num_disks - 1, source=source, spare=target, target=spare)\n                + [(source, target)]\n                + solve_hanoi_1(num_disks - 1, source=spare, spare=source, target=target)\n            )\n\n    return solve_hanoi_1(num_disks, source=0, spare=1, target=2)\n\n\ndef do_move(move: Move, state: Hanoi) -> Hanoi:\n    def remove_disk(src, state):\n        disk, *src_new = state[src]\n        state_new = state[:src] + [src_new] + state[src + 1 :]\n        return disk, state_new\n\n    def add_disk(tgt, disk, state):\n        tgt_new = [disk] + state[tgt]\n        state_new = state[:tgt] + [tgt_new] + state[tgt + 1 :]\n        return state_new\n\n    src, tgt = move\n    disk, state1 = remove_disk(src, state)\n    state2 = add_disk(tgt, disk, state1)\n    return state2\n\n\ndef state_sequence(num_disks: int) -> List[Hanoi]:\n    state: Hanoi = [list(range(num_disks)), [], []]\n    states: List[Hanoi] = [state]\n    for move in solve_hanoi(num_disks):\n        state = do_move(move, state)\n        states.append(state)\n    return states\n\n\ndef draw_state_sequence(seq: List[Hanoi]) -> Diagram:\n    return concat(draw_hanoi(state).translate(0, 7.5 * i) for i, state in enumerate(seq))\n\n\ndiagram = draw_state_sequence(state_sequence(3))\n\n\npath = \"examples/output/hanoi.svg\"\ndiagram.render_svg(path, height=700)\npath = \"examples/output/hanoi.png\"\ndiagram.render(path, height=700)\nPILImage.open(path)\n"
  },
  {
    "path": "docs/examples/koch.py",
    "content": "# Based on the following example from Diagrams\n# https://archives.haskell.org/projects.haskell.org/diagrams/gallery/Koch.html\n\nfrom PIL import Image as PILImage\nfrom chalk import *\nfrom chalk.transform import *\nimport chalk\n\nunit_x = Trail.hrule(1)\n\ndef koch(n):\n    if n == 0:\n        return unit_x.scale_x(5)\n    else:\n        return (\n            koch(n - 1).scale(1 / 3)\n            + koch(n - 1).scale(1 / 3).rotate_by(+1 / 6)\n            + koch(n - 1).scale(1 / 3).rotate_by(-1 / 6)\n            + koch(n - 1).scale(1 / 3)\n        )\n\nd = vcat(koch(i).stroke().line_width(0.01) for i in range(1, 5))\n\n\n# Render\nheight = 512\nd.render_svg(\"examples/output/koch.svg\", height)\nd.render(\"examples/output/koch.png\", height)\nPILImage.open(\"examples/output/koch.png\")\n"
  },
  {
    "path": "docs/examples/lenet.py",
    "content": "from PIL import Image as PILImage\nfrom chalk import *\nfrom colour import Color\nfrom chalk import BoundingBox\n\n# Colors\npapaya = Color(\"#ff9700\")\nblue = Color(\"#005FDB\")\nblack = Color(\"#000000\")\nwhite = Color(\"#ffffff\")\ngrey = Color(\"#bbbbbb\")\n\n\n# Some general functions\n\ndef label(te):\n    \"Create text.\"\n    return text(te, 2).fill_color(black).line_width(0)\n\ndef cover(d, a, b):\n    \"Draw a bounding_box around a subdiagram\"\n    b1 = d.get_subdiagram_envelope(a)\n    b2 = d.get_subdiagram_envelope(b)\n    new_bb = b1 + b2\n    return rectangle(new_bb.width, new_bb.height) \\\n            .translate(new_bb.center.x, new_bb.center.y)\n\ndef tile(d, m, n, name = \"\"):\n    \"Tile a digram with names\"\n    return hcat(vcat(d.named((name, j, i)) for j in range(n)) for i in range(m)).center_xy()\n\ndef connect_all(d, a, b):\n    \"Connect all corners of two diagrams\"\n\n    for x_border in [-unit_x, unit_x]:\n        for y_border in [-unit_y, unit_y]:\n            p = x_border + y_border\n            d = d.connect_perim(a, b, p, p, ArrowOpts(head_arrow=empty()))\n    return d\n\n# NN drawing\ndef cell():\n    return rectangle(1, 1).line_width(0.01)\n\ndef matrix(n, r, c):\n    return tile(cell(), c, r, n)\n\ndef back(r, n):\n    \"Backing stack\"\n    return concat((r.translate(-i/2, -i/2).fill_opacity((n - i + n /2) / n)\n                   for i in range(n-1, -1, -1)))\n\nlw = 0.05\ndef stack(n, size, l, top, bot):\n    \"Feature map stack\"\n    m = matrix(n, size, size).fill_color(Color(\"#dddddd\"))\n    r = rectangle(size, size).fill_color(grey).line_width(lw)\n    return (label(top) / (back(r, l) + m) / label(bot)).center_xy()\nstack(\"a\", 32, 0, \"\", \"\")\n\ndef network(n, size, top, bot):\n    \"Draw a network layer\"\n    return (label(top) /  rectangle(2, size).fill_color(grey).line_width(lw).named(n) / label(bot)).center_xy()\n\n# The number 7\ndraw = make_path([(-10, -10), (10, -10 ), (-10, 10)]).line_width(0.09).line_color(blue).fill_opacity(0)\n\n# Draw the main diagram.\nh = hstrut(6.5)\nd = ((stack(\"a\", 32, 0, \"\", \"\") + draw) | (label(\"conv\") / h) |\n     stack(\"b\", 28, 6, \"\", \"C1\") | (label(\"pool\") / h) |\n     stack(\"c\", 14, 6, \"\", \"S2\") | (label(\"conv\") / h) |\n     stack(\"d\", 10, 16, \"\", \"C3\") | (label(\"pool\") / h) | hstrut(-0.5) |\n     stack(\"e\", 5, 16, \"\", \"S4\") | (label(\"dense\") / h) |\n     network(\"dense1\",  12, \"\", \"\") | (label(\"dense\") / (h)) |\n     network(\"dense2\",  8.4, \"\", \"\") | (label(\"dense\") / h) |\n     network(\"dense3\",  1, \"\", \"\"))\n\nd = d.scale_uniform_to_x(5)\n\n# Draw the orange boxes\nboxes = [((\"a\", 2, 2), (\"a\", 6, 6)),\n         ((\"b\", 2, 2), (\"b\", 2, 2)),\n         ((\"b\", 20, 2), (\"b\", 23, 5)),\n         ((\"c\", 10, 2), (\"c\", 11, 3)),\n         ((\"c\", 4, 6), (\"c\", 8, 10)),\n         ((\"d\", 4, 6), (\"d\", 4, 6)),\n         ((\"d\", 6, 4), (\"d\", 9, 7)),\n         ((\"e\", 3, 2), (\"e\", 4, 3))]\n\nd += concat([cover(d, *b).fill_color(papaya).fill_opacity(0.3).named((\"box\", i))\n             for i, b in enumerate(boxes)])\n\nconnect = [((\"box\", i), (\"box\", i + 1)) for i in range(0, 6, 2)]\n\n\nfor b in connect:\n    d = connect_all(d, *b)\n\nset_svg_height(300)\nd\n"
  },
  {
    "path": "docs/examples/squares.py",
    "content": "from PIL import Image as PILImage\nimport math\nimport random\n\nfrom itertools import product\n\nfrom colour import Color\nfrom chalk import square, concat, empty\n\nrandom.seed(0)\n\ndef make_square():\n    colors = [\n        Color(\"#ff9700\"),  # papaya\n        Color(\"#005FDB\"),  # blue\n    ]\n\n    # generate uniformly a value in [-max_angle, max_angle]\n    max_angle = math.pi / 24.0\n    θ = 2 * max_angle * random.random() - max_angle\n\n    # pick a random color\n    i = random.random() > 0.75\n    color = colors[i]\n\n    return square(0.75).line_color(color).rotate_rad(-θ)\nmake_square()\n\ndef make_group(num_squares=4):\n    return concat(make_square() for _ in range(num_squares))\nmake_group()\n\ndisps = range(4)\ndiagram = concat(make_group().translate(x, y) for x, y in product(disps, disps))\n\ndiagram = diagram.line_width(0.02)\ndiagram\n"
  },
  {
    "path": "docs/examples/tensor.py",
    "content": "from PIL import Image as PILImage\nfrom chalk import *\nfrom colour import Color\n\nh = hstrut(2.5)\npapaya = Color(\"#ff9700\")\nwhite = Color(\"white\")\nblack = Color(\"black\")\n\n\ndef draw_cube():\n    # Assemble cube\n    face_m = rectangle(1, 1).align_tl()\n    face_t = rectangle(1, 0.5).shear_x(-1).align_bl()\n    face_r = rectangle(0.5, 1).shear_y(-1).align_tr()\n    cube = (face_t + face_m).align_tr() + face_r\n\n    # Replace envelope with front face. \n    return cube.align_bl().with_envelope(face_m.align_bl())\n\ndef draw_tensor(depth, rows, columns):\n    \"Draw a tensor\"\n    cube  = draw_cube()\n    # Fix this ...\n    hyp = (unit_y * 0.5).shear_x(-1)\n    # Build a matrix. \n    front = cat([hcat([cube for i in range(columns)])\n                 for j in reversed(range(rows))], -unit_y).align_t()\n\n    # Build depth\n    return concat(front.translate(-k * hyp.x, -k * hyp.y)\n                  for k in reversed(range(depth)))\n\ndraw_tensor(2, 3, 4)\n\ndef t(d, r, c):\n    return draw_tensor(d, r, c).fill_color(white)\n\ndef label(te, s=1.5):\n    return (text(te, s).fill_color(black).line_color(white).center_xy())\n\n\n# Create a diagram.\nd, r, c = 3, 4, 5\nbase = t(d, r, c).line_color(papaya)\nm = hcat([t(1, r, c),  t(d, 1, c), label(\"→\"), (base + t(1, r, c)), (base + t(d, 1, c) ), label(\"=\"), t(d, r, c)], sep=2.5).line_width(0.02)\nm\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\nhide:\n  - navigation\n  - toc\n---\n--8<-- \"README.md\"\n"
  },
  {
    "path": "docs/overrides/main.html",
    "content": "{% extends \"base.html\" %}\n\n{% block content %}\n{{ super() }}\n\n<style>\n// Do whatever changes you need here\n\n.jp-RenderedHTMLCommon p {\n    color: red;\n}\n\n.jupyter-wrapper .jp-CodeCell .jp-Cell-inputWrapper .jp-InputPrompt {\n   display: none;\n}\n.jupyter-wrapper .jp-CodeCell .jp-Cell-outputWrapper .jp-OutputPrompt {\n   display: none;\n}\n\n.jupyter-wrapper .celltag_hide {\n   display: none;\n}\n\n.jupyter-wrapper .celltag_hide_inp .jp-InputArea {\n   display: none;\n}\n\n.jupyter-wrapper .jp-OutputArea-output dt {\n    width: 100%;\n}\n.jupyter-wrapper .jp-OutputArea-output dd {\n    width: 100%;\n    margin-left: 20px;\n}\n\n.jupyter-wrapper .jp-OutputArea-output.jp-RenderedSVG {\n    text-align: center;\n    padding-top: 10px;\n}\n\n</style>\n{% endblock content %}\n\n  \n"
  },
  {
    "path": "docs/quickstart/.gitkeep",
    "content": ""
  },
  {
    "path": "examples/arrows.py",
    "content": "from chalk import *\nfrom chalk.trail import Trail\nfrom colour import Color\nfrom PIL import Image as PILImage\n\ngrey = Color(\"grey\")\nblue = Color(\"blue\")\norange = Color(\"orange\")\n\noctagon = regular_polygon(8, 1.5).rotate_by(1 / 16).line_color(grey).line_width(0.5).show_origin()\ndias = octagon.named(\"first\") | hstrut(3) | octagon.named(\"second\")\n\nex1 = dias.connect(\n    \"first\",\n    \"second\",\n    ArrowOpts(trail=Trail.from_offsets([unit_x, 0.25 * unit_y, unit_x, 0.25 * unit_y]))\n)\nex1\n\nex1 = dias.connect(\n    \"first\",\n    \"second\",\n    ArrowOpts(\n        head_style=Style.empty().fill_color(grey),\n        arc_height=0.5,\n        shaft_style=Style.empty().line_color(blue),\n    ),\n)\nex1 = ex1.connect(\n    \"second\",\n    \"first\",\n    ArrowOpts(\n        head_style=Style.empty().fill_color(grey),\n        arc_height=0.5,\n        shaft_style=Style.empty().line_color(blue),\n    ),\n)\n\nex12 = ex1.connect_perim(\n    \"first\",\n    \"second\",\n    unit_x.rotate_by(15 / 16),\n    unit_x.rotate_by(9 / 16),\n    ArrowOpts(head_pad=0.1),\n)\nex3 = arrow_v(unit_y)\nd = ex12 + ex3\n\nd\n\noutput_path = \"examples/output/arrows.svg\"\nd.render_svg(output_path, height=200)\n\noutput_path = \"examples/output/arrows.png\"\nd.render(output_path, height=200)\n\nPILImage.open(output_path)\n\noutput_path = \"examples/output/arrows.pdf\"\nd.render_pdf(output_path, height=200)\n"
  },
  {
    "path": "examples/bigben.py",
    "content": "# # Big Ben\n\n# *A literate notebook by [Sasha Rush](http://www.rush-nlp.com)*\n\n# [Big Ben](https://en.wikipedia.org/wiki/Big_Ben) is the most iconic clock face in the world.\n\n# <img style=\"height:300px;width:300px;max-width:auto;\" src=\"bigben.png\">\n\n# In this notebook, we are going to replicate the design of the clockface from first principles using the\n# Chalk library. This project was done for fun without any knowledge of clockmaking or even the right\n# terminology. It is meant mainly as an introduction programmatic 2D diagramming.\n\n# Here is what it will look like when we are done.\n\n# <img style=\"height:300px;width:300px;max-width:auto;\" src=\"chalk_bigben.svg\">\n\n# ## Preliminary: Roman Numerals\n\nfrom chalk import *\nfrom colour import Color\n\n# The whole diagram a simple color pallet of gold black and a bit of grey.\n\ngold = Color(\"#E7D49C\")\nwhite = Color(\"#CCCCCC\")\nblack = Color(\"black\")\ngrey = Color(\"#444444\")\n\n\n# To begin, we will introduce some of the concepts of the Chalk library\n# by mimicking the shape of the roman numeral I, V, and X.\n\n# ![](part0.png)\n\n# Chalk uses basic shapes to build up compositional diagrams. For instance here\n# is a filled rectangle.\n\ncolumn = rectangle(1, 4).fill_color(black)\ncolumn\n\n# Each diagram has style properties and an \"envelope\" that describes\n# its boundaries. Envelopes are a bit complex, they roughly are the\n# the bounding box of the diagram.\n\ncolumn.show_envelope()\n\n# Next lets make a diamond with an inlay. To do this we use `+` to put a\n# grey box on a black box.\n\ndiamond = rectangle(1, 1).fill_color(black) + rectangle(0.5, 0.5).fill_color(grey)\ndiamond\n\n# The benefit of the envelope representation is that it behaves more\n# intuitively under affine transformations like rotation.\n\ndiamond = diamond.rotate_by(1 / 8)\ndiamond.show_envelope()\n\n\n# We can easily combine diagrams. To see what a combination will look\n# like we can use the `show_beside` method. You give it a vector along\n# which to combine.\n\ncolumn.show_beside(diamond, -unit_y)\n\n# We can also update the envelope of diagrams before combination.\n# For instance here we substitute a small envelope for overlap.\n\ncolumn = column.with_envelope(rectangle(1, 2.5))\ncolumn.show_beside(diamond, -unit_y)\n\n# When stacking on top we can use the shortcut `/`. The function `center_xy` resets\n# the center to the middle of the envelope.\n\ni = ((column / diamond).beside(diamond, -unit_y)).center_xy()\ni = i + rectangle(0.01, 4).line_color(grey)\ni.show_envelope()\n\n# We can also put diagrams next to each other using the `|` notation.\n\nii = i | i\nii\n\n# We can similar create diagrams for the other roman numerals.\n# The `align` functions also re-center diagrams.\n\nv = rectangle(1.5, 1).fill_color(black).align_bl() + i.align_b()\nv\n\n\n# Changing the center allows us to use `+` which joins diagrams together at the origin.\n\n\nv = (v.align_br() + i.align_b()).center_xy()\nv\n\n\n# Creating X is a bit harder. We use two transformations to help us out.\n# Using `translate` helps us nudge diagrams away from the origin.\n\n\nddiamond = (diamond | diamond).translate(-0.5, 0)\nddiamond.show_origin()\n\n# Using `shear` lets us create a center line with a diagonal slash.\n\nmid = (\n    rectangle(2, 0.5).fill_color(black) + rectangle(1.5, 0.1).fill_color(grey)\n).shear_x(-0.2)\nmid\n\n\n# We can then combine these complex diagrams together.\ncolumn = column\nx = ((column / ddiamond) + mid).beside(ddiamond, -unit_y).center_xy()\nx\n\n\n# Compositionality is fun. We can take these shapes and make numbers from 1-12.\n\nnumbers = [\n    x | i | i,\n    i,\n    i | i,\n    i | i | i,\n    i | v,\n    v,\n    v | i,\n    v | i | i,\n    v | i | i | i,\n    i | x,\n    x,\n    x | i,\n]\n\n\n# We can draw the main clock-face by moving each number to the edge, and then rotating it\n# to its location. The `concat` combinator glues each of these together at the origin.\n\n\npart0 = concat(\n    [\n        n.center_xy().scale(0.05).translate_by(-unit_y).rotate_by(-i / 12)\n        for i, n in enumerate(numbers)\n    ]\n)\n\nset_svg_height(300)\npart0.show_origin()\n\n\n# ## Inner Pattern\n\n# ![](part1.png)\n\n# This inner pattern is a bit more complex. We are going to need more than just\n# simple shapes to draw it.\n\n# To start, let us make a function for rotational symmetry.\n\n\ndef rot_cycle(d: Diagram, times: int) -> Diagram:\n    \"Rotate diagram around a circle.\"\n    return concat(d.rotate_by(i / times) for i in range(times))\n\n\n# To try it out, we make the inner pattern by making a circle and\n# rotating it around 12 times.\n\nset_svg_height(200)\n\nwidth = -4.4\ninner_circle = rot_cycle(circle(1.1).translate(0, width), 12).rotate_by(\n    (1 / 12) / 2\n) + circle(3).fill_color(black)\ninner_circle\n\n\n# Now we want to trace a trail that looks like the inner patttern.\n# There is no magic here, just a little geometry to guess the shapes.\n# Vectors `unit_y` and `unit_x` are geometric helpers.\n\nu45 = unit_x.rotate(-45)\nu60 = unit_x.rotate(60)\ndiffy = abs(u45.y / u60.y)\ndiffx = diffy * abs(u60.x / u45.x)\n\n# A `Trail` is a sequence of vectors drawn in order.\n# Once you are done drawing one you can use `stroke` to\n# make it a diagram. We start at the top left and draw\n# downward.\n\nfudge = 0.73\ny = (\n    Trail.from_offsets(\n        [\n            u45,\n            diffy * u60,\n            -diffy * u60,\n            -fudge * u60,\n            fudge * u60,\n            diffx * u45,\n            diffx * unit_y,\n        ]\n    )\n    .stroke()\n    .align_br()\n)\ny.show_origin()\n\n\n# To draw the curve we use `arc_between` which\n# connects two points with a specified radius.\n\ncurve = 0.5\nunder_arc = arc_between(-unit_x, 2 * -unit_y, curve).align_tr()\nunder_arc.show_origin()\n\n# We then combine them to the right scale.\n\npattern = (y.scale(3) / under_arc).align_r()\npattern.show_origin()\n\n# And then use reflection to double the pattern.\n\npattern = (pattern + pattern.reflect_x()).align_b()\npattern\n\n\n# We can then rotate this to create the whole pattern. We set the\n# fudge factor above to make the pattern connect.\n\nset_svg_height(300)\npart1 = inner_circle + rot_cycle(pattern.translate(0, width - 1), 12)\npart1 = part1.line_color(gold).line_width(0.2)\npart1\n\n# Looks pretty close to the original!\n\n# ![](part1.png)\n\n\n# ## Outer Bands\n\n\n# ![](part2.png)\n\n\n# With the functions we have so far it is not so hard to do the rest of the main\n# clock-face. The maink point worth noting is that we can build each part without\n# needing to know the sizes of the others. This makes it easy to debug and update.\n\n\n# The first band is two circles with a black dots. Nothing new.\n\nset_svg_height(200)\n\nband1 = (circle(1.1).line_width(0.1) + circle(1)).line_width(0.2) + rot_cycle(\n    diamond.scale(0.05).translate(1.05, 0), 12\n)\nband1\n\n\n# Band two has thin dividing lines and the hour markers from part 0.\n# In order to fit in the numbers we write a function that lets us scale to a given circle.\n\n\ndef fit_in(b: Diagram, s: Diagram, frame=0.1) -> Diagram:\n    # Find the inner radius\n    m = min([x for x in b.get_trace()(origin, unit_x) if x > 0])\n\n    # Scale the inner diagram to that size\n    return b + s.scale_uniform_to_x(2 * m - frame)\n\n\n# We use this to put the numbers in a circle.\n\nband2 = fit_in(circle(1.4), part0, 0.1) + circle(1)\nband2\n\n# We then draw thin lines in this region.\n\n\ndef thin_line(h):\n    return rectangle(h, 0.001).fill_color(black).center_xy().line_width(0.01)\n\n\nlines = thin_line(0.4)\nband2 = band2 + rot_cycle(lines.translate(1.2, 0), 48)\nband2\n\n\n# Band 3 has a little jewel cross. We can draw this with shapes.\n\ndiamond = rectangle(1, 1).rotate_by(1 / 8).fill_color(black).line_color(black)\ns = (\n    (diamond | rectangle(2, 1).with_envelope(rectangle(1, 1)) | diamond)\n    .fill_color(black)\n    .line_color(black)\n    .center_xy()\n)\ns = s.scale_y(0.5) + s.rotate_by(0.25).scale_y(0.75).scale_x(0.25)\njewel = s.rotate_by(0.25)\njewel\n\n\n# Draw the outlines\n\na = 1.8\nc = 1.7\nb = 1.6\nband3 = (\n    circle(2.0).line_width(0.7)\n    + (circle(a) + circle(b)).line_width(0.3)\n    + circle(1.4).line_width(0.4)\n)\nband3\n\n# Add the thin lines and rounded rect tracks\n\ntrack = rectangle(0.33, 0.001, 0.1).fill_color(black).center_xy().line_width(0.01)\nband3 = (\n    band3\n    + rot_cycle(track.line_width(0.3).translate(c, 0), 60)\n    + rot_cycle(thin_line(0.6).translate(c, 0).rotate_by(1 / 48), 48)\n)\nband3\n\n# Add the jewel.\n\nband3 = band3 + rot_cycle(\n    jewel.center_xy().scale_uniform_to_x((a - b) * 2).translate(0, c), 12\n)\nband3\n\n# And voila.\n\npart2 = fit_in(band3, fit_in(band2, band1))\nset_svg_height(300)\npart2\n\n# Looks pretty close to the original!\n\n# ![](part2.png)\n\n# ## Frame\n\n# !<img style=\"height:300px;width:300px;max-width:auto;\" src=\"bigben.png\">\n\n\n# The whole clock is surrounded by a thick gold frame with some ornamentation. We start with the outer box.\n\nset_svg_height(200)\nr = rectangle(1, 1).fill_color(black).line_color(gold).line_width(0.6)\nr\n\n# Next we do each of the outer corners by themselves. We first make a sloping triangle shape using trails and arc_between.\n\ncorner = (\n    Trail.hrule(1).stroke().align_l()\n    + Trail.vrule(1).reflect_y().stroke()\n    + arc_between((0, -1), (1, 0), -0.2)\n).line_width(0.2)\ncorner\n\n\n# Internally there is a little golden decoration. To make this we use the `juxtapose` function\n# which moves a diagram to be next to another along an angle.\n\ndecal = rectangle(2, 2).with_envelope(rectangle(0, 0)).fill_color(gold)\nc = circle(1).fill_color(black)\ndecal = concat(\n    [\n        decal.juxtapose(c, unit_y),\n        decal.juxtapose(c, -unit_y),\n        decal.juxtapose(c, unit_x),\n        decal.juxtapose(c, -unit_x),\n        decal,\n    ]\n)\ndecal.show_origin()\n\n\n# We then add a black circle behind it to emphasize details.\n\ndecal = decal.line_color(gold).line_width(0.2)\ndecal = circle(2).fill_color(black) + decal\ndecal\n\n# To add the other shapes with we `arc` which draws part of a circle.\n\ndisp = 20  # degrees\nmarc = arc(2 / 2, 0, 180 - disp)\ndecal = concat(\n    [\n        decal,\n        decal.juxtapose(marc, unit_x.rotate(90 + disp)),\n        decal.juxtapose(marc.rotate(-(90 + disp)), unit_x.rotate(-disp)),\n    ]\n).line_width(0.2)\ndecal\n\n\n# We scale the decoration to fit in the corner we created.\n\nfudge = 0.515\ncorner = corner.align_bl() + decal.scale_uniform_to_x(fudge).align_bl()\ncorner = corner.align_tr().translate(-0.4, 0.4).line_color(gold)\ncorner.show_origin()\n\n# The corners are rotationally symmetric.\n\ncorner4 = rot_cycle(corner, 4)\ncorner4\n\n# Putting it together gives the outer frame.\n\nset_svg_height(300)\ninner = (\n    circle(1)\n    .line_width(0.3)\n    .line_color(gold)\n    .fill_color(black)\n    .scale_uniform_to_x(1 - 0.04)\n)\npart3 = fit_in(r, corner4, 0.05) + inner\npart3\n\n\n# !<img style=\"height:300px;width:300px;max-width:auto;\" src=\"bigben.png\">\n\n# ## Clock Hands\n\n# ![](part1.png)\n\n\n# To make the clock hands we just trace a path. We `make_path` and give\n# it a list of coordinates. We then reflect since it is symmetric.\n\nset_svg_height(200)\nhand = (\n    make_path([(2, -0.5), (1, -0), (0.4, 20), (0, 21), (0, -1.5), (0.5, -1), (2, -0.5)], closed=True)\n    .fill_color(black)\n    .line_color(grey)\n)\nhand = (hand + hand.reflect_x()).translate(0, -4).line_width(0.1)\nhand.show_origin()\n\nhand2 = make_path(\n    [\n        (1, 0),\n        (1, 7),\n        (2, 7),\n        (2.5, 8),\n        (2, 8.5),\n        (1, 9.5),\n        (0.3, 11),\n        (0, 12),\n        (0, 0),\n        (1, 0),\n    ],\n    closed=True\n).fill_color(black)\nhand2 = (hand2 + hand2.reflect_x()).translate(0, -3).line_width(0.1).line_color(grey)\nhand2.show_origin()\n\n# We then overlay them as in the picture at the right scales.\n\nset_svg_height(300)\npart4 = hand2.scale_uniform_to_y(0.5).rotate_by(0.07) + hand.scale_uniform_to_y(1.0)\npart4\n\n\n# ## All Together\n\n# Our final picture overlays each of the three parts using our fitting functions.\n# Each part was done separately, but they all click together to make the final image.\n\nfinal = (\n    part3\n    + fit_in(inner, (fit_in(part2, part1)), 0.0)\n    + part4.scale_x(0.8).scale(0.55).rotate_by(0.10)\n)\nfinal\n\nfinal.render_svg(\"chalk_bigben.svg\", height=300)\n\n# <img style=\"height:300px;width:300px;max-width:auto;\" src=\"chalk_bigben.svg\">  <img style=\"height:300px;width:300px;max-width:auto;\" src=\"bigben.png\">\n"
  },
  {
    "path": "examples/comparison.tex",
    "content": "\\documentclass{article}%\n\\usepackage[T1]{fontenc}%\n\\usepackage[utf8]{inputenc}%\n\\usepackage{lmodern}%\n\\usepackage{fullpage}%\n\\usepackage{textcomp}%\n\\usepackage{lastpage}%\n\\usepackage{graphicx}%\n%\n%\n%\n\\begin{document}%\n\n\n\\section{Intro}\n\n\n\\includegraphics[width=100pt]{examples/output/intro-01.png}\n\\includegraphics[width=100pt]{examples/output/intro-01.pdf}\n\n\\includegraphics[width=100pt]{examples/output/intro-02.png}\n\\includegraphics[width=100pt]{examples/output/intro-02.pdf}\n\n\n\\includegraphics[width=100pt]{examples/output/intro-03.png}\n\\includegraphics[width=100pt]{examples/output/intro-03.pdf}\n\n\n\\includegraphics[width=100pt]{examples/output/intro-04.png}\n\\includegraphics[width=100pt]{examples/output/intro-04.pdf}\n\n\\pagebreak\n\\section{LeNet}\n\n\\includegraphics[width=\\textwidth]{examples/output/lenet.png}\n\n\\includegraphics[width=\\textwidth]{examples/output/lenet.pdf}\n\n\\pagebreak\n\\section{Squares}\n\n\\includegraphics[width=\\textwidth]{examples/output/squares.png}\n\n\\includegraphics[width=\\textwidth]{examples/output/squares.pdf}\n\n\\pagebreak\n\\section{Tensor}\n\n\\includegraphics[width=\\textwidth]{examples/output/tensor.png}\n\n\\includegraphics[width=\\textwidth]{examples/output/tensor.pdf}\n\n\\pagebreak\n\n\\section{Logo}\n\n\\includegraphics[width=\\textwidth]{examples/output/logo.png}\n\n\\includegraphics[width=\\textwidth]{examples/output/logo.pdf}\n\\pagebreak\n\n\n\\section{Hanoi}\n\n\\includegraphics[width=0.6\\textwidth]{examples/output/hanoi.png}\n\n\\includegraphics[width=0.6\\textwidth]{examples/output/hanoi.pdf}\n\\pagebreak\n\n\\section{Escher}\n\n\\includegraphics[width=\\textwidth]{examples/output/escher-square-limit.png}\n\n\\includegraphics[width=\\textwidth]{examples/output/escher-square-limit.pdf}\n\\pagebreak\n\n\\section{Hex}\n\n\\includegraphics[width=\\textwidth]{examples/output/hex-variation.png}\n\n\\includegraphics[width=\\textwidth]{examples/output/hex-variation.pdf}\n\\pagebreak\n\n\\end{document}\n"
  },
  {
    "path": "examples/escher_square_limit.py",
    "content": "# This example is based on a corresponding example from Lisp by Frank Buss:\n# https://frank-buss.de/lisp/functional.html\n# A more general implementation is provided by Jeremy Gibbons using diagrams in Haskell:\n# https://archives.haskell.org/projects.haskell.org/diagrams/gallery/SquareLimit.html\n\nimport math\n\nfrom toolz import take, iterate  # type: ignore\n\nfrom chalk import concat, make_path, square, strut\n\n\n# fmt: off\nmarkings = {\n    \"p\": [\n        [(4, 4), (6, 0)],\n        [(0, 3), (3, 4), (0, 8), (0, 3)],\n        [(4, 5), (7, 6), (4, 10), (4, 5)],\n        [(11, 0), (10, 4), (8, 8), (4, 13), (0, 16)],\n        [(11, 0), (14, 2), (16, 2)],\n        [(10, 4), (13, 5), (16, 4)],\n        [(9, 6), (12, 7), (16, 6)],\n        [(8, 8), (12, 9), (16, 8)],\n        [(8, 12), (16, 10)],\n        [(0, 16), (6, 15), (8, 16), (12, 12), (16, 12)],\n        [(10, 16), (12, 14), (16, 13)],\n        [(12, 16), (13, 15), (16, 14)],\n        [(14, 16), (16, 15)],\n    ],\n    \"q\": [\n        [(2, 0), (4, 5), (4, 7)],\n        [(4, 0), (6, 5), (6, 7)],\n        [(6, 0), (8, 5), (8, 8)],\n        [(8, 0), (10, 6), (10, 9)],\n        [(10, 0), (14, 11)],\n        [(12, 0), (13, 4), (16, 8), (15, 10), (16, 16), (12, 10), (6, 7), (4, 7), (0, 8)],\n        [(13, 0), (16, 6)],\n        [(14, 0), (16, 4)],\n        [(15, 0), (16, 2)],\n        [(0, 10), (7, 11)],\n        [(9, 12), (10, 10), (12, 12), (9, 12)],\n        [(8, 15), (9, 13), (11, 15), (8, 15)],\n        [(0, 12), (3, 13), (7, 15), (8, 16)],\n        [(2, 16), (3, 13)],\n        [(4, 16), (5, 14)],\n        [(6, 16), (7, 15)],\n    ],\n    \"r\": [\n        [(0, 12), (1, 14)],\n        [(0, 8), (2, 12)],\n        [(0, 4), (5, 10)],\n        [(0, 0), (8, 8)],\n        [(1, 1), (4, 0)],\n        [(2, 2), (8, 0)],\n        [(3, 3), (8, 2), (12, 0)],\n        [(5, 5), (12, 3), (16, 0)],\n        [(0, 16), (2, 12), (8, 8), (14, 6), (16, 4)],\n        [(6, 16), (11, 10), (16, 6)],\n        [(11, 16), (12, 12), (16, 8)],\n        [(12, 12), (16, 16)],\n        [(13, 13), (16, 10)],\n        [(14, 14), (16, 12)],\n        [(15, 15), (16, 14)],\n    ],\n    \"s\": [\n        [(0, 0), (4, 2), (8, 2), (16, 0)],\n        [(0, 4), (2, 1)],\n        [(0, 6), (7, 4)],\n        [(0, 8), (8, 6)],\n        [(0, 10), (7, 8)],\n        [(0, 12), (7, 10)],\n        [(0, 14), (7, 13)],\n        [(8, 16), (7, 13), (7, 8), (8, 6), (10, 4), (16, 0)],\n        [(10, 16), (11, 10)],\n        [(10, 6), (12, 4), (12, 7), (10, 6)],\n        [(13, 7), (15, 5), (15, 8), (13, 7)],\n        [(12, 16), (13, 13), (15, 9), (16, 8)],\n        [(13, 13), (16, 14)],\n        [(14, 11), (16, 12)],\n        [(15, 9), (16, 10)],\n    ],\n}\n# fmt: on\n\nnames = \"pqrs\"\nblank = strut(1, 1)\nθ = 90\n\n\ndef normalize(coords):\n    def center(val: float) -> float:\n        return (val - 8) / 16\n\n    return [(center(x), -center(y)) for x, y in coords]\n\n\ndef make_tile(name):\n    return concat(make_path(normalize(coords)) for coords in markings[name])\n\ndef quartet(tl, tr, bl, br):\n    diagram = (tl | tr) / (bl | br)\n    return diagram.center_xy().scale(0.5)\n\n\ndef cycle(diagram):\n    tl = diagram\n    tr = diagram.rotate(θ).rotate(θ).rotate(θ)\n    bl = diagram.rotate(θ)\n    br = diagram.rotate(θ).rotate(θ)\n    return quartet(tl, tr, bl, br)\n\nfish = {name: make_tile(name) for name in names}\nfish_t = quartet(fish[\"p\"], fish[\"q\"], fish[\"r\"], fish[\"s\"])\nfish_u = cycle(fish[\"q\"].rotate(θ))\n\nside_1 = quartet(blank, blank, fish_t.rotate(θ), fish_t)\n\nside_2 = quartet(side_1, side_1, fish_t.rotate(θ), fish_t)\ncorner_1 = quartet(blank, blank, blank, fish_u)\n\ncorner_2 = quartet(corner_1, side_1, side_1.rotate(θ), fish_u)\npseudocorner = quartet(corner_2, side_2, side_2.rotate(θ), fish_t.rotate(θ))\n\n\npseudolimit = cycle(pseudocorner).line_width(0.05)\n\noutput_path = \"examples/output/escher-square-limit.png\"\npseudolimit.render(output_path, height=512)\n\n# SVG render\noutput_path = \"examples/output/escher-square-limit.svg\"\npseudolimit.render_svg(output_path, height=512)\n\n\noutput_path = \"examples/output/escher-square-limit.pdf\"\npseudolimit.render_pdf(output_path, height=512)\n"
  },
  {
    "path": "examples/hanoi.py",
    "content": "# Based on the following example from Diagrams\n# https://archives.haskell.org/projects.haskell.org/diagrams/gallery/Hanoi.html\n\nfrom PIL import Image as PILImage\nfrom typing import Dict, List, Tuple\n\nfrom colour import Color  # type: ignore\nfrom chalk import Diagram, rectangle, concat, hcat, vcat\n\n\nDisk = int\nStack = List[Disk]  # disks on one peg\nHanoi = List[Stack]  # disks on all three pegs\nMove = Tuple[int, int]\n\ncolors: List[Color] = [\n    Color(\"#9FB4CC\"),\n    Color(\"#CCCC9F\"),\n    Color(\"#DB4105\"),\n]\nblack = Color(\"black\")\n\n\ndef draw_disk(n: Disk) -> Diagram:\n    return (\n        rectangle(n + 2, 1)\n        .fill_color(colors[n])\n        .line_color(colors[n])\n        .line_width(0.05)\n    )\ndraw_disk(0)\n\n\ndef draw_stack(s: Stack) -> Diagram:\n    disks = vcat(map(draw_disk, s))\n    post = rectangle(0.8, 6).fill_color(black)\n    return post.align_b() + disks.align_b()\ndraw_stack([0, 1])\n\n\ndef draw_hanoi(state: Hanoi) -> Diagram:\n    hsep = 7\n    return concat(\n        draw_stack(stack).translate(7 * i, 0) for i, stack in enumerate(state)\n    )\ndraw_hanoi([[0], [1, 2], []])\n\ndef solve_hanoi(num_disks: int) -> List[Move]:\n    def solve_hanoi_1(num_disks, *, source, spare, target):\n        if num_disks <= 0:\n            return []\n        else:\n            return (\n                solve_hanoi_1(num_disks - 1, source=source, spare=target, target=spare)\n                + [(source, target)]\n                + solve_hanoi_1(num_disks - 1, source=spare, spare=source, target=target)\n            )\n\n    return solve_hanoi_1(num_disks, source=0, spare=1, target=2)\n\n\ndef do_move(move: Move, state: Hanoi) -> Hanoi:\n    def remove_disk(src, state):\n        disk, *src_new = state[src]\n        state_new = state[:src] + [src_new] + state[src + 1 :]\n        return disk, state_new\n\n    def add_disk(tgt, disk, state):\n        tgt_new = [disk] + state[tgt]\n        state_new = state[:tgt] + [tgt_new] + state[tgt + 1 :]\n        return state_new\n\n    src, tgt = move\n    disk, state1 = remove_disk(src, state)\n    state2 = add_disk(tgt, disk, state1)\n    return state2\n\n\ndef state_sequence(num_disks: int) -> List[Hanoi]:\n    state: Hanoi = [list(range(num_disks)), [], []]\n    states: List[Hanoi] = [state]\n    for move in solve_hanoi(num_disks):\n        state = do_move(move, state)\n        states.append(state)\n    return states\n\n\ndef draw_state_sequence(seq: List[Hanoi]) -> Diagram:\n    return concat(draw_hanoi(state).translate(0, 7.5 * i) for i, state in enumerate(seq))\n\n\ndiagram = draw_state_sequence(state_sequence(3))\n\n\npath = \"examples/output/hanoi.svg\"\ndiagram.render_svg(path, height=700)\n\n\ntry: \n    path = \"examples/output/hanoi.png\"\n    diagram.render(path, height=700)\n    PILImage.open(path)\n\n    path = \"examples/output/hanoi.pdf\"\n    diagram.render_pdf(path, height=700)\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n"
  },
  {
    "path": "examples/hex_variation.py",
    "content": "import math\nimport random\n\nfrom itertools import product\n\nfrom chalk import *\n\nrandom.seed(1337)\n\n\nh = math.sqrt(3) / 2\nh1 = math.cos(math.pi / 3)\n\n\ndef hexagon_tile():\n    arc1 = arc(0.5, -from_radians(math.pi / 3), from_radians(math.pi / 3))\n    return (\n        arc1.translate(-1, 0)\n        + vrule(2 * h)\n        + arc1.rotate_by(1 / 2).translate(1, 0)\n        # + polygon(6, 1)\n    )\n\ndef rotated_hexagon_tile(n):\n    return hexagon_tile().rotate_rad(-n * 2 * math.pi / 3)\n\ndef center_position(x, y):\n    if x % 2 == 0:\n        return (2 - h1) * x, 2 * y * h\n    else:\n        return (2 - h1) * x, (2 * y - 1) * h\n\n\ndef hex_variation(num_tiles):\n    rows = list(range(num_tiles))\n    cols = list(range(num_tiles))\n    get_angle = lambda: random.randint(0, 2)\n    diagrams = [rotated_hexagon_tile(get_angle()) for _ in product(rows, cols)]\n    grid = [center_position(x, y) for x, y in product(rows, cols)]\n    return place_at(diagrams, grid)\n\n\ndia = hex_variation(12).line_width(0.05)\ndia = dia.rotate_by(-1 / 4)\n\ndia.render_svg(\"examples/output/hex-variation.svg\", height=512)\ntry:\n    dia.render(\"examples/output/hex-variation.png\", height=512)\n    dia.render_pdf(\"examples/output/hex-variation.pdf\", height=512)\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n\n"
  },
  {
    "path": "examples/hilbert.py",
    "content": "# Based on the following example from Diagrams\n# https://archives.haskell.org/projects.haskell.org/diagrams/gallery/Hilbert.html\n\nfrom PIL import Image as PILImage\nfrom chalk import *\nfrom chalk.transform import *\n\nunit_x, unit_y = Trail.hrule(1), Trail.vrule(1)\n\n# Draw a space filling Hilbert curve\n\ndef hilbert(n):\n    def hilbert2(m): return hilbert(m).rotate_by(0.25)\n    if n == 0: return Trail.empty()\n    h, h2 = hilbert(n -1), hilbert2(n-1)\n    return (h2.reflect_y() + unit_y\n            + h + unit_x\n            + h + unit_y.reflect_y()\n            + h2.reflect_x())\n\nd = hilbert(5).stroke().center_xy().line_width(0.05)\nd.render_svg(\"examples/output/hilbert.svg\", 500)\ntry: \n    d.render_pdf(\"examples/output/hilbert.pdf\", 500)\n    d.render(\"examples/output/hilbert.png\", 500)\n    PILImage.open(\"examples/output/hilbert.png\")\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n"
  },
  {
    "path": "examples/intro.py",
    "content": "from colour import Color\nfrom chalk import *\n\n# define some colors\npapaya = Color(\"#ff9700\")\nblue = Color(\"#005FDB\")\n\n\npath = \"examples/output/intro-01.png\"\nd = circle(1).fill_color(papaya)\nd.render(path, height=64)\n\n\n# # Alternative, render as svg\npath = \"examples/output/intro-01.svg\"\nd.render_svg(path, height=64)\n\n# Alternative, render as pdf\npath = \"examples/output/intro-01.pdf\"\nd.render_pdf(path, height=64)\n\n\npath = \"examples/output/intro-02.png\"\nd = circle(0.5).fill_color(papaya) | square(1).fill_color(blue)\nd.render(path, height=64)\n\npath = \"examples/output/intro-02.svg\"\nd.render_svg(path, height=64)\n\npath = \"examples/output/intro-02.pdf\"\nd.render_pdf(path)\n\npath = \"examples/output/intro-03.png\"\nd = hcat(circle(0.1 * i) for i in range(1, 6)).fill_color(blue)\nd.render(path, height=64)\n\n# Alternative, render as svg\npath = \"examples/output/intro-03.svg\"\nd.render_svg(path, height=64)\n\n# Alternative, render as pdf\npath = \"examples/output/intro-03.pdf\"\nd.render_pdf(path)\n\npath = \"examples/output/intro-04.png\"\n\ndef sierpinski(n: int, size: int) -> Diagram:\n    if n <= 1:\n        return triangle(size)\n    else:\n        smaller = sierpinski(n - 1, size / 2)\n        return smaller.above((smaller | smaller).center_xy())\n\nd = sierpinski(5, 4).fill_color(papaya)\nd.render(path, height=256)\n\npath = \"examples/output/intro-04.svg\"\nd.render_svg(path, height=256)\n\npath = \"examples/output/intro-04.pdf\"\nd.render_pdf(path, height=256)\n"
  },
  {
    "path": "examples/isometric.py",
    "content": "from dataclasses import dataclass\nfrom PIL import Image as PILImage\nfrom chalk import *\nfrom colour import Color\nimport numpy as np\nfrom numpy.typing import ArrayLike\n\nh = hstrut(2.5)\npapaya = Color(\"#ff9700\")\nwhite = Color(\"white\")\nblack = Color(\"black\")\n\n\ndef lookAt(eye: ArrayLike, center: ArrayLike, up: ArrayLike):\n    \"Python version of the haskell lookAt function in linear.projections\"\n    f = (center - eye) / np.linalg.norm(center - eye)\n    s = np.cross(f, up) / np.linalg.norm(np.cross(f, up))\n    u = np.cross(s, f)\n    return np.array([[*s, 0], [*u, 0], [*-f, 0], [0, 0, 0, 1]])\n\n\ndef scale3(x, y, z):\n    return np.array([[x, 0, 0, 0], [0, y, 0, 0], [0, 0, z, 0], [0, 0, 0, 1]])\n\n\n@dataclass\nclass D3:\n    x: float\n    y: float\n    z: float\n\n    def to_np(self):\n        return np.array([self.x, self.y, self.z])\n\n\nV3 = D3\n\n\ndef homogenous(trails: List[List[D3]]):\n    \"Convert list of directions to a np.array of homogenous coordinates\"\n    return np.array([[[*o.to_np(), 1] for o in offsets] for offsets in trails])\n\n\ndef cube():\n    \"3 faces of a cube drawn as offsets from the origin.\"\n    return homogenous(\n        [\n            [D3(*v) for v in offset]\n            for offset in [\n                [(1, 0, 0), (0, 1, 0), (-1, 0, 0), (0, -1, 0)],\n                [(1, 0, 0), (0, 0, 1), (-1, 0, 0), (0, 0, -1)],\n                [(0, 0, 1), (0, 1, 0), (0, 0, -1), (0, -1, 0)],\n            ]\n        ]\n    )\n\n\ndef to_trail(trail: ArrayLike, locations: ArrayLike):\n    return [\n        (\n            Path(\n                [Trail.from_offsets([V2(*v[:2]) for v in trail]).close().at(V2(*l[:2]))]\n            ),\n            l[2],\n        )\n        for l in locations\n    ]\n\n\ndef project(projection, shape3, positions):\n    p = homogenous([positions for _ in range(shape3.shape[0])])\n    locations = p @ projection.T\n    trails = shape3 @ projection.T\n    return [out for t, l in zip(trails, locations) for out in to_trail(t, l)]\n\n\n# Create Data\nx = np.random.rand(20, 30, 40) > 0.9\na, b, c = x.nonzero()\n\n# Big Cube\ns = scale3(*x.shape)\nbig_cube = cube() @ s.T\ns_ = x.shape\n\n# Isometric projection of tensor\nprojection = lookAt(\n    V3(s_[1] + s_[2], s_[0] + s_[2], s_[0] + s_[1]).to_np(),\n    V3(0, 0, 0).to_np(),\n    V3(0, 0, 1).to_np(),\n)\nouter = project(projection, big_cube, [V3(0, 0, 0)])\nd = (\n    concat([p.stroke().fill_color(white).fill_opacity(0.1) for p, _ in outer])\n    .line_width(0.2)\n    .line_color(white)\n)\n\ncubes = project(projection, cube(), [V3(x, y, z) for x, y, z in zip(a, b, c)])\ncubes.sort(key=lambda x: x[1], reverse=True)\nd2 = concat([p.stroke() for p, _ in cubes])\nd = d2.fill_color(papaya).fill_opacity(0.9).line_width(0.05).with_envelope(d) + d\nd.render(\"output/tensor2.png\", 500)\nd.render_svg(\"output/tensor2.svg\", 500)\n"
  },
  {
    "path": "examples/koch.py",
    "content": "# Based on the following example from Diagrams\n# https://archives.haskell.org/projects.haskell.org/diagrams/gallery/Koch.html\n\nfrom PIL import Image as PILImage\nfrom chalk import *\nfrom chalk.transform import *\nimport chalk\n\nunit_x = Trail.hrule(1)\n\ndef koch(n):\n    if n == 0:\n        return unit_x.scale_x(5)\n    else:\n        return (\n            koch(n - 1).scale(1 / 3)\n            + koch(n - 1).scale(1 / 3).rotate_by(+1 / 6)\n            + koch(n - 1).scale(1 / 3).rotate_by(-1 / 6)\n            + koch(n - 1).scale(1 / 3)\n        )\n\nd = vcat(koch(i).stroke().line_width(0.01) for i in range(1, 5))\n\n\n# Render\nheight = 512\nd.render_svg(\"examples/output/koch.svg\", height)\ntry:\n    d.render(\"examples/output/koch.png\", height)\n    d.render_pdf(\"examples/output/koch.pdf\", height)\n    PILImage.open(\"examples/output/koch.png\")\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n"
  },
  {
    "path": "examples/latex.py",
    "content": "from chalk import *\nfrom colour import Color\n\n\ngrey = Color(\"#bbbbbb\")\npapaya = Color(\"#ff9700\")\n\nleft_arrow = make_path([(0, 0), (1, 0)]).reflect_x().line_width(0.03).center_xy()\ndef box(t):\n    return rectangle(1.5, 1).line_width(0.05).fill_color(papaya) + latex(t).scale(0.7)\n\ndef label(text):\n    return latex(text).scale(0.5).pad(0.4)\n\ndef arrow(text, d=True):\n    return label(text) // left_arrow\n\n# Autograd 1\nd = hcat([arrow(r\"$f'_x(g(x))$\"), box(\"$f$\"), arrow(r\"$f'_{g(x)}(g(x))$\"), box(\"$g$\"), arrow(\"1\")], 0.2)\nd.render_svg(\"examples/output/latex.svg\", 100)\n"
  },
  {
    "path": "examples/lattice.py",
    "content": "import sys\n\nfrom chalk import *\nfrom colour import Color\n\nsys.setrecursionlimit(100_000)\n\nBLACK = Color(\"black\")\nSTEPS = 7\nNODES = 7\n\n\ndef node(i, j):\n    name = Name((i, j))\n    r = rectangle(1, 0.4, 0.1).named(name)\n    t = text(f\"Node {i} {j}\", 0.2).fill_color(BLACK)\n    return r + t\n\n\nd = hcat([vcat([node(i, j) for j in range(NODES)], sep=1) for i in range(STEPS)], sep=2)\n\nfor i in range(NODES - 1):\n    for j in range(STEPS):\n        for j2 in range(STEPS):\n            src = Name((i, j))\n            tgt = Name((i + 1, j2))\n            d = d.connect_perim(src, tgt, unit_x, -unit_x)\n\n\npath = \"examples/output/lattice.svg\"\nd.render_svg(path, height=256)\n\npath = \"examples/output/lattice.png\"\nd.render(path, height=256)\n"
  },
  {
    "path": "examples/lenet.py",
    "content": "from PIL import Image as PILImage\nfrom chalk import *\nfrom colour import Color\nfrom chalk import BoundingBox\n\n# Colors\npapaya = Color(\"#ff9700\")\nblue = Color(\"#005FDB\")\nblack = Color(\"#000000\")\nwhite = Color(\"#ffffff\")\ngrey = Color(\"#bbbbbb\")\n\n\n# Some general functions\n\ndef label(te):\n    \"Create text.\"\n    return text(te, 2).fill_color(black).line_width(0)\n\ndef cover(d, a, b, n):\n    \"Draw a bounding_box around a subdiagram\"\n    e1 = d.get_subdiagram(a).get_envelope()\n    e2 = d.get_subdiagram(b).get_envelope()\n    envelope = e1 + e2\n    bbox = rectangle(envelope.width, envelope.height)\n    return bbox.named(n).translate(envelope.center.x, envelope.center.y)\n\ndef tile(d, m, n, name=\"\"):\n    \"Tile a digram with names\"\n    return hcat(vcat(d.named((name, j, i)) for j in range(n)) for i in range(m)).center_xy()\n\ndef connect_all(d, a, b):\n    \"Connect all corners of two diagrams\"\n    for x_border in [-unit_x, unit_x]:\n        for y_border in [-unit_y, unit_y]:\n            p = x_border + y_border\n            d = d.connect_perim(a, b, p, p, ArrowOpts(head_arrow=empty()))\n    return d\n\n# NN drawing\ndef cell():\n    return rectangle(1, 1).line_width(0.01)\n\ndef matrix(n, r, c):\n    return tile(cell(), c, r, n)\n\ndef back(r, n):\n    \"Backing stack\"\n    return concat((r.translate(-i/2, -i/2).fill_opacity((n - i + n /2) / n)\n                   for i in range(n-1, -1, -1)))\n\nlw = 0.05\ndef stack(n, size, l, top, bot):\n    \"Feature map stack\"\n    m = matrix(n, size, size).fill_color(Color(\"#dddddd\"))\n    r = rectangle(size, size).fill_color(grey).line_width(lw)\n    return (label(top) / (back(r, l) + m) / label(bot)).center_xy()\n\nstack(\"a\", 32, 0, \"\", \"\")\n\ndef network(n, size, top, bot):\n    \"Draw a network layer\"\n    return (label(top) /  rectangle(2, size).fill_color(grey).line_width(lw).named(n) / label(bot)).center_xy()\n\n# The number 7\ndraw = make_path([(-10, -10), (10, -10 ), (-10, 10)]).line_width(0.09).line_color(blue).fill_opacity(0)\n\n# Draw the main diagram.\nh = hstrut(6.5)\nd = ((stack(\"a\", 32, 0, \"\", \"\") + draw) | (label(\"conv\") / h) |\n     stack(\"b\", 28, 6, \"\", \"C1\") | (label(\"pool\") / h) |\n     stack(\"c\", 14, 6, \"\", \"S2\") | (label(\"conv\") / h) |\n     stack(\"d\", 10, 16, \"\", \"C3\") | (label(\"pool\") / h) | hstrut(-0.5) | \n     stack(\"e\", 5, 16, \"\", \"S4\") | (label(\"dense\") / h) |\n     network(\"dense1\",  12, \"\", \"\") | (label(\"dense\") / (h)) |\n     network(\"dense2\",  8.4, \"\", \"\") | (label(\"dense\") / h) |\n     network(\"dense3\",  1, \"\", \"\"))\n     \nd = d.scale_uniform_to_x(5)\n\n# Draw the orange boxes\nboxes = [((\"a\", 2, 2), (\"a\", 6, 6)),\n         ((\"b\", 2, 2), (\"b\", 2, 2)),\n         ((\"b\", 20, 2), (\"b\", 23, 5)),\n         ((\"c\", 10, 2), (\"c\", 11, 3)),\n         ((\"c\", 4, 6), (\"c\", 8, 10)),\n         ((\"d\", 4, 6), (\"d\", 4, 6)),\n         ((\"d\", 6, 4), (\"d\", 9, 7)),\n         ((\"e\", 3, 2), (\"e\", 4, 3))]\n\nd += concat([cover(d, *b, (\"box\", i)).fill_color(papaya).fill_opacity(0.3) for i, b in enumerate(boxes)])\n\nconnect = [((\"box\", i), (\"box\", i + 1)) for i in range(0, 6, 2)]\n\n\nfor b in connect:\n    d = connect_all(d, *b)\nd\n\n\nd.render_svg(\"examples/output/lenet.svg\", 400)\n\ntry:\n    d.render(\"examples/output/lenet.png\", 400)\n    PILImage.open(\"examples/output/lenet.png\")\n    d.render_pdf(\"examples/output/lenet.pdf\", 400)\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n\n"
  },
  {
    "path": "examples/logo.py",
    "content": "from colour import Color\nfrom chalk import *\n\n# This code is for a Vogel subflower, ported from:\n# https://diagrams.github.io/gallery/Sunflower.html\n\nblack = Color(\"#000000\")\nwhite = Color(\"white\")\ngrey = Color(\"#cccccc\")\n\ndef coord(m):\n    return from_polar(math.sqrt(m)/1.2, 2.4 * m)\n\ndef from_polar(r, theta):\n    return (r * math.cos(theta), r * math.sin(theta))\n\ndef mkCoords(n):\n    return [coord(i) for i in range(1, n+1)]\n\ndef floret(r):\n    n = math.floor(1.8 * math.sqrt(r)) % 5\n    # Hippie color palette.\n    colors = [Color(h) for h in [\"#18b0dc\",\n                                 \"#056753\",\n                                 \"#b564ac\",\n                                 \"#e0b566\",\n                                 \"#e52828\"]\n    ]\n\n    return circle(0.6).line_width(0).fill_color(colors[n])\n\ndef florets(m):\n    return [floret(math.sqrt(i)) for i in range(1,m+1)]\n    \ndef sunflower(n):\n    return concat(flor.translate(cord[0], cord[1]) for cord, flor in zip(mkCoords(n), florets(n)))\n        \nfloret = sunflower(1900).center_xy().scale_uniform_to_x(1).center_xy()\nbackground = rectangle(1.6, 1).fill_color(black).line_width(0).translate(-0.15, 0)\nlogo = text(\"Chalk\", 0.35).fill_color(grey).line_width(0.1).line_color(black).translate(-0.4, -0.1)\nmask = rectangle(1.6, 0.6).translate(-0.15, 0)\n\n# assemble\nd = (background + floret + logo).align_t().with_envelope(mask.align_t())\n\n\nd.render_svg(\"examples/output/logo.svg\", 500)\n\ntry:\n    d.render(\"examples/output/logo.png\", 500)\n    d.render_pdf(\"examples/output/logo.pdf\", 50)\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n"
  },
  {
    "path": "examples/normalized.py",
    "content": "from chalk import *\n\n\nd = triangle(1).line_width(0.1) / triangle(0.5).line_width(0.1).scale(2) / triangle(0.5).line_width(0.1).scale_x(2).scale_y(2) / triangle(1)\n\nheight = 100\nd.render_svg(\"examples/output/normalized.svg\", height)\nd.render(\"examples/output/normalized.png\", height)\nd.render_pdf(\"examples/output/normalized.pdf\", height)\n\n\n"
  },
  {
    "path": "examples/output/index.html",
    "content": "<html>\n  \n  <div>\n    <h2> intro-01 </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"intro-01.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"intro-01.png\" height=\"64\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"intro-01.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"64\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> intro-02 </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"intro-02.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"intro-02.png\" height=\"64\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"intro-02.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"64\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> intro-03 </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"intro-03.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"intro-03.png\" height=\"64\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"intro-03.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"64\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> intro-04 </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"intro-04.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"intro-04.png\" height=\"256\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"intro-04.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"256\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> normalized </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"normalized.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"normalized.png\" height=\"100\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"normalized.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"100\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> escher-square-limit </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"escher-square-limit.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"escher-square-limit.png\" height=\"512\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"escher-square-limit.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"512\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> hex-variation </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"hex-variation.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"hex-variation.png\" height=\"512\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"hex-variation.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"512\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> koch </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"koch.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"koch.png\" height=\"512\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"koch.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"512\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> squares </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"squares.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"squares.png\" height=\"256\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"squares.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"256\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> hanoi </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"hanoi.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"hanoi.png\" height=\"700\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"hanoi.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"700\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> hilbert </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"hilbert.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"hilbert.png\" height=\"500\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"hilbert.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"500\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> lenet </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"lenet.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"lenet.png\" height=\"400\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"lenet.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"400\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> logo </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"logo.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"logo.png\" height=\"500\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"logo.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"500\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> tournament-network </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"tournament-network.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"tournament-network.png\" height=\"128\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"tournament-network.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"128\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> arrows </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"arrows.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"arrows.png\" height=\"200\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"arrows.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"200\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> path </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"path.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"path.png\" height=\"300\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"path.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"300\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> tensor </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"tensor.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"tensor.png\" height=\"500\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"tensor.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"500\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n  <div>\n    <h2> tree </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"tree.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"tree.png\" height=\"1200\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"tree.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"1200\" width=\"50%\">\n    </div>\n     \n  </div>\n  \n</html>\n"
  },
  {
    "path": "examples/output/path.tex",
    "content": "\\documentclass{standalone}%\n\\usepackage[T1]{fontenc}%\n\\usepackage[utf8]{inputenc}%\n\\usepackage{lmodern}%\n\\usepackage{textcomp}%\n\\usepackage{lastpage}%\n\\usepackage{tikz}%\n%\n%\n%\n\\begin{document}%\n\\normalsize%\n\\begin{tikzpicture}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, -1.0, (0.0, 0.0)}]%\n\\begin{scope}[cm={1.7025000519869151, 0.0, 0.0, 1.7025000519869151, (0.0, 0.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (1.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.5)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 2.5)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (-0.5,-0.5) -- (0.5,-0.5) -- (0.5,0.5) -- (-0.5,0.5) -- (-0.5,-0.5) -- cycle (-0.25,-0.25) -- (-0.25,0.25) -- (0.25,0.25) -- (0.25,-0.25) -- (-0.25,-0.25) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.0)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 3.5)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (2.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=2.0, y radius=2.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=2.0, y radius=2.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=2.0, y radius=2.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=2.0, y radius=2.0]} -- cycle (1.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-270.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 6.0)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 7.5)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=-90.0] arc [\n                           start angle=-90.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 0.6)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 1.2000000000000002)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=90.0] arc [\n                           start angle=-270.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 10.2)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 11.126776695296636)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=-44.999999999999986] arc [\n                           start angle=-109.4712206344907, end angle=109.47122063449068,\n                           x radius=0.7499999999999999, y radius=0.7499999999999999]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.5)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 3.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=135.00000000000003] arc [\n                           start angle=-109.47122063449072, end angle=109.47122063449066,\n                           x radius=0.7499999999999999, y radius=0.7499999999999999]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 3.9267766952966365)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 4.526776695296636)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=135.00000000000003] arc [\n                           start angle=-250.52877936550934, end angle=-469.4712206344907,\n                           x radius=0.7499999999999999, y radius=0.7499999999999999]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.9267766952966368)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 3.924452891525279)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=-135.0] arc [\n                           start angle=270.0, end angle=450.0,\n                           x radius=0.5, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 20.178006282118552)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 23.428006282118552)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-0.7500000000000001, -2.25)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (0.0,4.5) {[rotate=135.00000000000003] arc [\n                           start angle=-315.0, end angle=-405.0,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (1.0,5.0) {[rotate=45.000000000000014] arc [\n                           start angle=45.0, end angle=-45.000000000000014,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (1.5000000000000002,0.0) {[rotate=-44.999999999999986] arc [\n                           start angle=44.99999999999999, end angle=-45.000000000000014,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (0.5000000000000002,-0.5000000000000002) {[rotate=-135.0] arc [\n                           start angle=45.0, end angle=-45.0,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (0.0,0.009999999999999778) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 3.25)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-2.625, 4.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (0.0,0.75) {[rotate=135.00000000000003] arc [\n                           start angle=-315.0, end angle=-405.0,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (5.0,1.0) {[rotate=45.000000000000014] arc [\n                           start angle=45.0, end angle=-45.000000000000014,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (5.25,0.0) {[rotate=-44.999999999999986] arc [\n                           start angle=44.99999999999999, end angle=-45.000000000000014,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (0.25,-0.2500000000000001) {[rotate=-135.0] arc [\n                           start angle=45.0, end angle=-45.0,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (-1.1102230246251565e-16,0.00999999999999989) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 5.5)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 7.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-1.0000000000000002, -2.220446049250313e-16)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (0.0,0.0) {[rotate=135.00000000000003] arc [\n                           start angle=-315.0, end angle=-405.0,\n                           x radius=1.0, y radius=1.0]} -- (1.0,1.0000000000000004) {[rotate=45.000000000000014] arc [\n                           start angle=45.0, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]} -- (2.0000000000000004,4.440892098500626e-16) {[rotate=-44.999999999999986] arc [\n                           start angle=44.99999999999999, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]} -- (1.0000000000000004,-1.0) {[rotate=-135.0] arc [\n                           start angle=45.0, end angle=-45.0,\n                           x radius=1.0, y radius=1.0]} -- (0.0,0.01) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.5000000000000002)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 2.5)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-0.5, -0.5)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (1.0,0.0) -- (1.0,1.0) -- (0.0,1.0) -- (0.0,0.0) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 33.92800628211855)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 35.42800628211855)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-180.0, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (1.9571067811865475, 0.0)}]%\n\\path (-0.25,0.0) rectangle (0.25,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (3.914213562373095, 0.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=180.0] arc [\n                           start angle=-180.0, end angle=-315.0,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.35, 0.0)}]%\n\\path (-0.25,0.0) rectangle (0.25,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (2.3071067811865476, 0.0)}]%\n\\begin{scope}[cm={-1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-180.0, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (3.303817427962057, -33.0960958756463)}]%\n\\path (-7.996333196250992,-34.883720930232556) rectangle (7.996333196250992,34.883720930232556);%\n\\end{scope}%\n\\end{tikzpicture}%\n\\end{document}"
  },
  {
    "path": "examples/output/path.tex.tex",
    "content": "\\documentclass{standalone}%\n\\usepackage[T1]{fontenc}%\n\\usepackage[utf8]{inputenc}%\n\\usepackage{lmodern}%\n\\usepackage{textcomp}%\n\\usepackage{lastpage}%\n\\usepackage{tikz}%\n%\n%\n%\n\\begin{document}%\n\\normalsize%\n\\begin{tikzpicture}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, -1.0, (0.0, 0.0)}]%\n\\begin{scope}[cm={1.7025000519869151, 0.0, 0.0, 1.7025000519869151, (0.0, 0.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (1.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.5)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 2.5)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (-0.5,-0.5) -- (0.5,-0.5) -- (0.5,0.5) -- (-0.5,0.5) -- (-0.5,-0.5) -- cycle (-0.25,-0.25) -- (-0.25,0.25) -- (0.25,0.25) -- (0.25,-0.25) -- (-0.25,-0.25) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.0)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 3.5)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (2.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=2.0, y radius=2.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=2.0, y radius=2.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=2.0, y radius=2.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=2.0, y radius=2.0]} -- cycle (1.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-270.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 6.0)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 7.5)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=-90.0] arc [\n                           start angle=-90.0, end angle=90.0,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 0.6)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 1.2000000000000002)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=90.0] arc [\n                           start angle=-270.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 10.2)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 11.126776695296636)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=-44.999999999999986] arc [\n                           start angle=-109.4712206344907, end angle=109.47122063449068,\n                           x radius=0.7499999999999999, y radius=0.7499999999999999]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.5)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 3.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=135.00000000000003] arc [\n                           start angle=-109.47122063449072, end angle=109.47122063449066,\n                           x radius=0.7499999999999999, y radius=0.7499999999999999]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 3.9267766952966365)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 4.526776695296636)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=135.00000000000003] arc [\n                           start angle=-250.52877936550934, end angle=-469.4712206344907,\n                           x radius=0.7499999999999999, y radius=0.7499999999999999]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.9267766952966368)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 3.924452891525279)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=-135.0] arc [\n                           start angle=270.0, end angle=450.0,\n                           x radius=0.5, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 20.178006282118552)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 23.428006282118552)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-0.7500000000000001, -2.25)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (0.0,4.5) {[rotate=135.00000000000003] arc [\n                           start angle=-315.0, end angle=-405.0,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (1.0,5.0) {[rotate=45.000000000000014] arc [\n                           start angle=45.0, end angle=-45.000000000000014,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (1.5000000000000002,0.0) {[rotate=-44.999999999999986] arc [\n                           start angle=44.99999999999999, end angle=-45.000000000000014,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (0.5000000000000002,-0.5000000000000002) {[rotate=-135.0] arc [\n                           start angle=45.0, end angle=-45.0,\n                           x radius=0.5000000000000001, y radius=0.5000000000000001]} -- (0.0,0.009999999999999778) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 3.25)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-2.625, 4.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (0.0,0.75) {[rotate=135.00000000000003] arc [\n                           start angle=-315.0, end angle=-405.0,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (5.0,1.0) {[rotate=45.000000000000014] arc [\n                           start angle=45.0, end angle=-45.000000000000014,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (5.25,0.0) {[rotate=-44.999999999999986] arc [\n                           start angle=44.99999999999999, end angle=-45.000000000000014,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (0.25,-0.2500000000000001) {[rotate=-135.0] arc [\n                           start angle=45.0, end angle=-45.0,\n                           x radius=0.25000000000000006, y radius=0.25000000000000006]} -- (-1.1102230246251565e-16,0.00999999999999989) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 5.5)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 7.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-1.0000000000000002, -2.220446049250313e-16)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (0.0,0.0) {[rotate=135.00000000000003] arc [\n                           start angle=-315.0, end angle=-405.0,\n                           x radius=1.0, y radius=1.0]} -- (1.0,1.0000000000000004) {[rotate=45.000000000000014] arc [\n                           start angle=45.0, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]} -- (2.0000000000000004,4.440892098500626e-16) {[rotate=-44.999999999999986] arc [\n                           start angle=44.99999999999999, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]} -- (1.0000000000000004,-1.0) {[rotate=-135.0] arc [\n                           start angle=45.0, end angle=-45.0,\n                           x radius=1.0, y radius=1.0]} -- (0.0,0.01) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 1.5000000000000002)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 2.5)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (-0.5, -0.5)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt] (0.0,0.0) -- (1.0,0.0) -- (1.0,1.0) -- (0.0,1.0) -- (0.0,0.0) -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={0.0, -1.0, 1.0, 0.0, (0.0, 33.92800628211855)}]%\n\\path (-0.5,0.0) rectangle (0.5,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 35.42800628211855)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-180.0, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (1.9571067811865475, 0.0)}]%\n\\path (-0.25,0.0) rectangle (0.25,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (3.914213562373095, 0.0)}]%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=180.0] arc [\n                           start angle=-180.0, end angle=-315.0,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (0.35, 0.0)}]%\n\\path (-0.25,0.0) rectangle (0.25,0.0);%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (2.3071067811865476, 0.0)}]%\n\\begin{scope}[cm={-1.0, 0.0, 0.0, 1.0, (0.0, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},line width=10.5pt,fill opacity=0] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-180.0, end angle=-45.000000000000014,\n                           x radius=1.0, y radius=1.0]};%\n\\end{scope}%\n\\begin{scope}[cm={0.1, 0.0, 0.0, 0.1, (0.1, 0.0)}]%\n\\path[draw,fill={rgb,1:red,0.0; green,0.0; blue,1.0},draw={rgb,1:red,1.0; green,0.0; blue,0.0},line width=10.5pt] (0.0,0.0) {[rotate=0.0] arc [\n                           start angle=-0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=-90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=180.0] arc [\n                           start angle=-360.0, end angle=-450.0,\n                           x radius=1.0, y radius=1.0]} {[rotate=90.0] arc [\n                           start angle=0.0, end angle=-90.0,\n                           x radius=1.0, y radius=1.0]} -- cycle;%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\end{scope}%\n\\begin{scope}[cm={1.0, 0.0, 0.0, 1.0, (3.303817427962057, -33.0960958756463)}]%\n\\path (-7.996333196250992,-34.883720930232556) rectangle (7.996333196250992,34.883720930232556);%\n\\end{scope}%\n\\end{tikzpicture}%\n\\end{document}"
  },
  {
    "path": "examples/path.py",
    "content": "from chalk import *\nfrom chalk.shapes.segment import Segment\nfrom chalk.shapes.arc import ArcSegment\nfrom colour import Color\nimport math\n# d = Path([ArcSegment.arc_between(P2(0, 0), P2(2, 0), 1), ArcSegment.arc_between(P2(2, 0), P2(0, 0), 1)]).stroke()\n# d = Primitive.from_shape(Path([Segment(P2(0, 0), P2(1, 1))]))\n# d = Primitive.from_shape(Path([Segment(P2(0, 0), P2(1, 1)),\n#                                Segment(P2(1, 1), P2(1, 2))]))\n\n# d = Path([ArcSegment.arc_between(P2(0, 0), P2(2, 0), 1)]).stroke()\nr = 0.2\nb = 1 - r\nrad = 0.2 / 4\n\na  = ArcSegment(10, 50).rotate(90)\n\na  = ArcSegment.arc_between(P2(-1, 0), P2(0, 1.0), 1)\n# print(a.p, a.q, a.r_x, a.r_y, a.angle, a.tangle)\n\nd = []\n\nd += [circle(1).show_origin()]\n\nd += [(Trail.rectangle(1, 1).centered().to_path() + Trail.rectangle(0.5, 0.5).reverse().centered().to_path()).stroke()]\n\nd += [(Trail.circle(2).centered().to_path() + Trail.circle(1.0, False).centered().to_path()).stroke()]\n\nd += [arc_seg(2*unit_x, 1).stroke().show_origin()]\n\nd += [arc_seg(2*unit_x, -1).stroke().show_origin()]\n\nd += [arc_seg(unit_x + unit_y, 1).stroke().show_origin()]\n\nd += [arc_seg(unit_x + unit_y, 1).scale(-1).stroke().show_origin()]\n\nd += [arc_seg(unit_x + unit_y, -1).stroke().show_origin()]\n\nd += [arc_seg(2 * unit_x, 1).scale_y(0.5).rotate(45).stroke().show_origin()]\n\n\nd += [rectangle(1, 5, 0.5), rectangle(5, 1, 0.25), rectangle(1, 1, 1)]\n\n\nd += [Trail.rectangle(1, 1).stroke().center_xy().show_origin()]\n\nd += [cat(\n    [\n        arc_seg_angle(180, 135).stroke().show_origin(),\n        arc_seg_angle(180, 135).scale_x(-1).stroke().show_origin(),\n        arc_seg_angle(180, 135).stroke().scale_x(-1).show_origin(),\n    ],\n    unit_x,\n    sep=0.5\n)]\n\nd = vcat(d, sep=1.0)\n\nd = d.fill_color(Color(\"blue\"))\n\nd.render_svg(\"examples/output/path.svg\", height=300)\nd.render_pdf(\"examples/output/path.pdf\", height=300)\nd.render(\"examples/output/path.png\", height=300)\n"
  },
  {
    "path": "examples/rectangle_parade.py",
    "content": "import random\nfrom colour import Color\nfrom chalk import *\nblue = Color(\"#005FDB\")\n\npath = \"examples/output/rectangle-parade.png\"\nd = hcat(rectangle(1 + 5*random.random(), 1 + 5*random.random()).rotate_by(random.random()).fill_color(blue).show_envelope()\n         for i in range(1, 20))\nd.render(path, height=64)\n"
  },
  {
    "path": "examples/squares.py",
    "content": "from PIL import Image as PILImage\nimport math\nimport random\n\nfrom itertools import product\n\nfrom colour import Color\nfrom chalk import square, concat, empty\n\nrandom.seed(0)\n\ndef make_square():\n    colors = [\n        Color(\"#ff9700\"),  # papaya\n        Color(\"#005FDB\"),  # blue\n    ]\n\n    # generate uniformly a value in [-max_angle, max_angle]\n    max_angle = math.pi / 24.0\n    θ = 2 * max_angle * random.random() - max_angle\n\n    # pick a random color\n    i = random.random() > 0.75\n    color = colors[i]\n\n    return square(0.75).line_color(color).rotate_rad(-θ)\nmake_square()\n\ndef make_group(num_squares=4):\n    return concat(make_square() for _ in range(num_squares))\nmake_group()\n\ndisps = range(4)\ndiagram = concat(make_group().translate(x, y) for x, y in product(disps, disps))\n\ndiagram = diagram.line_width(0.02)\n\npath = \"examples/output/squares.svg\"\ndiagram.render_svg(path, height=256)\n\ntry:\n    path = \"examples/output/squares.png\"\n    diagram.render(path, height=256)\n    PILImage.open(path)\n\n    path = \"examples/output/squares.pdf\"\n    diagram.render_pdf(path, height=256)\n\nexcept ModuleNotFoundError:\n    print(\"Need to install Cairo\")\n\n\n"
  },
  {
    "path": "examples/subdiagrams.py",
    "content": "import pdb\nfrom colour import Color\nfrom chalk import *\n\n\ndef example1():\n    dia1 = square(1).translate(0, -4).named(Name(\"bob\")) | circle(1).named(Name(\"joe\"))\n    dia1 = dia1.connect(Name(\"bob\"), Name(\"joe\"))\n    \n    dia2 = square(1).named(Name(\"bob\")).translate(0, -4) | circle(1).named(Name(\"joe\"))\n    dia2 = dia2.connect(Name(\"bob\"), Name(\"joe\"))\n    \n    dia = hcat([dia1, dia2], sep=2)\n    dia.render_svg(\"examples/output/subdiagrams-1.svg\")\n\n\ndef example2():\n    red = Color(\"red\")\n\n    def attach(subs, dia):\n        sub1, sub2 = subs\n        p1 = tuple(sub1.get_location())\n        p2 = tuple(sub2.get_location())\n        return dia + make_path([p1, p2]).line_color(red)\n\n\n    def squares():\n        s = square(1)\n        return (\n            (s.named(Name(\"NW\")) | s.named(Name(\"NE\"))) /\n            (s.named(Name(\"SW\")) | s.named(Name(\"SE\"))))\n\n\n    dia = hcat([squares().qualify(Name(i)) for i in range(5)], sep=0.5)\n    pairs = [\n        [Name(0) + Name(\"NE\"), Name(2) + Name(\"SW\")],\n        [Name(1) + Name(\"SE\"), Name(4) + Name(\"NE\")],\n        [Name(3) + Name(\"NW\"), Name(3) + Name(\"SE\")],\n        [Name(0) + Name(\"SE\"), Name(1) + Name(\"NW\")],\n    ]\n\n    for pair in pairs:\n        dia = dia.with_names(pair, attach)\n\n    dia = dia.show_labels(0.3)\n    dia.render_svg(\"examples/output/subdiagrams-2.svg\")\n\n\ndef example3():\n    root = circle(1).named(Name(\"root\"))\n    leaves = hcat([circle(1).named(Name(c)) for c in \"abcde\"], sep=0.5).center()\n\n    def connect(subs, nodes):\n        subp, subc = subs\n        pp = tuple(subp.boundary_from(unit_y))\n        pc = tuple(subc.boundary_from(-unit_y))\n        return nodes + make_path([pp, pc])\n\n    nodes = root / vstrut(2) / leaves\n\n    for c in \"abcde\":\n        nodes = nodes.with_names([Name(\"root\"), Name(c)], connect)\n\n    nodes.render_svg(\"examples/output/subdiagrams-3.svg\")\n\n\nexample1()\nexample2()\nexample3()\n"
  },
  {
    "path": "examples/tensor.py",
    "content": "from PIL import Image as PILImage\nfrom chalk import *\nfrom colour import Color\n\nh = hstrut(2.5)\npapaya = Color(\"#ff9700\")\nwhite = Color(\"white\")\nblack = Color(\"black\")\n\n\ndef draw_cube():\n    # Assemble cube\n    face_m = rectangle(1, 1).align_tl()\n    face_t = rectangle(1, 0.5).shear_x(-1).align_bl()\n    face_r = rectangle(0.5, 1).shear_y(-1).align_tr()\n    cube = (face_t + face_m).align_tr() + face_r\n\n    # Replace envelope with front face. \n    return cube.align_bl().with_envelope(face_m.align_bl())\n\ndef draw_tensor(depth, rows, columns):\n    \"Draw a tensor\"\n    cube  = draw_cube()\n    # Fix this ...\n    hyp = (unit_y * 0.5).shear_x(-1)\n    # Build a matrix. \n    front = cat([hcat([cube for i in range(columns)])\n                 for j in reversed(range(rows))], -unit_y).align_t()\n\n    # Build depth\n    return concat(front.translate(-k * hyp.x, -k * hyp.y)\n                  for k in reversed(range(depth)))\n\ndraw_tensor(2, 3, 4)\n\ndef t(d, r, c):\n    return draw_tensor(d, r, c).fill_color(white)\n\ndef label(te, s=1.5):\n    return (text(te, s).fill_color(black).line_color(white).center_xy())\n\n\n# Create a diagram.\nd, r, c = 3, 4, 5\nbase = t(d, r, c).line_color(papaya)\nm = hcat([t(1, r, c),  t(d, 1, c), label(\"→\"), (base + t(1, r, c)), (base + t(d, 1, c) ), label(\"=\"), t(d, r, c)], sep=2.5).line_width(0.02)\n\n\npathsvg = \"examples/output/tensor.svg\"\nm.render_svg(pathsvg, 500)\npath = \"examples/output/tensor.png\"\nm.render(path, 500)\nPILImage.open(path)\n\nm.render_pdf(\"examples/output/tensor.pdf\", 50)\n"
  },
  {
    "path": "examples/tests/index.tpl.html",
    "content": "<html>\n  {% for x, h in [(\"intro-01\", 64),  (\"intro-02\", 64), (\"intro-03\", 64),  (\"intro-04\", 256), (\"normalized\", 100),\n  (\"escher-square-limit\", 512),  (\"hex-variation\", 512),   (\"koch\", 512),  (\"squares\", 256),  (\"hanoi\", 700),  (\"hilbert\", 500),  (\"lenet\", 400),  (\"logo\", 500), (\"tournament-network\", 128), (\"arrows\", 200), (\"path\", 300), (\"tensor\", 500),   (\"tree\", 1200)] %}\n  <div>\n    <h2> {{x}} </h2>\n    <div> <h3>SVG</h3>\n      <object data=\"{{x}}.svg\" type=\"image/svg+xml\">\n      </object>\n    </div>\n    <div> <h3>PNG</h3>\n      <img src=\"{{x}}.png\" height=\"{{h}}\" />\n    </div>\n    <div><h3>PDF</h3>\n      <embed src=\"{{x}}.pdf#toolbar=0&navpanes=0&scrollbar=0\" type=\"application/pdf\" height=\"{{h}}\" width=\"50%\">\n    </div>\n     \n  </div>\n  {% endfor %}\n</html>\n"
  },
  {
    "path": "examples/tournament-network.py",
    "content": "# Example taken from\n# https://diagrams.github.io/doc/quickstart.html\n\nfrom colour import Color\nfrom chalk import *\n\n\nwhite = Color(\"white\")\ngreen = Color(\"green\")\npink = Color(\"pink\")\n\n\ndef make_node(n):\n    c = circle(0.2).fill_color(green)\n    t = text(str(n), 0.2).fill_color(white).line_color(white).line_width(0)\n    return c + t\n\n\nn = 6\nhexagon = Trail.regular_polygon(n, 1)\nnodes = [make_node(i).named(i) for i in range(n)]\nnodes = [node.translate(point.x, point.y) for node, point in zip(nodes, hexagon.points())]\ndia = concat(nodes) \n\nfor i in range(n):\n    for j in range(i + 1, n):\n        dia = dia.connect_outside(i, j, ArrowOpts(head_pad=0.1, tail_pad=0.1))\n\ndia.render(\"examples/output/tournament-network.png\")\ndia.render_svg(\"examples/output/tournament-network.svg\")\n"
  },
  {
    "path": "examples/tree.py",
    "content": "from PIL import Image as PILImage\nfrom chalk import *\nfrom chalk.transform import *\nimport random\n\nfrom chalk import transform as tx\nfrom colour import Color\n\nblue = Color(\"blue\")\nred = Color(\"red\")\n\nrandom.seed(10)\ndef flip(p=0.4):\n    return random.random() > p\n\ndef sample_tree(n=1):\n    \"Sample a most right-branching tree\"\n    if n > 20:\n        return None\n    else:\n        return (flip(),\n                sample_tree(n+1) if flip(0.55)  else None,\n                sample_tree(n+1) if flip(0.2) else None)\n\n\ndef draw_tree(tree, name=\"\", ysep=5, xsep=1):\n    node = circle(5).named(name)\n    if tree is None:\n        node = node.fill_color(blue)\n        return name, node\n    node = node.fill_color(blue if tree[0] else red)\n\n    # Draw subtrees.\n    lname, l = draw_tree(tree[1], name + \"l\")\n    rname, r = draw_tree(tree[2], name + \"r\")\n\n    # Position node an origin and subtrees to both sides.\n    off = (l.get_envelope()(unit_x) + r.get_envelope()(-unit_x) + xsep) / 2    \n    x = node / vstrut(ysep) /  (l | hstrut(xsep) | r).translate(-off, 0)\n\n    # Connect to children\n    x = x.connect_outside(name, lname, ArrowOpts(head_arrow=empty()))\n    x = x.connect_outside(name, rname, ArrowOpts(head_arrow=empty()))\n    return name, x\n\n\n_, d =  draw_tree((True, (True, None, None), (False, (True, None, None), None)))\nd.line_width(0.01)\n\n_, d = draw_tree(sample_tree())\nd = d.line_width(0.01)\n\nd.render_svg(\"examples/output/tree.svg\", 1200)\nd.render(\"examples/output/tree.png\", 1200)\nPILImage.open(\"examples/output/tree.png\")\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: chalk-diagrams\nsite_url: https://chalk-diagrams.github.io\nsite_description: Chalk Diagrams - A declarative drawing API\nsite_author: Dan Oneață and Alexander Rush\n\n### Repository\n# repo_name: chalk-diagrams/chalk-diagrams.github.io\nrepo_url: https://github.com/chalk-diagrams/chalk\nedit_uri: '' # comment this out to disable allowing editing of the docs from the website.\nremote_branch: gh-pages\nremote_name: origin\n\n### Copyright\ncopyright: |\n  Maintained by <a href=\"https://github.com/danoneata\">Dan Oneață</a> and <a href=\"https://github.com/srush\">Alexander Rush</a>.\n\n### Preview Controls\nuse_directory_urls: true\nstrict: false\ndev_addr: localhost:8000\n\n### Configuration\ndocs_dir: docs\n# watch a list of directories for changes\n# and automatically regenerate the docs\nwatch:\n  - chalk\n\n### Theme\ntheme:\n  name: material\n  include_sidebar: true\n  custom_dir: docs/overrides\n  #custom_dir: overrides\n  palette:\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      primary: white\n      accent: amber\n    #   toggle:\n    #     # icon: material/lightbulb-outline\n    #     icon: material/toggle-switch-off-outline\n    #     name: Switch to dark mode\n    # - media: \"(prefers-color-scheme: dark)\"\n    #   scheme: slate\n    #   primary: white\n    #   accent: amber\n    #   toggle:\n    #     # icon: material/lightbulb\n    #     icon: material/toggle-switch\n    #     name: Switch to light mode\n  features:\n    - content.code.annotate\n    - content.tabs.link\n    # - header.autohide\n    # - navigation.expand\n    - navigation.indexes # @regular\n    - navigation.instant # @regular | enables \"instant-loading\"; good for a very large docs repo.\n    - navigation.sections # @regular | extending top level sections.\n    - navigation.tabs # @regular | enables showing toplevel sections as tabs (horizontal).\n    - navigation.tabs.sticky # @regular | keeps the tabs visible even when you have scrolled down.\n    - navigation.top # @regular | adds a \"back-to-top\" is shown after the user scrolls down and then starts to come back up again.\n    - navigation.tracking # @insiders\n    - search.highlight\n    - search.share\n    - search.suggest\n    - toc.integrate: false # @regular | integrates the nav (on-left) with toc (on-right) and places the integrated nav+toc on-left.\n  icon:\n    # repo: fontawesome/brands/git-square\n    repo: fontawesome/brands/git-alt\n    # repo: fontawesome/brands/github\n    # repo: fontawesome/brands/github-alt\n    # repo: fontawesome/brands/github-square\n  logo: logo.png # img/icon-white.svg\n  # favicon: logo.png # img/favicon.png\n  font:\n    text: Roboto\n    code: Roboto Mono # Source Code Pro, JetBrains Mono, Roboto Mono\n  language: en\n\n### Plugins\nplugins:\n  - exclude:\n      glob:\n        - '*/storage/*'\n  - search:\n      indexing: 'full' # 'full' (default), 'sections', 'titles'\n  - autorefs\n  - git-revision-date\n  # macros must be placed after plugin: git-revision-date\n  - macros:\n      include_dir: docs/assets/snippets # snippets\n      # j2_block_start_string: '{{%'\n      # j2_block_end_string: '%}}'\n      # j2_variable_start_string: '{{'\n      # j2_variable_end_string: '}}'\n  - markdownextradata:\n      data: data\n  - minify:\n      minify_html: true\n  # - social # @insiders\n  - mkdocs-jupyter:\n      include_source: true\n      ignore_h1_titles: true\n      execute: true\n  - tooltips\n  - mkdocstrings\n\n### Extensions\nmarkdown_extensions:\n  # - abbr\n  # - admonition\n  # - attr_list\n  # - codehilite\n  # - def_list\n  # - extra\n  # - footnotes\n  # - meta\n  # - md_in_html\n  # - smarty\n  # - tables\n  # - toc\n  ##! Controls: markdown.extensions\n  - markdown.extensions.abbr # same as: - abbr\n  - markdown.extensions.admonition # same as: - admonition\n  - markdown.extensions.attr_list # same as: - attr_list\n  - markdown.extensions.codehilite: # same as: - codehilite\n      guess_lang: false\n  - markdown.extensions.def_list # same as: - def_list\n  - markdown.extensions.extra # same as: - extra\n  - markdown.extensions.footnotes # same as: - footnotes\n  - markdown.extensions.meta: # same as: - meta\n  - markdown.extensions.md_in_html # same as: - md_in_html\n  - markdown.extensions.smarty: # same as: - smarty\n      smart_quotes: false\n  - markdown.extensions.tables # same as: - tables\n  - markdown.extensions.toc: # same as: - toc\n      slugify: !!python/name:pymdownx.slugs.uslugify\n      permalink: true\n      toc_depth: 6 # default: 6\n      #separator: \"-\"\n\n  - markdown_include.include:\n      base_path: docs\n\n  ##! Controls: mdx\n  - mdx_include:\n      base_path: docs\n  - mdx_truly_sane_lists:\n      nested_indent: 2\n      truly_sane: true\n\n  ##! Controls: pymdownx\n  - pymdownx.arithmatex:\n      generic: true\n  # - pymdownx.b64:\n  #     base_path: '.'\n  - pymdownx.betterem:\n      smart_enable: all # default: 'underscore' ; options: 'underscore', 'all', 'asterisk', or 'none'\n  - pymdownx.caret: # \"super^script^\" will render as superscript text: super<sup>script</sup>.\n      smart_insert: true # default: true\n      insert: true # default: true\n      superscript: true # default: true\n  - pymdownx.critic\n  - pymdownx.details\n  - pymdownx.emoji:\n      emoji_index: !!python/name:materialx.emoji.twemoji\n      emoji_generator: !!python/name:materialx.emoji.to_svg\n  - pymdownx.escapeall:\n      hardbreak: false\n      nbsp: false\n  # Uncomment these 2 lines during development to more easily add highlights\n  - pymdownx.highlight:\n      use_pygments: true # this uses pygments\n      linenums: false # Set \"linenums\" to true for enabling\n                      #     code-block line-numbering\n                      #     globally.\n                      # None: only enable line-numbering on a per code-block basis.\n                      # False: disable line-numbering globally.\n      auto_title: false\n      auto_title_map: {\n          \"Python Console Session\": \"Python\", # lang: pycon\n        }\n      linenums_style: pymdownx-inline # table or pymdownx-inline\n  - pymdownx.inlinehilite:\n      custom_inline:\n        - name: math\n          class: arithmatex\n          format: !!python/name:pymdownx.arithmatex.inline_mathjax_format\n  - pymdownx.keys:\n      separator: \"\\uff0b\"\n  - pymdownx.magiclink:\n      repo_url_shortener: true\n      repo_url_shorthand: true #\n      social_url_shorthand: true\n      social_url_shortener: true\n      user: !ENV REPO_OWNER # sugatoray, danoneata (github userid)\n      repo: chalk #\n      normalize_issue_symbols: true\n  - pymdownx.mark:\n      smart_mark: true\n  - pymdownx.pathconverter:\n      base_path: 'chalk' # default: ''\n      relative_path: '' # default ''\n      absolute: true # default: false\n      tags: 'a script img link object embed'\n  - pymdownx.progressbar:\n      level_class: true\n      add_classes: ''\n        #'progress-0plus progress-10plus progress-20plus progress-30plus progress-40plus progress-50plus progress-60plus progress-70plus progress-80plus progress-90plus progress-100plus'\n      progress_increment: 10\n  - pymdownx.saneheaders\n  - pymdownx.superfences:\n      # highlight_code: true # This was removed from pymdownx v9.0\n      preserve_tabs: false\n      disable_indented_code_blocks: false # default: false | set this to \"true\"\n                                          # if you only use fenced code-blocks.\n      custom_fences:\n      - name: mermaid\n        class: mermaid\n        format: !!python/name:pymdownx.superfences.fence_code_format ''\n      - name: math\n        class: arithmatex\n        format: !!python/name:pymdownx.arithmatex.fence_mathjax_format\n      # - name: md-render\n      #   class: md-render\n      #   format: !!python/name:tools.pymdownx_md_render.md_sub_render\n  - pymdownx.smartsymbols\n  - pymdownx.snippets:\n      base_path:\n        - '.'\n        - './docs_src'\n        - './LICENSE'\n        - './README.md'\n        - './doc' # [TODO: move the contents of this folder to the docs folder]\n      encoding: 'utf-8' # Encoding to use when reading in the snippets.\n      check_paths: true # Make the build fail if a snippet can't be found.\n  - pymdownx.striphtml\n  - pymdownx.tabbed\n  - pymdownx.tasklist:\n      custom_checkbox: true\n  - pymdownx.tasklist:\n      custom_checkbox: true\n  - pymdownx.tilde # ~~text~~ will render as strikethrough text. \"sub~script\" will render as subscript text: sub<sub>script</sub>.\n\n\n### Customization\n# extra:\n  # version:\n  #   default: latest\n  #   provider: mike\n  # disqus: !ENV REPO_OWNER\n  #alternate:\n  #- name: en - English\n  #  link: /\n  #  lang: en\n\n\n\n### Extra CSS\nextra_css:\n  ## for: termynal (terminal animation)\n  - assets/css-js/termynal/css/termynal.css\n  - assets/css-js/termynal/css/custom.css\n  ## for: pymdownx.progressbar\n  - assets/css-js/general/css/progressbar.css\n  # - assets/css-js/pymdownx-extras/css/extra.css # (for striped progress bar)\n  ## for: mkdocs-tooltips\n  - assets/css-js/mkdocs-tooltips/css/hint.min.css\n  - assets/css-js/mkdocs-tooltips/css/custom.css\n  ## for: mkdocs-material using highlight.js\n  - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css\n  ## for: fastapi like side-theme\n  - assets/css-js/fastapi/custom.css\n\n\n### Extra JS\nextra_javascript:\n  ## for: pymdownx.arithmatex\n  - https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js?config=TeX-MML-AM_CHTML\n  ## for: markdown.extensions.tables\n  - https://cdnjs.cloudflare.com/ajax/libs/tablesort/5.2.1/tablesort.min.js\n  - assets/css-js/general/js/tables.js\n  ## for: termynal (terminal animation)\n  - assets/css-js/termynal/js/termynal.js\n  - assets/css-js/termynal/js/custom.js\n  # Set the environment variable \"FONTAWESOME_KIT\" with the value of the kit.\n  - !ENV FONTAWESOME_KIT\n  ## for: lottiefiles\n  - https://unpkg.com/@lottiefiles/lottie-player@latest/dist/lottie-player.js\n  ## for: mkdocs-material using highlight.js\n  - https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js\n  - assets/css-js/general/js/highlight-config.js\n  ## for: mkdocs-markmap\n  - https://unpkg.com/d3@6.7.0/dist/d3.min.js\n  - https://unpkg.com/markmap-lib@0.11.5/dist/browser/index.min.js\n  - https://unpkg.com/markmap-view@0.2.6/dist/index.min.j\n  ## Others\n  - https://polyfill.io/v3/polyfill.min.js?features=es6\n  # - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js\n  ## for: fastapi like side-theme\n  - assets/css-js/fastapi/custom.js\n  - assets/css-js/fastapi/chat.js\n\n### Pages: Navigation\n\n## @@ Begin NAVIGATION\nnav:\n  - Home: index.md\n  - API:\n    - Shapes: api/shapes.py\n    - Alignment: api/alignment.py\n    - Combinators: api/combinators.py\n    - Transformations: api/transformations.py\n    - Style: api/style.py\n    - Rendering: api/rendering.py\n    - Trails: api/trails.py\n    - Connections: api/names.py\n    - Utils: api/utils.md\n    - Internals: api/internals.md\n  - Examples:\n    - BigBen: examples/bigben.py\n    - Hanoi: examples/hanoi.py\n    - Koch: examples/koch.py\n    - LeNet: examples/lenet.py\n    - Squares: examples/squares.py\n    - Tensor: examples/tensor.py\n    - Tree: examples/tree.py\n  - About:\n    - about/index.md\n    - License: about/license.md\n\n## @@ End NAVIGATION\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\n    \"setuptools\",\n    \"wheel\"\n]\n\nbuild-backend = \"setuptools.build_meta\"\n"
  },
  {
    "path": "requirements/dev.txt",
    "content": "flake8>=3.6.0\nblack>=19.3b0\nmypy>=0.95\ninterrogate>=1.5.0\nisort>=5.10.0\ndarglint>=1.8.0\npre-commit>=2.2.0\n# flake8-print>=4.4.0\npytest>=4.0.2\nhypothesis\n"
  },
  {
    "path": "requirements/docs.txt",
    "content": "mkdocs\nmkdocs-material==8.1.3\nmkdocs-material-extensions>=1.0.3\npymdown-extensions>=9.0\nmdx-include>=1.4.1\nmarkdown-include>=0.6.0\nmdx_truly_sane_lists>=1.2\npygments\njupyter\n\n## Navigation & page building\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#navigation--page-building\nmkdocs-exclude>=1.0.2\nmkdocs-awesome-pages-plugin>=2.5.0\nmkdocs-markdownextradata-plugin>=0.2.4\n\n\n## Links & references\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#links--references\nmkdocs-redirects>=1.0.3\nmkdocs-tooltipster-links-plugin>=0.1.0\n\n\n## HTML processing & CSS styling\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#html-processing--css-styling\nmkdocs-minify-plugin>=0.4.1\nmkdocs-enumerate-headings-plugin>=0.4.5\n\n\n## API documentation building\nmkapi\nmkautodoc\nmkdocstrings[python]>=0.16.2\nmkdocs-gen-files>=0.3.3\nmkdocs-autorefs>=0.3.1\n\n\n## Citations & bibliography\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#citations--bibliography\nmkdocs-bibtex>=1.0.0\n\n\n## Code execution, variables & templating\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#code-execution-variables--templating\npydoc-markdown>=4.6.3\nmkdocs-jupyter>=0.18.2\nmkdocs-markdownextradata-plugin>=0.2.4\nmkdocs-markmap>=2.1.2\n\n\n## Git repos and info\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#git-repos--info\nmkdocs-macros-plugin>=0.6.0\nmkdocs-git-revision-date-plugin>=0.3.1\n\n\n## Images, Tables, Charts & Graphs\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#images-tables-charts--graphs\nmkdocs-kroki-plugin>=0.2.0\nmkdocs-drawio-exporter>=0.8.0\nmkdocs-table-reader-plugin>=0.6\n\n\n## PDF & site conversion\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#pdf--site-conversion\n# https://doc.courtbouillon.org/weasyprint/latest/first_steps.html#linux\nmkdocs-pdf-export-plugin>=0.5.9\n\n\n## Other\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#other\nmkdocs-tooltips>=0.1.0\nmkdocs-coverage>=0.2.4\n\n\n## Reusing content, snippets & includes\n#  source: https://github.com/mkdocs/mkdocs/wiki/MkDocs-Plugins#reusing-content-snippets--includes\nmkdocs-include-markdown-plugin>=3.2.3\n"
  },
  {
    "path": "requirements.txt",
    "content": "toolz\ncolour\nsvgwrite\ncairosvg\nPillow\ngit+https://github.com/chalk-diagrams/planar\nlatextools\nloguru\ntyping-extensions\nimportlib-metadata\n"
  },
  {
    "path": "setup.cfg",
    "content": "[bdist_wheel]\n# what is this for?\n# - https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#wheels\nuniversal=0\n\n[metadata]\nlicense_file = LICENSE\n\n[black]\nline-length = 79\n# exclude = '''\n# /(\n#     \\.archive\n#   | \\.git\n#   | \\.hg\n#   | \\.mypy_cache\n#   | \\.tox\n#   | \\.venv\n#   | \\.vscode\n#   | _build\n#   | buck-out\n#   | build\n#   | dist\n#   | migrations\n#   | site\n# )/\n# '''\n\n[isort]\n# make it compatible with black\nprofile = black\n# # Make sure this matches `*.py` in .editorconfig\n# ensure_newline_before_comments = true\n# force_single_line = true\n# lines_after_imports = 3\n# include_trailing_comma = true\n# use_parentheses = true\n\n[flake8]\nper-file-ignores=chalk/__init__.py: F401\nmax-line-length = 88\nextend-ignore = E203\n\n[darglint]\n##? Source: https://github.com/terrencepreilly/darglint\n## Ignore properties\nignore_properties = 1\n## Ignore private methods\nignore_regex = ^_(.*)\n## Use message template\n# message_template = {msg_id}@{path}:{line}\n## Docstring style to use:\n# - google (default)\n# - sphinx\n# - numpy\ndocstring_style = google\n## How strict?\n# short: One-line descriptions are acceptable; anything\n#        more and the docstring will be fully checked.\n#\n# long: One-line descriptions and descriptions without\n#       arguments/returns/yields/etc. sections will be\n#       allowed. Anything more, and the docstring will\n#       be fully checked.\n#\n# full: (Default) Docstrings will be fully checked.\nstrictness = long\n## Ignore common exceptions\n# ignore_raise = ValueError,MyCustomError\n## Ignore Specific Error Codes\n# Example: ignore = DAR402,DAR103\n#------------------------------------------------------------------------\n# DAR001  # The docstring was not parsed correctly due to a syntax error.\n# DAR002  # An argument/exception lacks a description\n# DAR003  # A line is under-indented or over-indented.\n# DAR004  # The docstring contains an extra newline where it shouldn't.\n# DAR005  # The item contains a type section (parentheses), but no type.\n# DAR101  # The docstring is missing a parameter in the definition.\n# DAR102  # The docstring contains a parameter not in function.\n# DAR103  # The docstring parameter type doesn't match function.\n# DAR104  # (disabled) The docstring parameter has no type specified\n# DAR105  # The docstring parameter type is malformed.\n# DAR201  # The docstring is missing a return from definition.\n# DAR202  # The docstring has a return not in definition.\n# DAR203  # The docstring parameter type doesn't match function.\n# DAR301  # The docstring is missing a yield present in definition.\n# DAR302  # The docstring has a yield not in definition.\n# DAR401  # The docstring is missing an exception raised.\n# DAR402  # The docstring describes an exception not explicitly raised.\n# DAR501  # The docstring describes a variable which is not defined.\n#------------------------------------------------------------------------\nignore = DAR103\n\n[mypy]\nstrict = true\nwarn_unreachable = true\npretty = true\nshow_column_numbers = true\nshow_error_codes = true\nshow_error_context = true\n\n[mypy-chalk]\nimplicit_reexport = true\n[mypy-chalk.shapes]\nimplicit_reexport = true"
  },
  {
    "path": "setup.py",
    "content": "import pathlib\n\nfrom setuptools import find_packages, setup\n\nLICENSE: str = \"MIT\"\nREADME: str = pathlib.Path(\"README.md\").read_text(encoding=\"utf-8\")\n\n# ---------------------------------------------------------------\n# NOTE:\n# Since the library name (chalk-diagrams) is different from\n#   the module (chalk), we set the custom dunder attribute\n#   __libname__ in chalk/__init__.py and use it there to fetch\n#   and set __version__ with library metadata inside\n#   chalk/__init__.py.\n#   Since, library name will not be changed in future, it is\n#   being maintained at two places\n#   1. setup.py\n#   1. chalk/__init__.py\n#\n#   The version will be updated often only from setup.py.\n# ---------------------------------------------------------------\nLIBNAME: str = \"chalk-diagrams\"\n\nsetup(\n    name=LIBNAME,\n    version=\"0.2.2\",\n    packages=find_packages(\n        include=[\"chalk\", \"chalk*\"],\n        exclude=[\"examples\", \"docs\", \"test*\"],\n    ),\n    description=\"A declarative drawing API\",\n    install_requires=[\n        \"toolz\",\n        \"colour\",\n        \"svgwrite\",\n        \"Pillow\",\n        \"loguru\",\n        \"chalk-planar\",\n        \"typing-extensions\",\n        \"importlib-metadata\",\n    ],\n    extras_require={\n        \"tikz\": [\"pylatex\"],\n        \"latex\": [\"latextools\"],\n        \"png\": [\"pycairo\"],\n        \"svg\": [\"cairosvg\"],\n    },\n    long_description=README,\n    long_description_content_type=\"text/markdown\",\n    author=\"Dan Oneață\",\n    author_email=\"dan.oneata@gmail.com\",\n    url=\"https://github.com/chalk-diagrams/chalk\",\n    project_urls={\n        \"Documentation\": \"https://chalk-diagrams.github.io\",\n        \"Source Code\": \"https://github.com/chalk-diagrams/chalk\",\n        \"Issue Tracker\": \"https://github.com/chalk-diagrams/chalk/issues\",\n    },\n    license=LICENSE,\n    license_files=(\"LICENSE\",),\n    classifiers=[\n        \"Intended Audience :: Science/Research\",\n        \"Operating System :: OS Independent\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n        f\"License :: OSI Approved :: {LICENSE} License\",\n        \"Topic :: Scientific/Engineering\",\n    ],\n)\n"
  },
  {
    "path": "tests/test_envelope.py",
    "content": "import math\n\nimport pytest\nfrom hypothesis import given\nfrom hypothesis.strategies import (\n    DrawFn,\n    composite,\n    integers,\n    lists,\n    one_of,\n    sampled_from,\n)\n\nimport chalk\nfrom chalk import (\n    P2,\n    V2,\n    Diagram,\n    Trail,\n    circle,\n    empty,\n    make_path,\n    origin,\n    rectangle,\n    unit_x,\n    unit_y,\n)\n\n\n@composite\ndef vectors(draw: DrawFn) -> V2:\n    x = draw(integers(min_value=-2, max_value=2).filter(lambda x: x != 0))\n    y = draw(integers(min_value=-2, max_value=2).filter(lambda x: x != 0))\n    return V2(x, y)\n\n\nsmall_nat = integers(min_value=1, max_value=10)\n\n\n@composite\ndef trails(draw: DrawFn) -> Trail:\n    vs = draw(lists(vectors(), min_size=1))\n    return Trail.from_offsets(vs)\n\n\n@composite\ndef paths(draw: DrawFn) -> Diagram:\n    return draw(trails()).stroke().center_xy()\n\n\n@composite\ndef circles(draw: DrawFn) -> Diagram:\n    return circle(draw(small_nat))\n\n\n@composite\ndef rects(draw: DrawFn) -> Diagram:\n    return rectangle(draw(small_nat), draw(small_nat))\n\n\n@composite\ndef shapes(draw: DrawFn) -> Diagram:\n    return draw(one_of(paths(), rects(), circles()))\n\n\n@composite\ndef diagrams(draw: DrawFn) -> Diagram:\n    shape = empty()\n    for j in range(3):\n        lshape = draw(shapes())\n        shape += chalk.transform.apply_affine(draw(transforms()), lshape)\n    return shape\n\n\n@composite\ndef transforms(draw: DrawFn) -> chalk.transform.Affine:\n    v2 = draw(vectors())\n    return draw(\n        sampled_from(\n            [\n                chalk.transform.Affine.scale(v2),\n                chalk.transform.Affine.translation(v2),\n                chalk.transform.Affine.rotation(v2.angle),\n            ]\n        )\n    )\n\n\n@given(diagrams(), vectors())\ndef test_envelope_trail(diagram: Diagram, vec: V2) -> None:\n    \"Property -> Envelope bounds trace.\"\n    trace = diagram.get_trace()\n    env = diagram.get_envelope()\n    ts = trace(P2(0, 0), vec)\n    e = env(vec)\n    for t in ts:\n        assert e == pytest.approx(t) or e > t\n\n\n@given(diagrams(), vectors())\ndef test_pad(diagram: Diagram, vec: V2) -> None:\n    orig = diagram.get_envelope()(vec)\n    p = diagram.pad(2)\n    assert p.get_envelope()(vec) == pytest.approx(2 * orig)\n    vec = vec.normalized()\n    orig = diagram.get_envelope()(vec)\n    f = diagram.frame(2)\n    assert f.get_envelope()(vec) == pytest.approx(2 + orig)\n\n\n# Some specific tests.\ndef test_square() -> None:\n    square = make_path([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])\n    env = square.get_envelope()\n    assert env(unit_x) == 1\n    assert env(2 * unit_x) == 0.5\n    assert env(unit_y) == 1\n    assert env((unit_x + unit_y).normalized()) == pytest.approx(\n        math.sqrt(1 + 1)\n    )\n\n\ndef test_circle() -> None:\n    d = circle(1)\n    env = d.get_envelope()\n    assert env(unit_x) == 1\n    assert env(2 * unit_x) == 0.5\n    assert env(unit_y) == 1\n    assert env((unit_x + unit_y).normalized()) == pytest.approx(1)\n\n\ndef test_circle_trace() -> None:\n    d = circle(1)\n    trace = d.get_trace()\n    assert set(trace(origin, unit_x)) == set([-1.0, 1.0])\n    assert set(trace(origin, (2 * unit_x))) == set([-0.5, 0.5])\n    assert set(trace(origin, unit_y)) == set([-1.0, 1.0])\n    trace(origin, (unit_x + unit_y))\n\n\ndef test_path_trace() -> None:\n    d = make_path([(1, 0), (1, 1)])\n    trace = d.get_trace()\n    assert trace.trace_v(origin, (unit_x + unit_y)) == V2(1.0, 1.0)\n    assert trace.trace_v(origin, (unit_x + unit_y).normalized()) == V2(\n        1.0, 1.0\n    )\n\n\ndef test_transform() -> None:\n    square = make_path([(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)])\n    env = square.scale_x(2).scale_y(3).get_envelope()\n    assert env(unit_x) == 2\n    assert env(2 * unit_x) == 1\n    assert env(unit_y) == 3\n    env = square.rotate(45).get_envelope()\n    assert env((unit_x + unit_y).normalized()) == pytest.approx(1)\n    env = square.translate(-2, -2).get_envelope()\n    assert env(unit_x) == pytest.approx(-1)\n"
  },
  {
    "path": "tests/test_reverse_trail.py",
    "content": "from hypothesis import given\nfrom hypothesis.strategies import (\n    DrawFn,\n    composite,\n    integers,\n    one_of,\n    sampled_from,\n)\n\nimport chalk\nfrom chalk import V2, Trail, unit_x\nfrom chalk.shapes.arc import arc_seg_angle\nfrom chalk.shapes.segment import seg\n\n\n@composite\ndef vectors(draw: DrawFn) -> V2:\n    x = draw(integers(-2, 2))\n    y = draw(integers(-2, 2))\n    return V2(x, y)\n\n\n@composite\ndef transforms(draw: DrawFn) -> chalk.transform.Affine:\n    v2 = draw(vectors())\n    return draw(\n        sampled_from(\n            [\n                chalk.transform.Affine.scale(unit_x),\n                chalk.transform.Affine.scale(v2),\n                chalk.transform.Affine.translation(v2),\n                chalk.transform.Affine.rotation(v2.angle),\n            ]\n        )\n    )\n\n\n@composite\ndef segment(draw: DrawFn) -> Trail:\n    dx = draw(integers())\n    dy = draw(integers())\n    return seg(V2(dx, dy))\n\n\n@composite\ndef arc(draw: DrawFn) -> Trail:\n    angle = draw(integers(-360, 360))\n    dangle = draw(integers(0, 360))\n    return arc_seg_angle(angle, dangle)\n\n\nsmall_nat = integers(min_value=1, max_value=10)\n\n\n@composite\ndef trails(draw: DrawFn) -> Trail:\n    parts = (\n        draw(one_of(segment(), arc())).apply_transform(draw(transforms()))\n        for _ in range(draw(small_nat))\n    )\n    return sum(parts, Trail.empty())\n\n\n@given(trails())\ndef test_involution(t: Trail) -> None:\n    \"Reverse should be an involution.\"\n    assert t.reverse().reverse() == t\n\n\n@given(trails(), trails())\ndef test_order(t1: Trail, t2: Trail) -> None:\n    assert (t1 + t2).reverse() == t2.reverse() + t1.reverse()\n\n\n# Other possible tests?\n# - End point of reversed trail should correspond to start point of original\n#   trail (and vicevers);\n# - The shape of the trail is preserved.\n"
  }
]