diff --git a/.gitattributes b/.gitattributes index 2f41707..8425c58 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1 @@ -*.ipynb filter=strip-notebook-output +*.ipynb filter=strip-notebook-output diff --git a/.github/actions/build-docs/action.yml b/.github/actions/build-docs/action.yml new file mode 100644 index 0000000..d1f3ec6 --- /dev/null +++ b/.github/actions/build-docs/action.yml @@ -0,0 +1,42 @@ +name: "Build Documentation" +description: "Build Sphinx documentation" +inputs: + python-version: + description: "Python version to use" + required: false + default: "3.10" + treat-warnings-as-errors: + description: "Treat warnings as errors (-W flag)" + required: false + default: "false" +runs: + using: "composite" + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + + - name: Install pandoc + shell: bash + run: | + apt-get update + apt-get install -y pandoc + + - name: Check if pdflatex is available + shell: bash + run: | + which pdflatex + pdflatex --version + + - name: Install Python dependencies + shell: bash + run: | + python -m pip install --upgrade pip + pip install ".[docs]" + + - name: Build Sphinx docs + shell: bash + run: | + cd docs + make html diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index bdb095b..0000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,85 +0,0 @@ -name: Deploy docs to GitHub Pages - -on: - push: - branches: ["devel"] # TODO: Set to main only after release - workflow_dispatch: - -defaults: - run: - shell: bash - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: "pages" - cancel-in-progress: false - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - container: - image: texlive/texlive:latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - - - name: Cache pip - uses: actions/cache@v3 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install pandoc - run: | - apt-get update - apt-get install -y pandoc - - # - name: Install TeX Live with pdflatex - # run: | - # sudo apt-get update - # sudo apt-get install -y texlive-full - # sudo apt-get install -y texlive texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-science - - - name: Check if pdflatex is available - run: | - which pdflatex - pdflatex --version - - - name: Set up virtual environment and install project - run: | - python -m venv env - source env/bin/activate - pip install --upgrade pip - pip install ".[test, dev, docs]" - - - name: Build Sphinx docs - run: | - source env/bin/activate - cd docs - make html - - - name: Setup Pages - uses: actions/configure-pages@v5 - - - name: Upload built docs - uses: actions/upload-pages-artifact@v3 - with: - path: docs/build/html/ - - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index eaff1cd..2e65b3f 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,4 +31,3 @@ jobs: TWINE_USERNAME: __token__ # Use API token TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: twine upload dist/* - diff --git a/.github/workflows/publish_docs.yml b/.github/workflows/publish_docs.yml new file mode 100644 index 0000000..31aefb8 --- /dev/null +++ b/.github/workflows/publish_docs.yml @@ -0,0 +1,46 @@ +name: Deploy docs to GitHub Pages + +on: + push: + branches: ["devel"] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + container: + image: texlive/texlive:latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build documentation + uses: ./.github/actions/build-docs + with: + python-version: "3.10" + treat-warnings-as-errors: "false" + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Upload built docs + uses: actions/upload-pages-artifact@v3 + with: + path: docs/build/html/ + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/publish_pypi.yml b/.github/workflows/publish_pypi.yml new file mode 100644 index 0000000..a376459 --- /dev/null +++ b/.github/workflows/publish_pypi.yml @@ -0,0 +1,36 @@ +name: Publish Python Package + +on: + push: + branches: + - main + +jobs: + build-and-publish: + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/project/tikzpics/ + permissions: + id-token: write + contents: read + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: "3.10" + + - name: Install build tools + run: | + python -m pip install --upgrade pip + pip install build + + - name: Build the package + run: python -m build + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 61b3cb6..fce5a85 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -15,42 +15,32 @@ defaults: shell: bash jobs: - cloc: + pre-commit: runs-on: ubuntu-latest steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Download and run cloc - run: | - curl -s https://raw.githubusercontent.com/AlDanial/cloc/master/cloc > cloc - chmod +x cloc - ./cloc --version - ./cloc $(git ls-files) - - black: - runs-on: ubuntu-latest - steps: - - name: Checkout the code - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" - - name: Code formatting with black - run: | - pip install black "black[jupyter]" - black --check src/ - black --check tutorials/ + - name: Run pre-commit + uses: pre-commit/action@v3.0.1 - isort: + cloc: runs-on: ubuntu-latest steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Code formatting with isort + - name: Download and run cloc run: | - pip install isort - isort --check src/ - isort --check tutorials/ + curl -s https://raw.githubusercontent.com/AlDanial/cloc/master/cloc > cloc + chmod +x cloc + ./cloc --version + ./cloc $(git ls-files) mypy: runs-on: ubuntu-latest @@ -59,43 +49,12 @@ jobs: - name: Checkout the code uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + - name: Type checking with mypy run: | pip install mypy mypy src/ || true - - prospector: - runs-on: ubuntu-latest - continue-on-error: true - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - - name: Code analysis with prospector - run: | - pip install prospector - prospector src/ || true - - ruff: - runs-on: ubuntu-latest - continue-on-error: true - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - - name: Linting with ruff - run: | - pip install ruff - ruff check src/ || true - - pylint: - runs-on: ubuntu-latest - continue-on-error: true - steps: - - name: Checkout the code - uses: actions/checkout@v4 - - - name: Linting with pylint - run: | - pip install pylint - pylint src/ || true diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3025731..f8d1285 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,7 +39,7 @@ jobs: key: ${{ runner.os }}-pip-${{ hashFiles('**/pyproject.toml') }} restore-keys: | ${{ runner.os }}-pip- - + - name: Install pandoc run: | apt-get update @@ -106,4 +106,3 @@ jobs: - name: Build the package run: python -m build - diff --git a/.github/workflows/test_docs.yml b/.github/workflows/test_docs.yml new file mode 100644 index 0000000..413bd1a --- /dev/null +++ b/.github/workflows/test_docs.yml @@ -0,0 +1,39 @@ +name: Test Documentation Build + +on: + push: + branches: + - main + - devel + pull_request: + branches: + - main + - devel + +defaults: + run: + shell: bash + +jobs: + test-docs: + runs-on: ubuntu-latest + container: + image: texlive/texlive:latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build documentation + uses: ./.github/actions/build-docs + with: + python-version: "3.10" + treat-warnings-as-errors: "true" + + - name: Upload docs artifact + uses: actions/upload-artifact@v4 + if: always() + with: + name: documentation + path: docs/build/html/ + retention-days: 7 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7de373c..d0b7c87 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,4 +54,3 @@ deploy: stage: deploy script: echo "Define your deployment script!" environment: production - diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index af67085..61316d0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,21 @@ +--- repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 # Use the latest stable version + rev: v6.0.0 hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace - id: check-added-large-files # Prevent giant files from being committed. - args: ["--maxkb=1000"] - - id: check-merge-conflict # Check for files that contain merge conflict strings. - - id: check-toml # Attempts to load all TOML files to verify syntax. - - id: check-yaml # Attempts to load all yaml files to verify syntax. - args: ["--unsafe"] - + args: [--maxkb=500] + - id: check-merge-conflict # Check for files that contain merge conflict strings. - repo: https://github.com/kynan/nbstripout - rev: 0.8.1 + rev: 0.9.0 hooks: - - id: nbstripout # remove jupyter notebook cell output + - id: nbstripout # strip jupyter noteook output + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.9.1 + hooks: + - id: ruff + args: [--fix, --select, I] + - id: ruff-format diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css new file mode 100644 index 0000000..fb0ac08 --- /dev/null +++ b/docs/source/_static/custom.css @@ -0,0 +1,93 @@ +/* Improved toctree styling */ + +/* Main page toctree container */ +.toctree-wrapper { + margin: 2rem 0; +} + +/* Toctree list styling */ +.toctree-wrapper ul { + padding-left: 0; + list-style: none; +} + +/* Toctree list items */ +.toctree-wrapper li { + margin: 0.4rem 0; + padding: 0.6rem 1rem; + border-bottom: 1.5px solid #c5d1e0; + border-left: 3px solid transparent; + transition: all 0.2s ease; +} + +.toctree-wrapper li:hover { + border-left-color: #4f46e5; + background: rgba(79, 70, 229, 0.03); +} + +.toctree-wrapper li:last-child { + border-bottom: 1.5px solid #c5d1e0; +} + +/* Toctree links */ +.toctree-wrapper a.reference { + font-size: 1.1rem; + font-weight: 500; + color: #1a365d; + text-decoration: none; + display: inline-block; + transition: all 0.2s ease; + padding: 0.25rem 0; +} + +.toctree-wrapper a.reference:hover { + color: #2563eb; + transform: translateX(5px); +} + +/* Nested toctree items */ +.toctree-wrapper ul ul { + padding-left: 1.5rem; + margin-top: 0.3rem; +} + +.toctree-wrapper ul ul li { + margin: 0.25rem 0; + padding: 0.4rem 0.8rem; + border-bottom: 1px solid #dce4f0; + border-left: 2px solid transparent; +} + +.toctree-wrapper ul ul li:hover { + border-left-color: #6366f1; + background: rgba(99, 102, 241, 0.03); +} + +.toctree-wrapper ul ul a.reference { + font-size: 1rem; + font-weight: 400; + color: #4a5568; +} + +.toctree-wrapper ul ul a.reference:hover { + color: #4f46e5; +} + +/* Toctree caption */ +.toctree-wrapper p.caption { + font-size: 1.3rem; + font-weight: 600; + color: #1e3a5f; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 3px solid #4f46e5; +} + +/* Improve spacing on main page */ +div.section > .toctree-wrapper { + background: linear-gradient(135deg, #f0f4f8 0%, #e8eef5 100%); + padding: 1.5rem; + border-radius: 12px; + box-shadow: 0 4px 6px rgba(79, 70, 229, 0.08); + border: 1px solid #dce4f0; +} diff --git a/docs/source/api.md b/docs/source/api.md deleted file mode 100644 index a1c08b6..0000000 --- a/docs/source/api.md +++ /dev/null @@ -1,6 +0,0 @@ -# API - -Description of the API - -```{toctree} -:maxdepth: 1 \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 5f47608..c8db3c9 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -2,136 +2,100 @@ # # For the full list of built-in configuration values, see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html - import os import shutil +import sys +from pathlib import Path -# At the top. -import sphinx_bootstrap_theme - - -def copy_tutorials(app): - src = os.path.abspath("../tutorials") - dst = os.path.abspath("source/tutorials") - - # Remove existing target directory if it exists - if os.path.exists(dst): - shutil.rmtree(dst) +sys.path.insert(0, os.path.abspath("../src")) - shutil.copytree(src, dst) +# Copy tutorial notebooks from project root to docs/source/tutorials/ +tutorials_source = Path(__file__).parent.parent.parent / "tutorials" +tutorials_dest = Path(__file__).parent / "tutorials" - -def setup(app): - app.connect("builder-inited", copy_tutorials) - # app.add_stylesheet("my-styles.css") - app.add_css_file("custom.css") +if tutorials_source.exists(): + tutorials_dest.mkdir(exist_ok=True) + for notebook in tutorials_source.glob("*.ipynb"): + shutil.copy2(notebook, tutorials_dest / notebook.name) # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information project = "tikzpics" -copyright = "2025, Max Lindqvist" -author = "Max Lindqvist" +copyright = "2025, Max" +author = "Max" # -- General configuration --------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. extensions = [ - "nbsphinx", - "sphinx.ext.mathjax", "sphinx.ext.autodoc", - "myst_parser", # enable Markdown support + "sphinx.ext.autosummary", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinxcontrib.mermaid", + "sphinx_design", + "nbsphinx", ] -exclude_patterns = [] +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] -# Recognize both .rst and .md -source_suffix = { - ".rst": "restructuredtext", - ".md": "markdown", -} +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**/.ipynb_checkpoints"] -# -- Options for HTML output ------------------------------------------------- -# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output +# nbsphinx configuration +nbsphinx_execute = "always" # Execute notebooks during build -# html_theme = "furo" -# conf.py -# ... -# Activate the theme. -html_theme = "bootstrap" -html_theme_path = sphinx_bootstrap_theme.get_html_theme_path() +# -- Options for HTML output ------------------------------------------------- -# (Optional) Logo. Should be small enough to fit the navbar (ideally 24x24). -# Path should be relative to the ``_static`` files directory. -# html_logo = "my_logo.png" +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_book_theme" -# Theme options are theme-specific and customize the look and feel of a -# theme further. html_theme_options = { - # Navigation bar title. (Default: ``project`` value) - "navbar_title": "python-template", - # Tab name for entire site. (Default: "Site") - "navbar_site_name": "Site", - # A list of tuples containing pages or urls to link to. - # Valid tuples should be in the following forms: - # (name, page) # a link to a page - # (name, "/aa/bb", 1) # a link to an arbitrary relative url - # (name, "http://example.com", True) # arbitrary absolute url - # Note the "1" or "True" value above as the third argument to indicate - # an arbitrary url. - "navbar_links": [ - ("Quickstart", "quickstart"), - ("Tutorials", "tutorials"), - ("API", "api"), - # ("Link", "http://example.com", True), + "repository_branch": "devel", + "show_toc_level": 2, + "secondary_sidebar_items": ["page-toc"], + "use_sidenotes": True, + "home_page_in_toc": True, + "collapse_navigation": False, + "navigation_depth": 2, + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/max-models/tikzpics/", + "icon": "fab fa-github", + "type": "fontawesome", + }, + { + "name": "⭐ Star us!", + "url": "https://github.com/max-models/tikzpics/", + "icon": "fas fa-star", + "type": "fontawesome", + }, ], - # Render the next and previous page links in navbar. (Default: true) - "navbar_sidebarrel": False, - # Render the current pages TOC in the navbar. (Default: true) - "navbar_pagenav": False, - # Tab name for the current pages TOC. (Default: "Page") - "navbar_pagenav_name": "Page", - # Global TOC depth for "site" navbar tab. (Default: 1) - # Switching to -1 shows all levels. - "globaltoc_depth": 2, - # Include hidden TOCs in Site navbar? - # - # Note: If this is "false", you cannot have mixed ``:hidden:`` and - # non-hidden ``toctree`` directives in the same page, or else the build - # will break. - # - # Values: "true" (default) or "false" - "globaltoc_includehidden": "true", - # HTML navbar class (Default: "navbar") to attach to
element. - # For black navbar, do "navbar navbar-inverse" - "navbar_class": "navbar inverse", - # Fix navigation bar to top of page? - # Values: "true" (default) or "false" - "navbar_fixed_top": "true", - # Location of link to source. - # Options are "nav" (default), "footer" or anything else to exclude. - "source_link_position": "footer", - # Bootswatch (http://bootswatch.com/) theme. - # - # Options are nothing (default) or the name of a valid theme - # such as "cosmo" or "sandstone". - # - # The set of valid themes depend on the version of Bootstrap - # that's used (the next config option). - # - # Currently, the supported themes are: - # - Bootstrap 2: https://bootswatch.com/2 - # - Bootstrap 3: https://bootswatch.com/3 - "bootswatch_theme": "flatly", - # Choose Bootstrap version. - # Values: "3" (default) or "2" (in quotes) - "bootstrap_version": "3", } +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +breathe_separate_member_pages = False +html_theme = "sphinx_book_theme" html_static_path = ["_static"] -templates_path = ["_templates"] +html_css_files = ["custom.css"] +autosummary_generate = True -html_sidebars = {"**": []} +autodoc_default_options = { + "members": True, + "undoc-members": False, + "show-inheritance": True, +} diff --git a/docs/source/guides/install.rst b/docs/source/guides/install.rst new file mode 100644 index 0000000..7f7df5e --- /dev/null +++ b/docs/source/guides/install.rst @@ -0,0 +1,12 @@ +Installation +============ + +Create and activate python environment:: + + python -m venv env + source env/bin/activate + pip install --upgrade pip + +Install the code and requirements with pip:: + + pip install tikzpics diff --git a/docs/source/index.md b/docs/source/index.md deleted file mode 100644 index d082474..0000000 --- a/docs/source/index.md +++ /dev/null @@ -1,11 +0,0 @@ -# Welcome - -Write the documentation of your python package here - -```{toctree} -:maxdepth: 2 -:caption: Contents - -quickstart.md -tutorials.md -api.md diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100644 index 0000000..65ebca6 --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,11 @@ +tikzpics Documentation +============================= + +Write the documentation of your python package here. + +.. toctree:: + :maxdepth: 2 + :caption: Documentation + + guides/install + tutorials diff --git a/docs/source/quickstart.md b/docs/source/quickstart.md deleted file mode 100644 index d8e6e56..0000000 --- a/docs/source/quickstart.md +++ /dev/null @@ -1,32 +0,0 @@ -# Quickstart - -Clone the repo - -``` -git clone ... -``` - -Create and activate python environment - -``` -python -m venv env -source env/bin/activate -pip install --upgrade pip -``` - -Install the code and requirements with pip - -``` -pip install -e . -``` - -# Build docs - -``` -make html -cd ../ -open docs/_build/html/index.html -``` - -```{toctree} -:maxdepth: 1 \ No newline at end of file diff --git a/docs/source/tutorials.md b/docs/source/tutorials.md deleted file mode 100644 index 84e10de..0000000 --- a/docs/source/tutorials.md +++ /dev/null @@ -1,9 +0,0 @@ -# Tutorials - -Here are a few tutorials to help you get started. - -```{toctree} -:maxdepth: 1 -:glob: - -tutorials/* \ No newline at end of file diff --git a/docs/source/tutorials.rst b/docs/source/tutorials.rst new file mode 100644 index 0000000..fed41de --- /dev/null +++ b/docs/source/tutorials.rst @@ -0,0 +1,10 @@ +Tutorials +========= + +Here are a few tutorials to help you get started. + +.. toctree:: + :maxdepth: 1 + :glob: + + tutorials/* diff --git a/docs/source/tutorials/tutorial_01.ipynb b/docs/source/tutorials/tutorial_01.ipynb deleted file mode 100644 index 6ea9072..0000000 --- a/docs/source/tutorials/tutorial_01.ipynb +++ /dev/null @@ -1,282 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "0", - "metadata": {}, - "source": [ - "# Tutorial 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1", - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2", - "metadata": {}, - "outputs": [], - "source": [ - "from maxtikzlib.figure import TikzFigure\n", - "import numpy as np" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "3", - "metadata": {}, - "outputs": [], - "source": [ - "fig = TikzFigure()\n", - "\n", - "n1 = fig.add_node(0, 0, shape=\"circle\", color=\"white\", fill=\"blue\", content=\"Hello!\")\n", - "n2 = fig.add_node(5, 0, shape=\"circle\", color=\"white\", fill=\"red\", content=\"Hi!\")\n", - "\n", - "fig.draw([n1, n2], options=[\"->\", \"line width=2\"], color=\"gray\")\n", - "\n", - "fig.show()\n", - "print(fig.generate_tikz())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4", - "metadata": {}, - "outputs": [], - "source": [ - "from maxtikzlib.node import Node\n", - "\n", - "fig = TikzFigure()\n", - "\n", - "n1 = Node(0, 0, shape=\"circle\", color=\"white\", fill=\"blue\", content=\"Hello!\")\n", - "n2 = Node(5, 0, shape=\"circle\", color=\"white\", fill=\"red\", content=\"Hi!\")\n", - "\n", - "fig.add([n1, n2])\n", - "\n", - "fig.draw([n1, n2], options=[\"<-\", \"line width=2\"], color=\"gray\")\n", - "\n", - "fig.show()\n", - "print(fig.generate_tikz())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5", - "metadata": {}, - "outputs": [], - "source": [ - "fig = TikzFigure()\n", - "\n", - "nodes_heart = [\n", - " [2, 0],\n", - " [0, 3.5],\n", - " [1, 4.5],\n", - " [2, 3.0],\n", - " [3, 4.5],\n", - " [4, 3.5],\n", - " [3, 2],\n", - "]\n", - "\n", - "fig.colorlet(\"lightred\", \"red!40!white\")\n", - "\n", - "# TODO: Not every corner should be rounded!\n", - "fig.draw(\n", - " nodes_heart,\n", - " options=[\"draw\", \"rounded corners=20pt\", \"line width=4\"],\n", - " layer=0,\n", - " cycle=True,\n", - " color=\"red\",\n", - " fill=\"lightred\",\n", - " comment=\"Heart shape\",\n", - ")\n", - "\n", - "fig.show()\n", - "print(fig.generate_tikz())" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6", - "metadata": {}, - "outputs": [], - "source": [ - "fig = TikzFigure()\n", - "nodes_star = [\n", - " [0, 0],\n", - " [1, 2.5],\n", - " [2, 0],\n", - " [-0.5, 1.5],\n", - " [2.5, 1.5],\n", - " [0, 0],\n", - "]\n", - "\n", - "fig.draw(\n", - " nodes_star,\n", - " options=[\"draw\", \"line width=3\"],\n", - " layer=0,\n", - " cycle=True,\n", - " color=\"gold\",\n", - " fill=\"yellow!50!white\",\n", - " comment=\"Star shape\",\n", - ")\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7", - "metadata": {}, - "outputs": [], - "source": [ - "fig = TikzFigure()\n", - "\n", - "N = 200\n", - "theta = np.linspace(0, 8 * np.pi, N)\n", - "r = np.linspace(0.1, 2, N)\n", - "x_spiral = r * np.cos(theta) + 5\n", - "y_spiral = r * np.sin(theta) + 2\n", - "nodes_spiral = list(zip(x_spiral, y_spiral))\n", - "\n", - "fig.draw(\n", - " nodes_spiral,\n", - " options=[\"draw\", \"line width=2\"],\n", - " layer=1,\n", - " color=\"teal\",\n", - " comment=\"Spiral curve\",\n", - ")\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8", - "metadata": {}, - "outputs": [], - "source": [ - "fig = TikzFigure()\n", - "\n", - "\n", - "# Circle (sun core)\n", - "theta = np.linspace(0, 2 * np.pi, 50)\n", - "x_circle = np.cos(theta) * 1 + 6\n", - "y_circle = np.sin(theta) * 1 + 6\n", - "nodes_circle = list(zip(x_circle, y_circle))\n", - "\n", - "fig.filldraw(\n", - " nodes_circle,\n", - " options=[\"line width=3\"],\n", - " layer=0,\n", - " cycle=True,\n", - " color=\"orange\",\n", - " fill=\"yellow!50!white\",\n", - " comment=\"Sun core\",\n", - ")\n", - "\n", - "# Rays\n", - "for angle in np.linspace(0, 2 * np.pi, 12, endpoint=False):\n", - " x0, y0 = 6 + np.cos(angle), 6 + np.sin(angle)\n", - " x1, y1 = 6 + 1.5 * np.cos(angle), 6 + 1.5 * np.sin(angle)\n", - " fig.draw(\n", - " [(x0, y0), (x1, y1)],\n", - " options=[\"draw\", \"line width=2\"],\n", - " layer=0,\n", - " color=\"orange\",\n", - " comment=\"Sun ray\",\n", - " )\n", - "\n", - "# TODO: Draw the rays with a tikz loop!\n", - "# num_rays = 12\n", - "# with fig.loop(\"k\", range(num_rays), comment=\"Sun rays\") as loop_k:\n", - "# angle = 2*np.pi/num_rays * loop_k.index # angle step\n", - "# x0, y0 = 6 + np.cos(angle), 6 + np.sin(angle)\n", - "# x1, y1 = 6 + 1.5*np.cos(angle), 6 + 1.5*np.sin(angle)\n", - "# loop_k.draw(\n", - "# [(x0, y0), (x1, y1)],\n", - "# options=[\"draw\", \"line width=2\"],\n", - "# layer=0,\n", - "# color=\"orange\",\n", - "# )\n", - "\n", - "fig.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9", - "metadata": {}, - "outputs": [], - "source": [ - "fig = TikzFigure()\n", - "\n", - "theta = np.linspace(0, 2 * np.pi, 50)\n", - "x_circle = np.cos(theta) * 1 + 6\n", - "y_circle = np.sin(theta) * 1 + 6\n", - "\n", - "# Coordinates for the Olympic rings\n", - "positions = [\n", - " (0, 0), # Blue\n", - " (2.5, 0), # Black\n", - " (5, 0), # Red\n", - " (1.25, -1.25), # Yellow\n", - " (3.75, -1.25), # Green\n", - "]\n", - "\n", - "colors = [\"blue\", \"black\", \"red\", \"yellow\", \"green\"]\n", - "\n", - "# TODO: How to make the rings overlap correctly?\n", - "# Add the rings\n", - "for pos, color in zip(positions, colors):\n", - " nodes_circle = list(zip(x_circle + pos[0], y_circle + pos[1]))\n", - " fig.draw(\n", - " nodes_circle,\n", - " options=[\"draw\", \"line width=5\"],\n", - " layer=0,\n", - " cycle=True,\n", - " color=color,\n", - " fill=\"none\",\n", - " comment=f\"{color} ring\",\n", - " )\n", - "\n", - "fig.show()\n", - "print(fig.generate_tikz())" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "env_maxtikzlib", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.3" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/pyproject.toml b/pyproject.toml index 3064a40..a09748d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,54 +1,58 @@ [build-system] -requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" +requires = [ "setuptools", "wheel" ] + [project] name = "tikzpics" -version = "0.1.1" +version = "0.1.2" description = "Python interface to generate (readable) Tikz figures." readme = "README.md" +keywords = [ "python" ] +license = { file = "LICENSE.txt" } +authors = [ { name = "Max" } ] requires-python = ">=3.8" -license = {file = "LICENSE.txt"} -keywords = ["python"] -authors = [{name = "Max"}] classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 3 - Alpha", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] dependencies = [ - "pymupdf", -] - -[project.optional-dependencies] - -test = [ - "pytest", - "coverage", + "pymupdf", ] -dev = [ - "tikzpics[test]", - "black[jupyter]", - "isort", - "ruff", - "pre-commit", - "pyproject-fmt", - "jupyterlab", - "ipykernel", - "numpy", - "scipy", +optional-dependencies.dev = [ + "black[jupyter]", + "isort", + "pre-commit", + "pyproject-fmt", + "ruff", + "tikzpics[docs]", + "tikzpics[test]", ] - -# https://medium.com/@pratikdomadiya123/build-project-documentation-quickly-with-the-sphinx-python-2a9732b66594 -docs = [ +optional-dependencies.docs = [ + "ipykernel", + "jupyterlab", "myst-parser", "nbconvert", "nbsphinx", + "numpy", + "scipy", "sphinx", - "sphinx_bootstrap_theme", + "sphinx-book-theme", + "sphinx-design", + "sphinxcontrib-mermaid", ] - -[project.urls] -"Source" = "https://github.com/max-models/tikzpics" +optional-dependencies.test = [ + "coverage", + "pytest", +] +urls."Source" = "https://github.com/max-models/tikzpics" [tool.setuptools.packages.find] -where = ["src"] +where = [ "src" ] diff --git a/pytest.ini b/pytest.ini index fa4a21d..fcccae1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,2 +1,2 @@ -[pytest] +[pytest] pythonpath = src diff --git a/src/tikzpics/base.py b/src/tikzpics/base.py index d019c10..4ec4aab 100644 --- a/src/tikzpics/base.py +++ b/src/tikzpics/base.py @@ -7,7 +7,6 @@ def __init__( options: list | None = None, **kwargs, ) -> None: - if options is None: options = [] diff --git a/src/tikzpics/figure.py b/src/tikzpics/figure.py index 9e49cfa..5fa1271 100644 --- a/src/tikzpics/figure.py +++ b/src/tikzpics/figure.py @@ -404,9 +404,9 @@ def generate_tikz(self, verbose: bool = False) -> str: # print("Move layer from buffer") ordered_layers.append(key) buffered_layers.remove(key) - assert ( - len(buffered_layers) == 0 - ), f"Layer order is impossible for layer {[layer.label for layer in buffered_layers]}" + assert len(buffered_layers) == 0, ( + f"Layer order is impossible for layer {[layer.label for layer in buffered_layers]}" + ) for layer in ordered_layers: tikz_script += layer.generate_tikz(verbose=verbose) @@ -595,7 +595,7 @@ def _add_tabs(self, tikz_script): for line in tikz_script.split("\n"): if "\\end" in line or "end \\foreach" in line: num_tabs = max(num_tabs - 1, 0) - tikz_script_new += f"{tab_str*num_tabs}{line}\n" + tikz_script_new += f"{tab_str * num_tabs}{line}\n" if "\\begin" in line or "start \\foreach" in line: num_tabs += 1 return tikz_script_new diff --git a/src/tikzpics/loop.py b/src/tikzpics/loop.py index 56b1a07..e2c95b4 100644 --- a/src/tikzpics/loop.py +++ b/src/tikzpics/loop.py @@ -5,7 +5,6 @@ class Loop(TikzObject): def __init__(self, variable, values, layer=0, comment: str | None = None): - self._variable = variable self._values = list(values) self._items = [] @@ -37,7 +36,6 @@ def add_node(self, *args, **kwargs): return node def add_path(self, nodes, comment: str | None = None, **kwargs): - path = Path(nodes, comment=comment, layer=self.layer, **kwargs) self._items.append(path) return path diff --git a/src/tikzpics/path.py b/src/tikzpics/path.py index 01f58af..9506bd9 100644 --- a/src/tikzpics/path.py +++ b/src/tikzpics/path.py @@ -68,9 +68,9 @@ def label_list(self) -> list: label_list = [] for node in self.nodes: if isinstance(node, Node): - assert ( - node.label != "" - ), "Trying to draw a path using a node without a label!" + assert node.label != "", ( + "Trying to draw a path using a node without a label!" + ) if self.center: label_list.append(f"({node.label}.center)") else: diff --git a/src/tikzpics/plot.py b/src/tikzpics/plot.py index 31e41dd..da5c82e 100644 --- a/src/tikzpics/plot.py +++ b/src/tikzpics/plot.py @@ -16,7 +16,6 @@ def __init__( options: list | None = None, **kwargs, ): - if options is None: options = [] diff --git a/src/tikzpics/tests/unit/test_tikz_generation.py b/src/tikzpics/tests/unit/test_tikz_generation.py index 34bb74a..45a3d43 100644 --- a/src/tikzpics/tests/unit/test_tikz_generation.py +++ b/src/tikzpics/tests/unit/test_tikz_generation.py @@ -2,7 +2,6 @@ def test_tikz_equivalence(): - tikz = TikzFigure() options = ["draw", "rounded corners", "line width=3"] @@ -35,7 +34,6 @@ def test_tikz_equivalence(): def test_logo_equivalence(): - tikz = TikzFigure() options = ["draw", "rounded corners", "line width=3"] diff --git a/src/tikzpics/variable.py b/src/tikzpics/variable.py index d8499c0..f7da5f3 100644 --- a/src/tikzpics/variable.py +++ b/src/tikzpics/variable.py @@ -9,7 +9,6 @@ def __init__( layer: int | None = 0, comment: str | None = None, ) -> None: - super().__init__(label=label, layer=layer, comment=comment) self._value = value diff --git a/tutorials/tutorial_01.ipynb b/tutorials/tutorial_01.ipynb index 6927414..48c8e33 100644 --- a/tutorials/tutorial_01.ipynb +++ b/tutorials/tutorial_01.ipynb @@ -26,9 +26,10 @@ "metadata": {}, "outputs": [], "source": [ + "import numpy as np\n", + "\n", "from tikzpics import TikzFigure\n", - "from tikzpics.node import Node\n", - "import numpy as np" + "from tikzpics.node import Node" ] }, { diff --git a/tutorials/tutorial_04.ipynb b/tutorials/tutorial_04.ipynb index 4cd738e..35fb56d 100644 --- a/tutorials/tutorial_04.ipynb +++ b/tutorials/tutorial_04.ipynb @@ -43,7 +43,6 @@ " node_positions = []\n", "\n", " for i in range(N + 1):\n", - "\n", " # Horizontal line\n", " y = y0 + i * delta\n", " node_positions = [[x0, y], [x1, y]]\n", @@ -67,7 +66,6 @@ "\n", "\n", "def draw_grid(tikz):\n", - "\n", " # Add backgrounds\n", " add_square(\n", " tikz=tikz,\n",